@@ -43,6 +43,21 @@ const TCP_PORT: u16 = 5800;
43
43
const FLUSH_DELAY : Option < Duration > = Some ( Duration :: from_millis ( 10 ) ) ;
44
44
const DUMP_BUFFER : usize = 8192 ;
45
45
46
+ #[ cfg( any( feature = "usb-host" , feature = "usb-device" ) ) ]
47
+ mod usb {
48
+ pub const VID : u16 = u16:: MAX - 2 ;
49
+ pub const PID : u16 = u16:: MAX - 2 ;
50
+ pub const MANUFACTURER : & str = env ! ( "CARGO_PKG_NAME" ) ;
51
+ pub const PRODUCT : & str = env ! ( "CARGO_BIN_NAME" ) ;
52
+ pub const CLASS : u8 = 255 ;
53
+ pub const SUB_CLASS : u8 = 255 ;
54
+ pub const PROTOCOL : u8 = 255 ;
55
+ pub const INTERFACE_CLASS : u8 = 255 ;
56
+ pub const INTERFACE_SUB_CLASS : u8 = 240 ;
57
+ pub const INTERFACE_PROTOCOL : u8 = 1 ;
58
+ pub const DEFAULT_INTERFACE_NAME : & str = "agg-tunnel" ;
59
+ }
60
+
46
61
/// Forward TCP ports through a connection of aggregated links.
47
62
///
48
63
/// This uses Aggligator to combine multiple TCP links into one connection,
@@ -124,6 +139,16 @@ pub struct ClientCli {
124
139
#[ cfg( feature = "rfcomm" ) ]
125
140
#[ arg( long) ]
126
141
rfcomm : Option < bluer:: rfcomm:: SocketAddr > ,
142
+ /// USB device serial number (equals hostname of speed test device).
143
+ ///
144
+ /// Use - to match any device.
145
+ #[ cfg( feature = "usb-host" ) ]
146
+ #[ arg( long) ]
147
+ usb : Option < String > ,
148
+ /// USB interface name.
149
+ #[ cfg( feature = "usb-host" ) ]
150
+ #[ arg( long, default_value=usb:: DEFAULT_INTERFACE_NAME ) ]
151
+ usb_interface_name : String ,
127
152
}
128
153
129
154
impl ClientCli {
@@ -167,6 +192,46 @@ impl ClientCli {
167
192
None => None ,
168
193
} ;
169
194
195
+ #[ cfg( feature = "usb-host" ) ]
196
+ let usb_connector = {
197
+ if let Some ( serial) = & self . usb {
198
+ targets. push ( format ! ( "USB {serial}" ) ) ;
199
+ }
200
+
201
+ let usb = self . usb . clone ( ) ;
202
+ move || match & usb {
203
+ Some ( serial) => {
204
+ let filter_serial = serial. clone ( ) ;
205
+ let filter_interface_name = self . usb_interface_name . clone ( ) ;
206
+ let filter =
207
+ move |dev : & aggligator_util:: transport:: usb:: DeviceInfo ,
208
+ iface : & aggligator_util:: transport:: usb:: InterfaceInfo | {
209
+ dev. vendor_id == usb:: VID
210
+ && dev. product_id == usb:: PID
211
+ && dev. manufacturer == usb:: MANUFACTURER
212
+ && dev. product == usb:: PRODUCT
213
+ && ( dev. serial_number == filter_serial || filter_serial == "-" )
214
+ && dev. class_code == usb:: CLASS
215
+ && dev. sub_class_code == usb:: SUB_CLASS
216
+ && dev. protocol_code == usb:: PROTOCOL
217
+ && iface. class_code == usb:: INTERFACE_CLASS
218
+ && iface. sub_class_code == usb:: INTERFACE_SUB_CLASS
219
+ && iface. protocol_code == usb:: INTERFACE_PROTOCOL
220
+ && iface. description == filter_interface_name
221
+ } ;
222
+
223
+ match aggligator_util:: transport:: usb:: UsbConnector :: new ( filter) {
224
+ Ok ( c) => Some ( c) ,
225
+ Err ( err) => {
226
+ eprintln ! ( "cannot use USB target: {err}" ) ;
227
+ None
228
+ }
229
+ }
230
+ }
231
+ None => None ,
232
+ }
233
+ } ;
234
+
170
235
if targets. is_empty ( ) {
171
236
bail ! ( "No connection transports." ) ;
172
237
}
@@ -195,6 +260,8 @@ impl ClientCli {
195
260
let tcp_connector = tcp_connector. clone ( ) ;
196
261
#[ cfg( feature = "rfcomm" ) ]
197
262
let rfcomm_connector = rfcomm_connector. clone ( ) ;
263
+ #[ cfg( feature = "usb-host" ) ]
264
+ let usb_connector = usb_connector. clone ( ) ;
198
265
let dump = dump. clone ( ) ;
199
266
port_tasks. push ( async move {
200
267
loop {
@@ -215,6 +282,10 @@ impl ClientCli {
215
282
if let Some ( c) = rfcomm_connector. clone ( ) {
216
283
connector. add ( c) ;
217
284
}
285
+ #[ cfg( feature = "usb-host" ) ]
286
+ if let Some ( c) = usb_connector ( ) {
287
+ connector. add ( c) ;
288
+ }
218
289
let control = connector. control ( ) ;
219
290
let outgoing = connector. channel ( ) . unwrap ( ) ;
220
291
@@ -317,6 +388,14 @@ pub struct ServerCli {
317
388
#[ cfg( feature = "rfcomm" ) ]
318
389
#[ arg( long) ]
319
390
rfcomm : Option < u8 > ,
391
+ /// Listen on USB device controller (UDC).
392
+ #[ cfg( feature = "usb-device" ) ]
393
+ #[ arg( long) ]
394
+ usb : bool ,
395
+ /// USB interface name.
396
+ #[ cfg( feature = "usb-host" ) ]
397
+ #[ arg( long, default_value=usb:: DEFAULT_INTERFACE_NAME ) ]
398
+ usb_interface_name : String ,
320
399
}
321
400
322
401
impl ServerCli {
@@ -371,6 +450,51 @@ impl ServerCli {
371
450
}
372
451
}
373
452
453
+ #[ cfg( feature = "usb-device" ) ]
454
+ let _usb_reg = if self . usb {
455
+ fn register_usb (
456
+ serial : & str , interface_name : & str ,
457
+ ) -> Result < ( usb_gadget:: RegGadget , upc:: device:: UpcFunction , std:: ffi:: OsString ) > {
458
+ let udc = usb_gadget:: default_udc ( ) ?;
459
+ let udc_name = udc. name ( ) . to_os_string ( ) ;
460
+
461
+ let ( upc, func_hnd) = upc:: device:: UpcFunction :: new (
462
+ upc:: device:: InterfaceId :: new ( upc:: Class :: new (
463
+ usb:: INTERFACE_CLASS ,
464
+ usb:: INTERFACE_SUB_CLASS ,
465
+ usb:: INTERFACE_PROTOCOL ,
466
+ ) )
467
+ . with_name ( interface_name) ,
468
+ ) ;
469
+
470
+ let reg = usb_gadget:: Gadget :: new (
471
+ usb_gadget:: Class :: new ( usb:: CLASS , usb:: SUB_CLASS , usb:: PROTOCOL ) ,
472
+ usb_gadget:: Id :: new ( usb:: VID , usb:: PID ) ,
473
+ usb_gadget:: Strings :: new ( usb:: MANUFACTURER , usb:: PRODUCT , serial) ,
474
+ )
475
+ . with_os_descriptor ( usb_gadget:: OsDescriptor :: microsoft ( ) )
476
+ . with_config ( usb_gadget:: Config :: new ( "config" ) . with_function ( func_hnd) )
477
+ . bind ( & udc) ?;
478
+
479
+ Ok ( ( reg, upc, udc_name) )
480
+ }
481
+
482
+ let serial = gethostname:: gethostname ( ) . to_string_lossy ( ) . to_string ( ) ;
483
+ match register_usb ( & serial, & self . usb_interface_name ) {
484
+ Ok ( ( usb_reg, upc, udc_name) ) => {
485
+ acceptor. add ( aggligator_util:: transport:: usb:: UsbAcceptor :: new ( upc, & udc_name) ) ;
486
+ server_ports. push ( format ! ( "UDC {} ({serial})" , udc_name. to_string_lossy( ) ) ) ;
487
+ Some ( usb_reg)
488
+ }
489
+ Err ( err) => {
490
+ eprintln ! ( "Cannot listen on USB: {err}" ) ;
491
+ None
492
+ }
493
+ }
494
+ } else {
495
+ None
496
+ } ;
497
+
374
498
if server_ports. is_empty ( ) {
375
499
bail ! ( "No listening transports." ) ;
376
500
}
0 commit comments