@@ -14,6 +14,12 @@ use winapi::um::winreg::*;
14
14
15
15
use crate :: { Error , ErrorKind , Result , SerialPortInfo , SerialPortType , UsbPortInfo } ;
16
16
17
+ #[ cfg( feature = "iocontrol" ) ]
18
+ mod iocontrol;
19
+
20
+ #[ cfg( feature = "iocontrol" ) ]
21
+ use iocontrol:: { IoControl , IoDescriptor } ;
22
+
17
23
// According to the MSDN docs, we should use SetupDiGetClassDevs, SetupDiEnumDeviceInfo
18
24
// and SetupDiGetDeviceInstanceId in order to enumerate devices.
19
25
// https://msdn.microsoft.com/en-us/windows/hardware/drivers/install/enumerating-installed-devices
@@ -94,7 +100,11 @@ fn get_ports_guids() -> Result<Vec<GUID>> {
94
100
/// - BlackMagic GDB Server: USB\VID_1D50&PID_6018&MI_00\6&A694CA9&0&0000
95
101
/// - BlackMagic UART port: USB\VID_1D50&PID_6018&MI_02\6&A694CA9&0&0002
96
102
/// - FTDI Serial Adapter: FTDIBUS\VID_0403+PID_6001+A702TB52A\0000
97
- fn parse_usb_port_info ( hardware_id : & str , parent_hardware_id : Option < & str > ) -> Option < UsbPortInfo > {
103
+ fn parse_usb_port_info (
104
+ hardware_id : & str ,
105
+ parent_hardware_id : Option < & str > ,
106
+ #[ cfg( feature = "iocontrol" ) ] device_location_info : Option < & str > ,
107
+ ) -> Option < UsbPortInfo > {
98
108
let re = Regex :: new ( concat ! (
99
109
r"VID_(?P<vid>[[:xdigit:]]{4})" ,
100
110
r"[&+]PID_(?P<pid>[[:xdigit:]]{4})" ,
@@ -109,12 +119,31 @@ fn parse_usb_port_info(hardware_id: &str, parent_hardware_id: Option<&str>) -> O
109
119
. name ( "iid" )
110
120
. and_then ( |m| u8:: from_str_radix ( m. as_str ( ) , 16 ) . ok ( ) ) ;
111
121
112
- if let Some ( _ ) = interface {
122
+ if interface . is_some ( ) {
113
123
// If this is a composite device, we need to parse the parent's HWID to get the correct information.
114
124
caps = re. captures ( parent_hardware_id?) ?;
115
125
}
116
126
117
- Some ( UsbPortInfo {
127
+ #[ cfg( not( feature = "iocontrol" ) ) ]
128
+ let usb_port_info = UsbPortInfo {
129
+ vid : u16:: from_str_radix ( & caps[ 1 ] , 16 ) . ok ( ) ?,
130
+ pid : u16:: from_str_radix ( & caps[ 2 ] , 16 ) . ok ( ) ?,
131
+ serial_number : caps. name ( "serial" ) . map ( |m| {
132
+ let m = m. as_str ( ) ;
133
+ if m. contains ( '&' ) {
134
+ m. split ( '&' ) . nth ( 1 ) . unwrap ( ) . to_string ( )
135
+ } else {
136
+ m. to_string ( )
137
+ }
138
+ } ) ,
139
+ manufacturer : None ,
140
+ product : None ,
141
+ #[ cfg( feature = "usbportinfo-interface" ) ]
142
+ interface,
143
+ } ;
144
+
145
+ #[ cfg( feature = "iocontrol" ) ]
146
+ let mut usb_port_info = UsbPortInfo {
118
147
vid : u16:: from_str_radix ( & caps[ 1 ] , 16 ) . ok ( ) ?,
119
148
pid : u16:: from_str_radix ( & caps[ 2 ] , 16 ) . ok ( ) ?,
120
149
serial_number : caps. name ( "serial" ) . map ( |m| {
@@ -128,8 +157,37 @@ fn parse_usb_port_info(hardware_id: &str, parent_hardware_id: Option<&str>) -> O
128
157
manufacturer : None ,
129
158
product : None ,
130
159
#[ cfg( feature = "usbportinfo-interface" ) ]
131
- interface : interface,
132
- } )
160
+ interface,
161
+ } ;
162
+
163
+ #[ cfg( feature = "iocontrol" ) ]
164
+ {
165
+ let re = Regex :: new ( concat ! ( r"Port_#(?P<hub_device_location>[[:xdigit:]]{4})" , ) ) . unwrap ( ) ;
166
+
167
+ caps = re. captures ( device_location_info?) ?;
168
+ let port_number = u8:: from_str_radix ( & caps[ 1 ] , 8 ) . ok ( ) ?;
169
+
170
+ let hub_name = format ! (
171
+ "{}#{{f18a0e88-c30c-11d0-8815-00a0c906bed8}}" ,
172
+ parent_hardware_id?. replace( '\\' , "#" ) ,
173
+ ) ;
174
+
175
+ let hdevice = IoControl :: get_handle ( & mut hub_name. clone ( ) ) . ok ( ) ?;
176
+
177
+ let imanufacturer = IoControl :: get_usb_string_descriptor (
178
+ & hdevice,
179
+ port_number,
180
+ & IoDescriptor :: Manufacturer ,
181
+ ) ;
182
+
183
+ let iproduct =
184
+ IoControl :: get_usb_string_descriptor ( & hdevice, port_number, & IoDescriptor :: Product ) ;
185
+
186
+ usb_port_info. manufacturer = imanufacturer;
187
+ usb_port_info. product = iproduct;
188
+ }
189
+
190
+ Some ( usb_port_info)
133
191
}
134
192
135
193
struct PortDevices {
@@ -320,11 +378,32 @@ impl PortDevice {
320
378
// Determines the port_type for this device, and if it's a USB port populate the various fields.
321
379
pub fn port_type ( & mut self ) -> SerialPortType {
322
380
self . instance_id ( )
323
- . map ( |s| ( s, self . parent_instance_id ( ) ) ) // Get parent instance id if it exists.
324
- . and_then ( |( d, p) | parse_usb_port_info ( & d, p. as_deref ( ) ) )
381
+ . map ( |s| {
382
+ (
383
+ s,
384
+ self . parent_instance_id ( ) ,
385
+ #[ cfg ( feature = "iocontrol" ) ]
386
+ self. property ( SPDRP_LOCATION_INFORMATION ) ,
387
+ )
388
+ } ) // Get parent instance id if it exists.
389
+ . and_then (
390
+ |#[ cfg( not( feature = "iocontrol" ) ) ] ( d, p) ,
391
+ #[ cfg( feature = "iocontrol" ) ] ( d, p, l) | {
392
+ parse_usb_port_info (
393
+ & d,
394
+ p. as_deref ( ) ,
395
+ #[ cfg( feature = "iocontrol" ) ]
396
+ l. as_deref ( ) ,
397
+ )
398
+ } ,
399
+ )
325
400
. map ( |mut info : UsbPortInfo | {
326
- info. manufacturer = self . property ( SPDRP_MFG ) ;
327
- info. product = self . property ( SPDRP_FRIENDLYNAME ) ;
401
+ if info. manufacturer . is_none ( ) {
402
+ info. manufacturer = self . property ( SPDRP_MFG )
403
+ } ;
404
+ if info. product . is_none ( ) {
405
+ info. product = self . property ( SPDRP_FRIENDLYNAME )
406
+ } ;
328
407
SerialPortType :: UsbPort ( info)
329
408
} )
330
409
. unwrap_or ( SerialPortType :: Unknown )
@@ -399,7 +478,13 @@ pub fn available_ports() -> Result<Vec<SerialPortInfo>> {
399
478
fn test_parsing_usb_port_information ( ) {
400
479
let bm_uart_hwid = r"USB\VID_1D50&PID_6018&MI_02\6&A694CA9&0&0000" ;
401
480
let bm_parent_hwid = r"USB\VID_1D50&PID_6018\85A12F01" ;
402
- let info = parse_usb_port_info ( bm_uart_hwid, Some ( bm_parent_hwid) ) . unwrap ( ) ;
481
+ let info = parse_usb_port_info (
482
+ bm_uart_hwid,
483
+ Some ( bm_parent_hwid) ,
484
+ #[ cfg( feature = "iocontrol" ) ]
485
+ None ,
486
+ )
487
+ . unwrap ( ) ;
403
488
404
489
assert_eq ! ( info. vid, 0x1D50 ) ;
405
490
assert_eq ! ( info. pid, 0x6018 ) ;
@@ -408,7 +493,13 @@ fn test_parsing_usb_port_information() {
408
493
assert_eq ! ( info. interface, Some ( 2 ) ) ;
409
494
410
495
let ftdi_serial_hwid = r"FTDIBUS\VID_0403+PID_6001+A702TB52A\0000" ;
411
- let info = parse_usb_port_info ( ftdi_serial_hwid, None ) . unwrap ( ) ;
496
+ let info = parse_usb_port_info (
497
+ ftdi_serial_hwid,
498
+ None ,
499
+ #[ cfg( feature = "iocontrol" ) ]
500
+ None ,
501
+ )
502
+ . unwrap ( ) ;
412
503
413
504
assert_eq ! ( info. vid, 0x0403 ) ;
414
505
assert_eq ! ( info. pid, 0x6001 ) ;
@@ -417,7 +508,13 @@ fn test_parsing_usb_port_information() {
417
508
assert_eq ! ( info. interface, None ) ;
418
509
419
510
let pyboard_hwid = r"USB\VID_F055&PID_9802\385435603432" ;
420
- let info = parse_usb_port_info ( pyboard_hwid, None ) . unwrap ( ) ;
511
+ let info = parse_usb_port_info (
512
+ pyboard_hwid,
513
+ None ,
514
+ #[ cfg( feature = "iocontrol" ) ]
515
+ None ,
516
+ )
517
+ . unwrap ( ) ;
421
518
422
519
assert_eq ! ( info. vid, 0xF055 ) ;
423
520
assert_eq ! ( info. pid, 0x9802 ) ;
0 commit comments