@@ -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,35 @@ 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 =
178+ IoControl :: get_string_descriptor ( & hdevice, port_number, & IoDescriptor :: Manufacturer )
179+ . ok ( ) ?;
180+
181+ let iproduct =
182+ IoControl :: get_string_descriptor ( & hdevice, port_number, & IoDescriptor :: Product ) . ok ( ) ?;
183+
184+ usb_port_info. manufacturer = Some ( imanufacturer) ;
185+ usb_port_info. product = Some ( iproduct) ;
186+ }
187+
188+ Some ( usb_port_info)
133189}
134190
135191struct PortDevices {
@@ -320,11 +376,32 @@ impl PortDevice {
320376 // Determines the port_type for this device, and if it's a USB port populate the various fields.
321377 pub fn port_type ( & mut self ) -> SerialPortType {
322378 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 ( ) ) )
379+ . map ( |s| {
380+ (
381+ s,
382+ self . parent_instance_id ( ) ,
383+ #[ cfg ( feature = "iocontrol" ) ]
384+ self. property ( SPDRP_LOCATION_INFORMATION ) ,
385+ )
386+ } ) // Get parent instance id if it exists.
387+ . and_then (
388+ |#[ cfg( not( feature = "iocontrol" ) ) ] ( d, p) ,
389+ #[ cfg( feature = "iocontrol" ) ] ( d, p, l) | {
390+ parse_usb_port_info (
391+ & d,
392+ p. as_deref ( ) ,
393+ #[ cfg( feature = "iocontrol" ) ]
394+ l. as_deref ( ) ,
395+ )
396+ } ,
397+ )
325398 . map ( |mut info : UsbPortInfo | {
326- info. manufacturer = self . property ( SPDRP_MFG ) ;
327- info. product = self . property ( SPDRP_FRIENDLYNAME ) ;
399+ if info. manufacturer . is_none ( ) {
400+ info. manufacturer = self . property ( SPDRP_MFG )
401+ } ;
402+ if info. product . is_none ( ) {
403+ info. product = self . property ( SPDRP_FRIENDLYNAME )
404+ } ;
328405 SerialPortType :: UsbPort ( info)
329406 } )
330407 . unwrap_or ( SerialPortType :: Unknown )
@@ -399,7 +476,13 @@ pub fn available_ports() -> Result<Vec<SerialPortInfo>> {
399476fn test_parsing_usb_port_information ( ) {
400477 let bm_uart_hwid = r"USB\VID_1D50&PID_6018&MI_02\6&A694CA9&0&0000" ;
401478 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 ( ) ;
479+ let info = parse_usb_port_info (
480+ bm_uart_hwid,
481+ Some ( bm_parent_hwid) ,
482+ #[ cfg( feature = "iocontrol" ) ]
483+ None ,
484+ )
485+ . unwrap ( ) ;
403486
404487 assert_eq ! ( info. vid, 0x1D50 ) ;
405488 assert_eq ! ( info. pid, 0x6018 ) ;
@@ -408,7 +491,13 @@ fn test_parsing_usb_port_information() {
408491 assert_eq ! ( info. interface, Some ( 2 ) ) ;
409492
410493 let ftdi_serial_hwid = r"FTDIBUS\VID_0403+PID_6001+A702TB52A\0000" ;
411- let info = parse_usb_port_info ( ftdi_serial_hwid, None ) . unwrap ( ) ;
494+ let info = parse_usb_port_info (
495+ ftdi_serial_hwid,
496+ None ,
497+ #[ cfg( feature = "iocontrol" ) ]
498+ None ,
499+ )
500+ . unwrap ( ) ;
412501
413502 assert_eq ! ( info. vid, 0x0403 ) ;
414503 assert_eq ! ( info. pid, 0x6001 ) ;
@@ -417,7 +506,13 @@ fn test_parsing_usb_port_information() {
417506 assert_eq ! ( info. interface, None ) ;
418507
419508 let pyboard_hwid = r"USB\VID_F055&PID_9802\385435603432" ;
420- let info = parse_usb_port_info ( pyboard_hwid, None ) . unwrap ( ) ;
509+ let info = parse_usb_port_info (
510+ pyboard_hwid,
511+ None ,
512+ #[ cfg( feature = "iocontrol" ) ]
513+ None ,
514+ )
515+ . unwrap ( ) ;
421516
422517 assert_eq ! ( info. vid, 0xF055 ) ;
423518 assert_eq ! ( info. pid, 0x9802 ) ;
0 commit comments