From 27016ec073fc89a93a80d889422590401d30ab60 Mon Sep 17 00:00:00 2001 From: CCnut Date: Tue, 4 Jun 2024 12:23:33 +0800 Subject: [PATCH 1/7] windows: add location_paths --- src/enumeration.rs | 12 +++++++++++- src/platform/windows_winusb/enumeration.rs | 5 ++++- src/platform/windows_winusb/util.rs | 6 +++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/enumeration.rs b/src/enumeration.rs index 6fa8f050..418fe366 100644 --- a/src/enumeration.rs +++ b/src/enumeration.rs @@ -14,7 +14,7 @@ use crate::{Device, Error}; /// /// * Some fields are platform-specific /// * Linux: `sysfs_path` -/// * Windows: `instance_id`, `parent_instance_id`, `port_number`, `driver` +/// * Windows: `instance_id`, `parent_instance_id`, `port_number`, `driver`, `location_paths` /// * macOS: `registry_id`, `location_id` #[derive(Clone)] pub struct DeviceInfo { @@ -36,6 +36,9 @@ pub struct DeviceInfo { #[cfg(target_os = "windows")] pub(crate) driver: Option, + #[cfg(target_os = "windows")] + pub(crate) location_paths: Vec, + #[cfg(target_os = "macos")] pub(crate) registry_id: u64, @@ -101,6 +104,12 @@ impl DeviceInfo { self.driver.as_deref() } + /// *(Windows-only)* Location paths of this device + #[cfg(target_os = "windows")] + pub fn location_paths(&self) -> Vec<&OsStr> { + self.location_paths.iter().map(<_>::as_ref).collect() + } + /// *(macOS-only)* IOKit Location ID #[cfg(target_os = "macos")] pub fn location_id(&self) -> u32 { @@ -255,6 +264,7 @@ impl std::fmt::Debug for DeviceInfo { s.field("parent_instance_id", &self.parent_instance_id); s.field("port_number", &self.port_number); s.field("driver", &self.driver); + s.field("location_paths", &self.location_paths); } #[cfg(target_os = "macos")] diff --git a/src/platform/windows_winusb/enumeration.rs b/src/platform/windows_winusb/enumeration.rs index f79b0d5e..50c39571 100644 --- a/src/platform/windows_winusb/enumeration.rs +++ b/src/platform/windows_winusb/enumeration.rs @@ -8,7 +8,7 @@ use windows_sys::Win32::Devices::{ Properties::{ DEVPKEY_Device_Address, DEVPKEY_Device_BusNumber, DEVPKEY_Device_BusReportedDeviceDesc, DEVPKEY_Device_CompatibleIds, DEVPKEY_Device_HardwareIds, DEVPKEY_Device_InstanceId, - DEVPKEY_Device_Parent, DEVPKEY_Device_Service, + DEVPKEY_Device_LocationPaths, DEVPKEY_Device_Parent, DEVPKEY_Device_Service, }, Usb::GUID_DEVINTERFACE_USB_DEVICE, }; @@ -59,6 +59,8 @@ pub fn probe_device(devinst: DevInst) -> Option { .and_then(|s| s.into_string().ok()) .unwrap_or_default(); + let location_paths = devinst.get_property::>(DEVPKEY_Device_LocationPaths)?; + let mut interfaces = if driver.eq_ignore_ascii_case("usbccgp") { devinst .children() @@ -93,6 +95,7 @@ pub fn probe_device(devinst: DevInst) -> Option { devinst, port_number, driver: Some(driver).filter(|s| !s.is_empty()), + location_paths, bus_number: bus_number as u8, device_address: info.address, vendor_id: info.device_desc.idVendor, diff --git a/src/platform/windows_winusb/util.rs b/src/platform/windows_winusb/util.rs index 5661d390..8ac32897 100644 --- a/src/platform/windows_winusb/util.rs +++ b/src/platform/windows_winusb/util.rs @@ -166,7 +166,11 @@ impl<'a> Iterator for NulSepListIter<'a> { fn next(&mut self) -> Option { if let Some(next_nul) = self.0.iter().copied().position(|x| x == 0) { let (i, next) = self.0.split_at(next_nul + 1); - self.0 = next; + self.0 = if next.is_empty() || next.first() == Some(&0) { + &[] + } else { + next + }; Some(unsafe { WCStr::from_slice_unchecked(i) }) } else { None From 0c5e11d7b8b2572fd283db20f3f57bf31d992072 Mon Sep 17 00:00:00 2001 From: CCnut Date: Wed, 12 Jun 2024 15:35:47 +0800 Subject: [PATCH 2/7] windows: Fix location_paths return type --- src/enumeration.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/enumeration.rs b/src/enumeration.rs index 418fe366..ccc651a0 100644 --- a/src/enumeration.rs +++ b/src/enumeration.rs @@ -106,8 +106,8 @@ impl DeviceInfo { /// *(Windows-only)* Location paths of this device #[cfg(target_os = "windows")] - pub fn location_paths(&self) -> Vec<&OsStr> { - self.location_paths.iter().map(<_>::as_ref).collect() + pub fn location_paths(&self) -> impl Iterator { + self.location_paths.iter().map(<_>::as_ref) } /// *(macOS-only)* IOKit Location ID From dcf4e38702fb91e60b34c995188f8fedabaa4a2c Mon Sep 17 00:00:00 2001 From: CCnut Date: Thu, 27 Jun 2024 11:50:04 +0800 Subject: [PATCH 3/7] Add DeviceInfo::port_chain (macos in progress) --- src/enumeration.rs | 36 +++---- src/platform/linux_usbfs/enumeration.rs | 7 ++ src/platform/macos_iokit/enumeration.rs | 5 +- src/platform/windows_winusb/enumeration.rs | 110 ++++++++++++++++++--- 4 files changed, 124 insertions(+), 34 deletions(-) diff --git a/src/enumeration.rs b/src/enumeration.rs index ccc651a0..515d1bde 100644 --- a/src/enumeration.rs +++ b/src/enumeration.rs @@ -14,7 +14,7 @@ use crate::{Device, Error}; /// /// * Some fields are platform-specific /// * Linux: `sysfs_path` -/// * Windows: `instance_id`, `parent_instance_id`, `port_number`, `driver`, `location_paths` +/// * Windows: `instance_id`, `parent_instance_id`, `driver` /// * macOS: `registry_id`, `location_id` #[derive(Clone)] pub struct DeviceInfo { @@ -27,18 +27,12 @@ pub struct DeviceInfo { #[cfg(target_os = "windows")] pub(crate) parent_instance_id: OsString, - #[cfg(target_os = "windows")] - pub(crate) port_number: u32, - #[cfg(target_os = "windows")] pub(crate) devinst: crate::platform::DevInst, #[cfg(target_os = "windows")] pub(crate) driver: Option, - #[cfg(target_os = "windows")] - pub(crate) location_paths: Vec, - #[cfg(target_os = "macos")] pub(crate) registry_id: u64, @@ -46,6 +40,8 @@ pub struct DeviceInfo { pub(crate) location_id: u32, pub(crate) bus_number: u8, + pub(crate) port_number: u32, + pub(crate) port_chain: Vec, pub(crate) device_address: u8, pub(crate) vendor_id: u16, @@ -92,24 +88,12 @@ impl DeviceInfo { &self.parent_instance_id } - /// *(Windows-only)* Port number - #[cfg(target_os = "windows")] - pub fn port_number(&self) -> u32 { - self.port_number - } - /// *(Windows-only)* Driver associated with the device as a whole #[cfg(target_os = "windows")] pub fn driver(&self) -> Option<&str> { self.driver.as_deref() } - /// *(Windows-only)* Location paths of this device - #[cfg(target_os = "windows")] - pub fn location_paths(&self) -> impl Iterator { - self.location_paths.iter().map(<_>::as_ref) - } - /// *(macOS-only)* IOKit Location ID #[cfg(target_os = "macos")] pub fn location_id(&self) -> u32 { @@ -127,6 +111,16 @@ impl DeviceInfo { self.bus_number } + /// Port number + pub fn port_number(&self) -> u32 { + self.port_number + } + + /// Port chain + pub fn port_chain(&self) -> impl Iterator { + self.port_chain.iter() + } + /// Number identifying the device within the bus. pub fn device_address(&self) -> u8 { self.device_address @@ -238,6 +232,8 @@ impl std::fmt::Debug for DeviceInfo { let mut s = f.debug_struct("DeviceInfo"); s.field("bus_number", &self.bus_number) + .field("port_number", &self.port_number) + .field("port_numbers", &self.port_chain) .field("device_address", &self.device_address) .field("vendor_id", &format_args!("0x{:04X}", self.vendor_id)) .field("product_id", &format_args!("0x{:04X}", self.product_id)) @@ -262,9 +258,7 @@ impl std::fmt::Debug for DeviceInfo { { s.field("instance_id", &self.instance_id); s.field("parent_instance_id", &self.parent_instance_id); - s.field("port_number", &self.port_number); s.field("driver", &self.driver); - s.field("location_paths", &self.location_paths); } #[cfg(target_os = "macos")] diff --git a/src/platform/linux_usbfs/enumeration.rs b/src/platform/linux_usbfs/enumeration.rs index e046f361..fa1c7d48 100644 --- a/src/platform/linux_usbfs/enumeration.rs +++ b/src/platform/linux_usbfs/enumeration.rs @@ -77,8 +77,15 @@ pub fn list_devices() -> Result, Error> { pub fn probe_device(path: SysfsPath) -> Result { debug!("probe device {path:?}"); + let port_chain: Vec = path + .read_attr::("devpath")? + .split('.') + .flat_map(|v| v.parse::()) + .collect(); Ok(DeviceInfo { bus_number: path.read_attr("busnum")?, + port_number: *port_chain.last().unwrap_or(&0), + port_chain, device_address: path.read_attr("devnum")?, vendor_id: path.read_attr_hex("idVendor")?, product_id: path.read_attr_hex("idProduct")?, diff --git a/src/platform/macos_iokit/enumeration.rs b/src/platform/macos_iokit/enumeration.rs index d9ae1a9f..5e95f2c4 100644 --- a/src/platform/macos_iokit/enumeration.rs +++ b/src/platform/macos_iokit/enumeration.rs @@ -50,10 +50,13 @@ fn probe_device(device: IoService) -> Option { log::debug!("Probing device {registry_id}"); // Can run `ioreg -p IOUSB -l` to see all properties + let location_id = get_integer_property(&device, "locationID")?; Some(DeviceInfo { registry_id, location_id: get_integer_property(&device, "locationID")?, - bus_number: 0, // TODO: does this exist on macOS? + bus_number: location_id >> 24, // ref libusb process_new_device + port_number: 0, // TODO: ref libusb get_device_port + port_chain: vec![], // TODO: ref libusb get_device_parent_sessionID device_address: get_integer_property(&device, "USB Address")?, vendor_id: get_integer_property(&device, "idVendor")?, product_id: get_integer_property(&device, "idProduct")?, diff --git a/src/platform/windows_winusb/enumeration.rs b/src/platform/windows_winusb/enumeration.rs index 50c39571..2f3f3688 100644 --- a/src/platform/windows_winusb/enumeration.rs +++ b/src/platform/windows_winusb/enumeration.rs @@ -1,4 +1,5 @@ use std::{ + collections::VecDeque, ffi::{OsStr, OsString}, io::ErrorKind, }; @@ -6,11 +7,14 @@ use std::{ use log::debug; use windows_sys::Win32::Devices::{ Properties::{ - DEVPKEY_Device_Address, DEVPKEY_Device_BusNumber, DEVPKEY_Device_BusReportedDeviceDesc, - DEVPKEY_Device_CompatibleIds, DEVPKEY_Device_HardwareIds, DEVPKEY_Device_InstanceId, + DEVPKEY_Device_Address, DEVPKEY_Device_BusReportedDeviceDesc, DEVPKEY_Device_CompatibleIds, + DEVPKEY_Device_HardwareIds, DEVPKEY_Device_InstanceId, DEVPKEY_Device_LocationInfo, DEVPKEY_Device_LocationPaths, DEVPKEY_Device_Parent, DEVPKEY_Device_Service, }, - Usb::GUID_DEVINTERFACE_USB_DEVICE, + Usb::{ + GUID_DEVINTERFACE_USB_DEVICE, GUID_DEVINTERFACE_USB_HOST_CONTROLLER, + GUID_DEVINTERFACE_USB_HUB, + }, }; use crate::{DeviceInfo, Error, InterfaceInfo}; @@ -22,22 +26,75 @@ use super::{ }; pub fn list_devices() -> Result, Error> { - let devs: Vec = cfgmgr32::list_interfaces(GUID_DEVINTERFACE_USB_DEVICE, None) + let hubs = cfgmgr32::list_interfaces(GUID_DEVINTERFACE_USB_HOST_CONTROLLER, None); + let devs: Vec = hubs .iter() .flat_map(|i| get_device_interface_property::(i, DEVPKEY_Device_InstanceId)) .flat_map(|d| DevInst::from_instance_id(&d)) - .flat_map(probe_device) + .flat_map(|root| root.children()) + .map(enum_root_hub) + .enumerate() + .flat_map(|bus| { + bus.1 + .flat_map(move |d| probe_device(d.0, (bus.0 + 1) as u32, d.1)) + }) .collect(); Ok(devs.into_iter()) } -pub fn probe_device(devinst: DevInst) -> Option { +pub fn enum_root_hub(rootinst: DevInst) -> impl Iterator)> { + let mut device_inst = VecDeque::new(); + + let mut tree: VecDeque> = VecDeque::new(); + let mut port_chain = Vec::new(); + + tree.push_back(rootinst.children().collect()); + while let Some(mut hub) = tree.pop_back() { + loop { + if let Some(inst) = hub.pop_back() { + if let Some(hubinst) = inst + .interfaces(GUID_DEVINTERFACE_USB_HUB) + .iter() + .next() + .and_then(|i| { + get_device_interface_property::(i, DEVPKEY_Device_InstanceId) + }) + .and_then(|d| DevInst::from_instance_id(&d)) + { + let port = get_port_number(hubinst).unwrap_or(0); + tree.push_back(hub); + tree.push_back(hubinst.children().collect()); + port_chain.push(port); + break; + } else if let Some(devinst) = inst + .interfaces(GUID_DEVINTERFACE_USB_DEVICE) + .iter() + .next() + .and_then(|i| { + get_device_interface_property::(i, DEVPKEY_Device_InstanceId) + }) + .and_then(|d| DevInst::from_instance_id(&d)) + { + let port = get_port_number(devinst).unwrap_or(0); + port_chain.push(port); + device_inst.push_back((devinst, port_chain.clone())); + port_chain.pop(); + } + } else { + port_chain.pop(); + break; + } + } + } + + device_inst.into_iter() +} + +pub fn probe_device(devinst: DevInst, bus_number: u32, port_chain: Vec) -> Option { let instance_id = devinst.get_property::(DEVPKEY_Device_InstanceId)?; debug!("Probing device {instance_id:?}"); let parent_instance_id = devinst.get_property::(DEVPKEY_Device_Parent)?; - let bus_number = devinst.get_property::(DEVPKEY_Device_BusNumber)?; - let port_number = devinst.get_property::(DEVPKEY_Device_Address)?; let hub_port = HubPort::by_child_devinst(devinst).ok()?; let info = hub_port.get_info().ok()?; @@ -59,8 +116,6 @@ pub fn probe_device(devinst: DevInst) -> Option { .and_then(|s| s.into_string().ok()) .unwrap_or_default(); - let location_paths = devinst.get_property::>(DEVPKEY_Device_LocationPaths)?; - let mut interfaces = if driver.eq_ignore_ascii_case("usbccgp") { devinst .children() @@ -93,10 +148,10 @@ pub fn probe_device(devinst: DevInst) -> Option { instance_id, parent_instance_id, devinst, - port_number, driver: Some(driver).filter(|s| !s.is_empty()), - location_paths, bus_number: bus_number as u8, + port_number: *port_chain.last().unwrap_or(&0), + port_chain, device_address: info.address, vendor_id: info.device_desc.idVendor, product_id: info.device_desc.idProduct, @@ -189,6 +244,37 @@ pub(crate) fn find_device_interface_path(dev: DevInst, intf: u8) -> Result Option { + // Find Port_#xxxx + // Port_#0002.Hub_#000D + if let Some(location_info) = devinst.get_property::(DEVPKEY_Device_LocationInfo) { + let s = location_info.to_string_lossy(); + if &s[0..6] == "Port_#" { + if let Ok(n) = s[6..10].parse::() { + return Some(n); + } + } + } + // Find last #USB(x) + // PCIROOT(B2)#PCI(0300)#PCI(0000)#USBROOT(0)#USB(1)#USB(2)#USBMI(3) + if let Some(location_paths) = + devinst.get_property::>(DEVPKEY_Device_LocationPaths) + { + for location_path in location_paths { + let s = location_path.to_string_lossy(); + for b in s.split('#').rev() { + if b.contains("USB(") { + if let Ok(n) = b[5..b.len()].parse::() { + return Some(n); + } + break; + } + } + } + } + devinst.get_property::(DEVPKEY_Device_Address) +} + fn get_interface_number(intf_dev: DevInst) -> Option { let hw_ids = intf_dev.get_property::>(DEVPKEY_Device_HardwareIds); hw_ids From 8e1539d8f6613f5b25323cfc7b6a8886717255b6 Mon Sep 17 00:00:00 2001 From: CCnut Date: Thu, 27 Jun 2024 21:45:19 +0800 Subject: [PATCH 4/7] Add DeviceInfo::{port_number, port_chain} for all platform --- src/enumeration.rs | 2 +- src/platform/macos_iokit/enumeration.rs | 88 ++++++++++++++++++++-- src/platform/windows_winusb/enumeration.rs | 22 ++++-- 3 files changed, 96 insertions(+), 16 deletions(-) diff --git a/src/enumeration.rs b/src/enumeration.rs index 515d1bde..0a3ff6d0 100644 --- a/src/enumeration.rs +++ b/src/enumeration.rs @@ -233,7 +233,7 @@ impl std::fmt::Debug for DeviceInfo { s.field("bus_number", &self.bus_number) .field("port_number", &self.port_number) - .field("port_numbers", &self.port_chain) + .field("port_chain", &self.port_chain) .field("device_address", &self.device_address) .field("vendor_id", &format_args!("0x{:04X}", self.vendor_id)) .field("product_id", &format_args!("0x{:04X}", self.product_id)) diff --git a/src/platform/macos_iokit/enumeration.rs b/src/platform/macos_iokit/enumeration.rs index 5e95f2c4..095f760b 100644 --- a/src/platform/macos_iokit/enumeration.rs +++ b/src/platform/macos_iokit/enumeration.rs @@ -1,7 +1,8 @@ -use std::io::ErrorKind; +use std::{collections::VecDeque, io::ErrorKind}; use core_foundation::{ base::{CFType, TCFType}, + data::CFData, number::CFNumber, string::CFString, ConcreteCFType, @@ -9,8 +10,9 @@ use core_foundation::{ use io_kit_sys::{ kIOMasterPortDefault, kIORegistryIterateParents, kIORegistryIterateRecursively, keys::kIOServicePlane, ret::kIOReturnSuccess, usb::lib::kIOUSBDeviceClassName, - IORegistryEntryGetChildIterator, IORegistryEntryGetRegistryEntryID, - IORegistryEntrySearchCFProperty, IOServiceGetMatchingServices, IOServiceMatching, + IORegistryEntryGetChildIterator, IORegistryEntryGetParentEntry, + IORegistryEntryGetRegistryEntryID, IORegistryEntrySearchCFProperty, + IOServiceGetMatchingServices, IOServiceMatching, }; use log::debug; @@ -51,12 +53,13 @@ fn probe_device(device: IoService) -> Option { // Can run `ioreg -p IOUSB -l` to see all properties let location_id = get_integer_property(&device, "locationID")?; + let port_chain: Vec = get_port_chain(&device).collect(); Some(DeviceInfo { registry_id, - location_id: get_integer_property(&device, "locationID")?, - bus_number: location_id >> 24, // ref libusb process_new_device - port_number: 0, // TODO: ref libusb get_device_port - port_chain: vec![], // TODO: ref libusb get_device_parent_sessionID + location_id, + bus_number: (location_id >> 24) as u8, + port_number: *port_chain.last().unwrap_or(&0), + port_chain, device_address: get_integer_property(&device, "USB Address")?, vendor_id: get_integer_property(&device, "idVendor")?, product_id: get_integer_property(&device, "idProduct")?, @@ -130,6 +133,10 @@ fn get_string_property(device: &IoService, property: &'static str) -> Option(device, property).map(|s| s.to_string()) } +fn get_data_property(device: &IoService, property: &'static str) -> Option> { + get_property::(device, property).map(|d| d.to_vec()) +} + fn get_integer_property>(device: &IoService, property: &'static str) -> Option { get_property::(device, property) .and_then(|n| n.to_i64()) @@ -150,6 +157,73 @@ fn get_children(device: &IoService) -> Result { } } +fn get_parent(device: &IoService) -> Result { + unsafe { + let mut handle = 0; + let r = IORegistryEntryGetParentEntry(device.get(), kIOServicePlane as *mut _, &mut handle); + if r != kIOReturnSuccess { + debug!("IORegistryEntryGetParentEntry failed: {r}"); + return Err(Error::from_raw_os_error(r)); + } + + Ok(IoService::new(handle)) + } +} + +fn get_port_number(device: &IoService) -> Option { + get_integer_property::(device, "PortNum").or_else(|| { + if let Ok(parent) = get_parent(device) { + return get_data_property(&parent, "port") + .map(|d| u32::from_ne_bytes(d[0..4].try_into().unwrap())); + } + None + }) +} + +fn get_port_chain(device: &IoService) -> impl Iterator { + let mut port_chain = VecDeque::new(); + + if let Some(port_number) = get_port_number(device) { + port_chain.push_back(port_number); + } + + if let Ok(mut hub) = get_parent(device) { + loop { + let port_number = match get_port_number(&hub) { + Some(p) => p, + None => break, + }; + if port_number == 0 { + break; + } + port_chain.push_front(port_number); + + let session_id = match get_integer_property::(&hub, "sessionID") { + Some(session_id) => session_id, + None => break, + }; + + hub = match get_parent(&hub) { + Ok(hub) => hub, + Err(_) => break, + }; + + // Ignore the same sessionID + if session_id + == match get_integer_property::(&hub, "sessionID") { + Some(session_id) => session_id, + None => break, + } + { + port_chain.pop_front(); + continue; + } + } + } + + port_chain.into_iter() +} + fn map_speed(speed: u32) -> Option { // https://developer.apple.com/documentation/iokit/1425357-usbdevicespeed match speed { diff --git a/src/platform/windows_winusb/enumeration.rs b/src/platform/windows_winusb/enumeration.rs index 2f3f3688..a18a4b77 100644 --- a/src/platform/windows_winusb/enumeration.rs +++ b/src/platform/windows_winusb/enumeration.rs @@ -61,10 +61,13 @@ pub fn enum_root_hub(rootinst: DevInst) -> impl Iterator impl Iterator Date: Wed, 24 Jul 2024 01:16:15 +0800 Subject: [PATCH 5/7] windows: get port_chain starting from a device --- src/platform/windows_winusb/enumeration.rs | 129 +++++++++------------ 1 file changed, 58 insertions(+), 71 deletions(-) diff --git a/src/platform/windows_winusb/enumeration.rs b/src/platform/windows_winusb/enumeration.rs index a18a4b77..d187b099 100644 --- a/src/platform/windows_winusb/enumeration.rs +++ b/src/platform/windows_winusb/enumeration.rs @@ -1,5 +1,5 @@ use std::{ - collections::VecDeque, + collections::{HashMap, VecDeque}, ffi::{OsStr, OsString}, io::ErrorKind, }; @@ -8,13 +8,11 @@ use log::debug; use windows_sys::Win32::Devices::{ Properties::{ DEVPKEY_Device_Address, DEVPKEY_Device_BusReportedDeviceDesc, DEVPKEY_Device_CompatibleIds, - DEVPKEY_Device_HardwareIds, DEVPKEY_Device_InstanceId, DEVPKEY_Device_LocationInfo, - DEVPKEY_Device_LocationPaths, DEVPKEY_Device_Parent, DEVPKEY_Device_Service, - }, - Usb::{ - GUID_DEVINTERFACE_USB_DEVICE, GUID_DEVINTERFACE_USB_HOST_CONTROLLER, - GUID_DEVINTERFACE_USB_HUB, + DEVPKEY_Device_EnumeratorName, DEVPKEY_Device_HardwareIds, DEVPKEY_Device_InstanceId, + DEVPKEY_Device_LocationInfo, DEVPKEY_Device_LocationPaths, DEVPKEY_Device_Parent, + DEVPKEY_Device_Service, }, + Usb::{GUID_DEVINTERFACE_USB_DEVICE, GUID_DEVINTERFACE_USB_HOST_CONTROLLER}, }; use crate::{DeviceInfo, Error, InterfaceInfo}; @@ -26,77 +24,25 @@ use super::{ }; pub fn list_devices() -> Result, Error> { - let hubs = cfgmgr32::list_interfaces(GUID_DEVINTERFACE_USB_HOST_CONTROLLER, None); - let devs: Vec = hubs + let bus_devs = cfgmgr32::list_interfaces(GUID_DEVINTERFACE_USB_HOST_CONTROLLER, None) .iter() .flat_map(|i| get_device_interface_property::(i, DEVPKEY_Device_InstanceId)) .flat_map(|d| DevInst::from_instance_id(&d)) - .flat_map(|root| root.children()) - .map(enum_root_hub) + .flat_map(|d| d.children()) + .map(|d| d.instance_id().to_string()) .enumerate() - .flat_map(|bus| { - bus.1 - .flat_map(move |d| probe_device(d.0, (bus.0 + 1) as u32, d.1)) - }) + .map(|v| (v.1, (v.0 + 1) as u8)) + .collect::>(); + let devs: Vec = cfgmgr32::list_interfaces(GUID_DEVINTERFACE_USB_DEVICE, None) + .iter() + .flat_map(|i| get_device_interface_property::(i, DEVPKEY_Device_InstanceId)) + .flat_map(|d| DevInst::from_instance_id(&d)) + .flat_map(|i| probe_device(i, &bus_devs)) .collect(); Ok(devs.into_iter()) } -pub fn enum_root_hub(rootinst: DevInst) -> impl Iterator)> { - let mut device_inst = VecDeque::new(); - - let mut tree: VecDeque> = VecDeque::new(); - let mut port_chain = Vec::new(); - - tree.push_back(rootinst.children().collect()); - while let Some(mut hub) = tree.pop_back() { - loop { - if let Some(inst) = hub.pop_back() { - if let Some(hubinst) = inst - .interfaces(GUID_DEVINTERFACE_USB_HUB) - .iter() - .next() - .and_then(|i| { - get_device_interface_property::(i, DEVPKEY_Device_InstanceId) - }) - .and_then(|d| DevInst::from_instance_id(&d)) - { - if let Some(port_number) = get_port_number(hubinst) { - if port_number != 0 { - tree.push_back(hub); - tree.push_back(hubinst.children().collect()); - port_chain.push(port_number); - } - } - break; - } else if let Some(devinst) = inst - .interfaces(GUID_DEVINTERFACE_USB_DEVICE) - .iter() - .next() - .and_then(|i| { - get_device_interface_property::(i, DEVPKEY_Device_InstanceId) - }) - .and_then(|d| DevInst::from_instance_id(&d)) - { - if let Some(port_number) = get_port_number(devinst) { - if port_number != 0 { - port_chain.push(port_number); - device_inst.push_back((devinst, port_chain.clone())); - port_chain.pop(); - } - } - } - } else { - port_chain.pop(); - break; - } - } - } - - device_inst.into_iter() -} - -pub fn probe_device(devinst: DevInst, bus_number: u32, port_chain: Vec) -> Option { +pub fn probe_device(devinst: DevInst, bus_devs: &HashMap) -> Option { let instance_id = devinst.get_property::(DEVPKEY_Device_InstanceId)?; debug!("Probing device {instance_id:?}"); @@ -150,12 +96,15 @@ pub fn probe_device(devinst: DevInst, bus_number: u32, port_chain: Vec) -> interfaces.sort_unstable_by_key(|i| i.interface_number); + let (bus_number, port_chain) = get_port_chain(devinst, bus_devs); + let port_chain = port_chain.collect::>(); + Some(DeviceInfo { instance_id, parent_instance_id, devinst, driver: Some(driver).filter(|s| !s.is_empty()), - bus_number: bus_number as u8, + bus_number, port_number: *port_chain.last().unwrap_or(&0), port_chain, device_address: info.address, @@ -250,6 +199,44 @@ pub(crate) fn find_device_interface_path(dev: DevInst, intf: u8) -> Result) -> (u8, impl Iterator) { + let mut bus_number = 0; + let mut port_chain = VecDeque::new(); + + if let Some(port_number) = get_port_number(dev) { + port_chain.push_back(port_number); + } + + if let Some(mut parent) = dev.parent() { + loop { + if parent + .get_property::(DEVPKEY_Device_EnumeratorName) + .unwrap_or_default() + .eq_ignore_ascii_case("USB") + { + if let Some(port_number) = get_port_number(parent) { + if port_number != 0 { + port_chain.push_front(port_number); + if let Some(d) = parent.parent() { + parent = d; + continue; + } + } else { + if let Some(bus_dev) = + parent.get_property::(DEVPKEY_Device_InstanceId) + { + bus_number = *bus_devs.get(&bus_dev.to_string()).unwrap_or(&0); + } + } + } + } + break; + } + } + + (bus_number, port_chain.into_iter()) +} + fn get_port_number(devinst: DevInst) -> Option { // Find Port_#xxxx // Port_#0002.Hub_#000D From 28b676254b47411f256cf67a1537ed95d2f737e8 Mon Sep 17 00:00:00 2001 From: CCnut Date: Sat, 3 Aug 2024 02:00:07 +0800 Subject: [PATCH 6/7] macos: fix fmt --- src/platform/macos_iokit/enumeration.rs | 68 ++++++++++++------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/platform/macos_iokit/enumeration.rs b/src/platform/macos_iokit/enumeration.rs index 615ad340..b10a7033 100644 --- a/src/platform/macos_iokit/enumeration.rs +++ b/src/platform/macos_iokit/enumeration.rs @@ -1,5 +1,5 @@ use std::{collections::VecDeque, io::ErrorKind}; - + use core_foundation::{ base::{CFType, TCFType}, data::CFData, @@ -15,42 +15,42 @@ use io_kit_sys::{ IOServiceGetMatchingServices, IOServiceMatching, }; use log::debug; - + use crate::{DeviceInfo, Error, InterfaceInfo, Speed}; - + use super::iokit::{IoService, IoServiceIterator}; - + fn usb_service_iter() -> Result { unsafe { let dictionary = IOServiceMatching(kIOUSBDeviceClassName); if dictionary.is_null() { return Err(Error::new(ErrorKind::Other, "IOServiceMatching failed")); } - + let mut iterator = 0; let r = IOServiceGetMatchingServices(kIOMasterPortDefault, dictionary, &mut iterator); if r != kIOReturnSuccess { return Err(Error::from_raw_os_error(r)); } - + Ok(IoServiceIterator::new(iterator)) } } - + pub fn list_devices() -> Result, Error> { Ok(usb_service_iter()?.filter_map(probe_device)) } - + pub(crate) fn service_by_registry_id(registry_id: u64) -> Result { usb_service_iter()? .find(|dev| get_registry_id(dev) == Some(registry_id)) .ok_or(Error::new(ErrorKind::NotFound, "not found by registry id")) } - + pub(crate) fn probe_device(device: IoService) -> Option { let registry_id = get_registry_id(&device)?; log::debug!("Probing device {registry_id:08x}"); - + // Can run `ioreg -p IOUSB -l` to see all properties let location_id = get_integer_property(&device, "locationID")? as u32; let port_chain: Vec = get_port_chain(&device).collect(); @@ -86,12 +86,12 @@ pub(crate) fn probe_device(device: IoService) -> Option { }), }) } - + pub(crate) fn get_registry_id(device: &IoService) -> Option { unsafe { let mut out = 0; let r = IORegistryEntryGetRegistryEntryID(device.get(), &mut out); - + if r == kIOReturnSuccess { Some(out) } else { @@ -101,11 +101,11 @@ pub(crate) fn get_registry_id(device: &IoService) -> Option { } } } - + fn get_property(device: &IoService, property: &'static str) -> Option { unsafe { let cf_property = CFString::from_static_string(property); - + let raw = IORegistryEntrySearchCFProperty( device.get(), kIOServicePlane as *mut i8, @@ -113,30 +113,30 @@ fn get_property(device: &IoService, property: &'static str) - std::ptr::null(), kIORegistryIterateRecursively | kIORegistryIterateParents, ); - + if raw.is_null() { debug!("Device does not have property `{property}`"); return None; } - + let res = CFType::wrap_under_create_rule(raw).downcast_into(); - + if res.is_none() { debug!("Failed to convert device property `{property}`"); } - + res } } - + fn get_string_property(device: &IoService, property: &'static str) -> Option { get_property::(device, property).map(|s| s.to_string()) } - + fn get_data_property(device: &IoService, property: &'static str) -> Option> { get_property::(device, property).map(|d| d.to_vec()) } - + fn get_integer_property(device: &IoService, property: &'static str) -> Option { let n = get_property::(device, property)?; n.to_i64().or_else(|| { @@ -144,7 +144,7 @@ fn get_integer_property(device: &IoService, property: &'static str) -> Option Result { unsafe { let mut iterator = 0; @@ -154,11 +154,11 @@ fn get_children(device: &IoService) -> Result { debug!("IORegistryEntryGetChildIterator failed: {r}"); return Err(Error::from_raw_os_error(r)); } - + Ok(IoServiceIterator::new(iterator)) } } - + fn get_parent(device: &IoService) -> Result { unsafe { let mut handle = 0; @@ -167,11 +167,11 @@ fn get_parent(device: &IoService) -> Result { debug!("IORegistryEntryGetParentEntry failed: {r}"); return Err(Error::from_raw_os_error(r)); } - + Ok(IoService::new(handle)) } } - + fn get_port_number(device: &IoService) -> Option { get_integer_property(device, "PortNum").map_or_else( || { @@ -184,14 +184,14 @@ fn get_port_number(device: &IoService) -> Option { |v| Some(v as u32), ) } - + fn get_port_chain(device: &IoService) -> impl Iterator { let mut port_chain = VecDeque::new(); - + if let Some(port_number) = get_port_number(device) { port_chain.push_back(port_number); } - + if let Ok(mut hub) = get_parent(device) { loop { let port_number = match get_port_number(&hub) { @@ -202,17 +202,17 @@ fn get_port_chain(device: &IoService) -> impl Iterator { break; } port_chain.push_front(port_number); - + let session_id = match get_integer_property(&hub, "sessionID") { Some(session_id) => session_id, None => break, }; - + hub = match get_parent(&hub) { Ok(hub) => hub, Err(_) => break, }; - + // Ignore the same sessionID if session_id == match get_integer_property(&hub, "sessionID") { @@ -225,10 +225,10 @@ fn get_port_chain(device: &IoService) -> impl Iterator { } } } - + port_chain.into_iter() } - + fn map_speed(speed: i64) -> Option { // https://developer.apple.com/documentation/iokit/1425357-usbdevicespeed match speed { From 980cbc19342faacc1dfefa170bd75aa8340d5e7f Mon Sep 17 00:00:00 2001 From: CCnut Date: Sat, 3 Aug 2024 02:01:11 +0800 Subject: [PATCH 7/7] windows: fix probe_device --- src/platform/windows_winusb/enumeration.rs | 25 +++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/platform/windows_winusb/enumeration.rs b/src/platform/windows_winusb/enumeration.rs index c777af53..897e0eca 100644 --- a/src/platform/windows_winusb/enumeration.rs +++ b/src/platform/windows_winusb/enumeration.rs @@ -30,25 +30,16 @@ use super::{ }; pub fn list_devices() -> Result, Error> { - let bus_devs = cfgmgr32::list_interfaces(GUID_DEVINTERFACE_USB_HOST_CONTROLLER, None) - .iter() - .flat_map(|i| get_device_interface_property::(i, DEVPKEY_Device_InstanceId)) - .flat_map(|d| DevInst::from_instance_id(&d)) - .flat_map(|d| d.children()) - .map(|d| d.instance_id().to_string()) - .enumerate() - .map(|v| (v.1, (v.0 + 1) as u8)) - .collect::>(); let devs: Vec = cfgmgr32::list_interfaces(GUID_DEVINTERFACE_USB_DEVICE, None) .iter() .flat_map(|i| get_device_interface_property::(i, DEVPKEY_Device_InstanceId)) .flat_map(|d| DevInst::from_instance_id(&d)) - .flat_map(|i| probe_device(i, &bus_devs)) + .flat_map(probe_device) .collect(); Ok(devs.into_iter()) } -pub fn probe_device(devinst: DevInst, bus_devs: &HashMap) -> Option { +pub fn probe_device(devinst: DevInst) -> Option { let instance_id = devinst.get_property::(DEVPKEY_Device_InstanceId)?; debug!("Probing device {instance_id:?}"); @@ -107,7 +98,17 @@ pub fn probe_device(devinst: DevInst, bus_devs: &HashMap) -> Option< interfaces.sort_unstable_by_key(|i| i.interface_number); - let (bus_number, port_chain) = get_port_chain(devinst, bus_devs); + let bus_devs = cfgmgr32::list_interfaces(GUID_DEVINTERFACE_USB_HOST_CONTROLLER, None) + .iter() + .flat_map(|i| get_device_interface_property::(i, DEVPKEY_Device_InstanceId)) + .flat_map(|d| DevInst::from_instance_id(&d)) + .flat_map(|d| d.children()) + .map(|d| d.instance_id().to_string()) + .enumerate() + .map(|v| (v.1, (v.0 + 1) as u8)) + .collect::>(); + + let (bus_number, port_chain) = get_port_chain(devinst, &bus_devs); let port_chain = port_chain.collect::>(); Some(DeviceInfo {