-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add port_number
, port_chain
for all platform
#58
Conversation
I'm wondering what location paths are used for -- what's your use case? This isn't necessarily a problem to add, but I don't want to go so far as caching every device property so wondering where to draw the line. For properties not supported, you can always look them up via the instance ID. |
Does the existing What would be ideal is a cross-platform way to identify the port, like libusb's Besides being Windows-specific and not portable, |
Getting Try enumerating the complete usb device tree to locate the position of the usb device, or use I will try to implement |
port_number
, port_chain
for all platform
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haven't had a chance to test this or give it a full review, but here are some initial questions.
// Find Port_#xxxx | ||
// Port_#0002.Hub_#000D | ||
if let Some(location_info) = devinst.get_property::<OsString>(DEVPKEY_Device_LocationInfo) { | ||
let s = location_info.to_string_lossy(); | ||
if &s[0..6] == "Port_#" { | ||
if let Ok(n) = s[6..10].parse::<u32>() { | ||
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::<Vec<OsString>>(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::<u32>() { | ||
return Some(n); | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
devinst.get_property::<u32>(DEVPKEY_Device_Address) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason all three of these techniques are used? Would be worth a comment explaining under what circumstances we'd hit each of these.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function references the libusb code.
The DEVPKEY_Device_LocationInfo
and DEVPKEY_Device_LocationPaths
parameters may not return a matching format.
} | ||
} | ||
// Find last #USB(x) | ||
// PCIROOT(B2)#PCI(0300)#PCI(0000)#USBROOT(0)#USB(1)#USB(2)#USBMI(3) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, do these #USB(n)
elements directly correspond to the port chain that we could just parse from the location path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be the case
.collect(); | ||
Ok(devs.into_iter()) | ||
} | ||
|
||
pub fn probe_device(devinst: DevInst) -> Option<DeviceInfo> { | ||
pub fn enum_root_hub(rootinst: DevInst) -> impl Iterator<Item = (DevInst, Vec<u32>)> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would there be a way to do this starting from a device and looking up its parent rather than switching to start at a root hub? Besides the increased complexity, one argument against this is that with hotplug attach events, we're given a single DevInst and would like to be able to find all of its DeviceInfo properties without walking the whole tree.
|
||
fn get_port_number(device: &IoService) -> Option<u32> { | ||
get_integer_property::<u32>(device, "PortNum").or_else(|| { | ||
if let Ok(parent) = get_parent(device) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the parent something other than a hub here? How is the line below getting the port for a specific child device when the property is on the parent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function also references the libusb code.
}; | ||
|
||
// Ignore the same sessionID | ||
if session_id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you observe it reporting a device as its own parent or find documentation indicating that, or is this just a precaution to prevent infinite-looping?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parent device may return the same sessionID
, which needs to be ignored until a new sessionID
is obtained or the obtaining fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is far too complicated for the functionality added. Libusb does a lot of crazy stuff that is no longer relevant; please don't copy what libusb does without understanding why it does it. Since much of this code is practically unreachable, it's also untested.
For example, this adds an alternative way to look up port numbers on macOS if portNum
is missing -- it turns out that libusb had this because macOS versions 10.11.0 - 10.11.2 lacked the portNum property, but macOS 10.11.2 is 9 years old. Meanwhile, the locationId
is just a bitwise encoding of the port path we're looking for, no iokit tree traversal needed.
Similarly on Windows, this adds two additional methods of finding the port number. The Windows backend already relies heavily on the port number, because it's what's passed to the hub ioctls to get descriptors. We've been using DEVPKEY_Device_Address
exclusively for that with no problems so far.
I've gone ahead and implemented a simpler version as #71. Need to figure out what to do with the bus number though, and may end up using some of this Windows code after all.
let bus_devs = cfgmgr32::list_interfaces(GUID_DEVINTERFACE_USB_HOST_CONTROLLER, None) | ||
.iter() | ||
.flat_map(|i| get_device_interface_property::<WCString>(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::<HashMap<String, u8>>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The existing bus number using DEVPKEY_Device_BusNumber
is indeed wrong (#70) but as mentioned in that issue I'm considering just exposing the bus ID as a string rather than trying to map it to a small integer.
} | ||
} | ||
// Find last #USB(x) | ||
// PCIROOT(B2)#PCI(0300)#PCI(0000)#USBROOT(0)#USB(1)#USB(2)#USBMI(3) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be the case
The usb bus number on Windows is related to Here's what I've tried so far to get the bus number. Use a prefix of |
Superseded by #71, but thanks for your work on this in helping figure out what was needed here. |
Add location paths for USB devices on Windows