@@ -14,6 +14,12 @@ use winapi::um::winreg::*;
1414
1515use crate :: { Error , ErrorKind , Result , SerialPortInfo , SerialPortType , UsbPortInfo } ;
1616
17+ #[ cfg( feature = "iocontrol" ) ]
18+ mod iocontrol;
19+
20+ #[ cfg( feature = "iocontrol" ) ]
21+ use iocontrol:: { IoControl , IoDescriptor } ;
22+
1723// According to the MSDN docs, we should use SetupDiGetClassDevs, SetupDiEnumDeviceInfo
1824// and SetupDiGetDeviceInstanceId in order to enumerate devices.
1925// https://msdn.microsoft.com/en-us/windows/hardware/drivers/install/enumerating-installed-devices
@@ -94,7 +100,11 @@ fn get_ports_guids() -> Result<Vec<GUID>> {
94100/// - BlackMagic GDB Server: USB\VID_1D50&PID_6018&MI_00\6&A694CA9&0&0000
95101/// - BlackMagic UART port: USB\VID_1D50&PID_6018&MI_02\6&A694CA9&0&0002
96102/// - 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 > {
98108 let re = Regex :: new ( concat ! (
99109 r"VID_(?P<vid>[[:xdigit:]]{4})" ,
100110 r"[&+]PID_(?P<pid>[[:xdigit:]]{4})" ,
@@ -109,12 +119,31 @@ fn parse_usb_port_info(hardware_id: &str, parent_hardware_id: Option<&str>) -> O
109119 . name ( "iid" )
110120 . and_then ( |m| u8:: from_str_radix ( m. as_str ( ) , 16 ) . ok ( ) ) ;
111121
112- if let Some ( _ ) = interface {
122+ if interface . is_some ( ) {
113123 // If this is a composite device, we need to parse the parent's HWID to get the correct information.
114124 caps = re. captures ( parent_hardware_id?) ?;
115125 }
116126
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 {
118147 vid : u16:: from_str_radix ( & caps[ 1 ] , 16 ) . ok ( ) ?,
119148 pid : u16:: from_str_radix ( & caps[ 2 ] , 16 ) . ok ( ) ?,
120149 serial_number : caps. name ( "serial" ) . map ( |m| {
@@ -128,8 +157,39 @@ fn parse_usb_port_info(hardware_id: &str, parent_hardware_id: Option<&str>) -> O
128157 manufacturer : None ,
129158 product : None ,
130159 #[ cfg( feature = "usbportinfo-interface" ) ]
131- interface : interface,
132- } )
160+ interface,
161+ } ;
162+
163+ #[ cfg( feature = "iocontrol" ) ]
164+ if parent_hardware_id. is_some ( ) && device_location_info. is_some ( ) {
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+ . ok ( ) ?;
183+
184+ let iproduct =
185+ IoControl :: get_usb_string_descriptor ( & hdevice, port_number, & IoDescriptor :: Product )
186+ . ok ( ) ?;
187+
188+ usb_port_info. manufacturer = Some ( imanufacturer) ;
189+ usb_port_info. product = Some ( iproduct) ;
190+ }
191+
192+ Some ( usb_port_info)
133193}
134194
135195struct PortDevices {
@@ -320,11 +380,32 @@ impl PortDevice {
320380 // Determines the port_type for this device, and if it's a USB port populate the various fields.
321381 pub fn port_type ( & mut self ) -> SerialPortType {
322382 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 ( ) ) )
383+ . map ( |s| {
384+ (
385+ s,
386+ self . parent_instance_id ( ) ,
387+ #[ cfg ( feature = "iocontrol" ) ]
388+ self. property ( SPDRP_LOCATION_INFORMATION ) ,
389+ )
390+ } ) // Get parent instance id if it exists.
391+ . and_then (
392+ |#[ cfg( not( feature = "iocontrol" ) ) ] ( d, p) ,
393+ #[ cfg( feature = "iocontrol" ) ] ( d, p, l) | {
394+ parse_usb_port_info (
395+ & d,
396+ p. as_deref ( ) ,
397+ #[ cfg( feature = "iocontrol" ) ]
398+ l. as_deref ( ) ,
399+ )
400+ } ,
401+ )
325402 . map ( |mut info : UsbPortInfo | {
326- info. manufacturer = self . property ( SPDRP_MFG ) ;
327- info. product = self . property ( SPDRP_FRIENDLYNAME ) ;
403+ if info. manufacturer . is_none ( ) {
404+ info. manufacturer = self . property ( SPDRP_MFG )
405+ } ;
406+ if info. product . is_none ( ) {
407+ info. product = self . property ( SPDRP_FRIENDLYNAME )
408+ } ;
328409 SerialPortType :: UsbPort ( info)
329410 } )
330411 . unwrap_or ( SerialPortType :: Unknown )
@@ -399,7 +480,13 @@ pub fn available_ports() -> Result<Vec<SerialPortInfo>> {
399480fn test_parsing_usb_port_information ( ) {
400481 let bm_uart_hwid = r"USB\VID_1D50&PID_6018&MI_02\6&A694CA9&0&0000" ;
401482 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 ( ) ;
483+ let info = parse_usb_port_info (
484+ bm_uart_hwid,
485+ Some ( bm_parent_hwid) ,
486+ #[ cfg( feature = "iocontrol" ) ]
487+ None ,
488+ )
489+ . unwrap ( ) ;
403490
404491 assert_eq ! ( info. vid, 0x1D50 ) ;
405492 assert_eq ! ( info. pid, 0x6018 ) ;
@@ -408,7 +495,13 @@ fn test_parsing_usb_port_information() {
408495 assert_eq ! ( info. interface, Some ( 2 ) ) ;
409496
410497 let ftdi_serial_hwid = r"FTDIBUS\VID_0403+PID_6001+A702TB52A\0000" ;
411- let info = parse_usb_port_info ( ftdi_serial_hwid, None ) . unwrap ( ) ;
498+ let info = parse_usb_port_info (
499+ ftdi_serial_hwid,
500+ None ,
501+ #[ cfg( feature = "iocontrol" ) ]
502+ None ,
503+ )
504+ . unwrap ( ) ;
412505
413506 assert_eq ! ( info. vid, 0x0403 ) ;
414507 assert_eq ! ( info. pid, 0x6001 ) ;
@@ -417,7 +510,13 @@ fn test_parsing_usb_port_information() {
417510 assert_eq ! ( info. interface, None ) ;
418511
419512 let pyboard_hwid = r"USB\VID_F055&PID_9802\385435603432" ;
420- let info = parse_usb_port_info ( pyboard_hwid, None ) . unwrap ( ) ;
513+ let info = parse_usb_port_info (
514+ pyboard_hwid,
515+ None ,
516+ #[ cfg( feature = "iocontrol" ) ]
517+ None ,
518+ )
519+ . unwrap ( ) ;
421520
422521 assert_eq ! ( info. vid, 0xF055 ) ;
423522 assert_eq ! ( info. pid, 0x9802 ) ;
0 commit comments