1
1
use std:: { io:: Read , result} ;
2
2
3
- use flate2:: read:: ZlibDecoder ;
3
+ use flate2:: { read:: { ZlibDecoder , GzDecoder } , DecompressError } ;
4
4
use hidapi:: { HidApi , HidDevice , HidError } ;
5
5
use once_cell:: sync:: OnceCell ;
6
6
use serde:: Deserialize ;
7
7
use serde_json:: Value ;
8
- use tracing:: error;
8
+ use tracing:: { error, info } ;
9
9
10
10
#[ derive( thiserror:: Error , Debug ) ]
11
11
pub enum Error {
@@ -21,6 +21,10 @@ pub enum Error {
21
21
ConfigReadFailed ,
22
22
#[ error( "protocol error: {0}" ) ]
23
23
ProtocolError ( & ' static str ) ,
24
+ #[ error( "deflate error: {0}" ) ]
25
+ DeflateError ( #[ from] DecompressError ) ,
26
+ #[ error( "IO error: {0}" ) ]
27
+ IOError ( #[ from] std:: io:: Error ) ,
24
28
}
25
29
26
30
type Result < T , E = Error > = result:: Result < T , E > ;
@@ -134,7 +138,8 @@ impl SteamDevice {
134
138
}
135
139
136
140
const VIVE_VID : u16 = 0x0bb4 ;
137
- const VIVE_PID : u16 = 0x0342 ;
141
+ const VIVE_PRO_2_PID : u16 = 0x0342 ;
142
+ const VIVE_COSMOS_PID : u16 = 0x0313 ;
138
143
139
144
#[ derive( Deserialize , Debug ) ]
140
145
pub struct ViveConfig {
@@ -175,11 +180,11 @@ const VIVE_PRO_2_MODES: [Mode; 6] = [
175
180
Mode :: new ( 5 , 2896 , 2448 , 120.0 ) ,
176
181
] ;
177
182
178
- pub struct ViveDevice ( HidDevice ) ;
179
- impl ViveDevice {
183
+ pub struct VivePro2Device ( HidDevice ) ;
184
+ impl VivePro2Device {
180
185
pub fn open_first ( ) -> Result < Self > {
181
186
let api = get_hidapi ( ) ?;
182
- let device = api. open ( VIVE_VID , VIVE_PID ) ?;
187
+ let device = api. open ( VIVE_VID , VIVE_PRO_2_PID ) ?;
183
188
Ok ( Self ( device) )
184
189
}
185
190
pub fn open ( sn : & str ) -> Result < Self > {
@@ -188,7 +193,7 @@ impl ViveDevice {
188
193
. device_list ( )
189
194
. find ( |dev| dev. serial_number ( ) == Some ( sn) )
190
195
. ok_or ( Error :: DeviceNotFound ) ?;
191
- if device. vendor_id ( ) != VIVE_VID || device. product_id ( ) != VIVE_PID {
196
+ if device. vendor_id ( ) != VIVE_VID || device. product_id ( ) != VIVE_PRO_2_PID {
192
197
return Err ( Error :: NotAVive ) ;
193
198
}
194
199
let open = api. open_serial ( device. vendor_id ( ) , device. product_id ( ) , sn) ?;
@@ -285,7 +290,7 @@ impl ViveDevice {
285
290
286
291
serde_json:: from_str ( & string) . map_err ( |_| Error :: ConfigReadFailed )
287
292
}
288
- /// Always returns at least one mode
293
+ // Always returns at least one mode
289
294
pub fn query_modes ( & self ) -> Vec < Mode > {
290
295
VIVE_PRO_2_MODES . into_iter ( ) . collect ( )
291
296
}
@@ -328,3 +333,104 @@ impl ViveDevice {
328
333
Ok ( ( ) )
329
334
}
330
335
}
336
+
337
+ const VIVE_COSMOS_MODES : [ Mode ; 1 ] = [
338
+ Mode :: new ( 0 , 2880 , 1700 , 90.0 ) ,
339
+ ] ;
340
+
341
+ pub struct ViveCosmosDevice ( HidDevice ) ;
342
+ use std:: fs:: File ;
343
+ use std:: io:: prelude:: * ;
344
+ impl ViveCosmosDevice {
345
+
346
+ pub fn open_first ( ) -> Result < Self > {
347
+ let api = get_hidapi ( ) ?;
348
+ let device = api. open ( VIVE_VID , VIVE_COSMOS_PID ) ?;
349
+ Ok ( Self ( device) )
350
+ }
351
+ pub fn open ( sn : & str ) -> Result < Self > {
352
+ let api = get_hidapi ( ) ?;
353
+ let device = api
354
+ . device_list ( )
355
+ . find ( |dev| dev. serial_number ( ) == Some ( sn) )
356
+ . ok_or ( Error :: DeviceNotFound ) ?;
357
+ if device. vendor_id ( ) != VIVE_VID || device. product_id ( ) != VIVE_COSMOS_PID {
358
+ return Err ( Error :: NotAVive ) ;
359
+ }
360
+ let open = api. open_serial ( device. vendor_id ( ) , device. product_id ( ) , sn) ?;
361
+ Ok ( Self ( open) )
362
+ }
363
+ // Always returns at least one mode
364
+ pub fn query_modes ( & self ) -> Vec < Mode > {
365
+ VIVE_COSMOS_MODES . into_iter ( ) . collect ( )
366
+ }
367
+ pub fn read_config ( & self ) -> Result < ViveConfig > {
368
+ let gz_file_buffer = self . read_stream ( b"HMD_JSON.gz" ) ?;
369
+
370
+ info ! ( "received gzipped config file" ) ;
371
+
372
+ let mut dec = GzDecoder :: new ( gz_file_buffer. as_slice ( ) ) ;
373
+ let mut config = String :: new ( ) ;
374
+ dec. read_to_string ( & mut config) ?;
375
+
376
+ info ! ( "config: {config}" ) ;
377
+
378
+ serde_json:: from_str ( & config) . map_err ( |_| Error :: ConfigReadFailed )
379
+ }
380
+ pub fn read_stream ( & self , filename : & [ u8 ] ) -> Result < Vec :: < u8 > > {
381
+ let mut report = [ 0u8 ; 65 ] ;
382
+
383
+ report[ 0 ] = 0x00 ; // id 0 -> root of the Application collection.
384
+ report[ 1 ] = 0x10 ; // the command I presume ?
385
+ report[ 2 ] = 0x00 ; // commant 2nd part ?
386
+ report[ 3 ] = filename. len ( ) as u8 ; // payload size
387
+ report[ 4 ] = 0xff ; // separator ?
388
+ report[ 5 ..] [ ..filename. len ( ) ] . copy_from_slice ( filename) ;
389
+
390
+ let total_len = {
391
+ self . 0 . send_feature_report ( & report) ?;
392
+
393
+ loop {
394
+ self . 0 . get_feature_report ( & mut report) ?;
395
+ if report[ 0 ] != 0x00 { return Err ( Error :: ProtocolError ( "unknown data received." ) ) }
396
+ if report[ 1 ] == 0x10 { break ; }
397
+ }
398
+
399
+ let mut total_len = [ 0u8 ; 4 ] ;
400
+ total_len. copy_from_slice ( & report[ 5 ..9 ] ) ;
401
+ u32:: from_le_bytes ( total_len) as usize
402
+ } ;
403
+ info ! ( "config read length : {total_len}" ) ;
404
+
405
+ let mut position = 0x0 as usize ;
406
+ let mut out = Vec :: < u8 > :: with_capacity ( total_len) ;
407
+
408
+ while position < total_len {
409
+ report = [ 0u8 ; 65 ] ;
410
+ report[ 1 ] = 0x11 ;
411
+ report[ 2 ] = 0x00 ;
412
+ report[ 3 ] = 0x08 ; //payload size;
413
+ report[ 4 ] = 0x80 ; // separator ?
414
+ report[ 5 ..9 ] . copy_from_slice ( & u32:: to_le_bytes ( position as u32 ) ) ; // start position
415
+ report[ 9 ] = 0x38 ;
416
+
417
+ self . 0 . send_feature_report ( & report) ?;
418
+ loop {
419
+ self . 0 . get_feature_report ( & mut report) ?;
420
+ if report[ 0 ] != 0x00 { return Err ( Error :: ProtocolError ( "unknown data received." ) ) }
421
+ if report[ 1 ] == 0x11 { break ; }
422
+ }
423
+
424
+ let size = ( report[ 3 ] - 0x04 ) as usize ;
425
+ out. extend_from_slice ( & report[ 5 ..5 + size] ) ;
426
+
427
+ position = position + size;
428
+ info ! ( "position: {position}, size: {size}" ) ;
429
+ }
430
+ if position != total_len {
431
+ return Err ( Error :: ProtocolError ( "config size mismatch" ) ) ;
432
+ }
433
+
434
+ Ok ( out)
435
+ }
436
+ }
0 commit comments