@@ -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,37 @@ 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+
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)
133191}
134192
135193struct PortDevices {
@@ -320,11 +378,32 @@ impl PortDevice {
320378 // Determines the port_type for this device, and if it's a USB port populate the various fields.
321379 pub fn port_type ( & mut self ) -> SerialPortType {
322380 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+ )
325400 . 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+ } ;
328407 SerialPortType :: UsbPort ( info)
329408 } )
330409 . unwrap_or ( SerialPortType :: Unknown )
@@ -399,7 +478,13 @@ pub fn available_ports() -> Result<Vec<SerialPortInfo>> {
399478fn test_parsing_usb_port_information ( ) {
400479 let bm_uart_hwid = r"USB\VID_1D50&PID_6018&MI_02\6&A694CA9&0&0000" ;
401480 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 ( ) ;
403488
404489 assert_eq ! ( info. vid, 0x1D50 ) ;
405490 assert_eq ! ( info. pid, 0x6018 ) ;
@@ -408,7 +493,13 @@ fn test_parsing_usb_port_information() {
408493 assert_eq ! ( info. interface, Some ( 2 ) ) ;
409494
410495 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 ( ) ;
412503
413504 assert_eq ! ( info. vid, 0x0403 ) ;
414505 assert_eq ! ( info. pid, 0x6001 ) ;
@@ -417,7 +508,13 @@ fn test_parsing_usb_port_information() {
417508 assert_eq ! ( info. interface, None ) ;
418509
419510 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 ( ) ;
421518
422519 assert_eq ! ( info. vid, 0xF055 ) ;
423520 assert_eq ! ( info. pid, 0x9802 ) ;
0 commit comments