Conversation
|
I can see this being quite useful on the application side too, for helping to identify where on the system a device is connected, or in bug reporting for identifying issues/behaviour that seem to be specific to certain hardware. Hacking this information into I reckon this would be better as |
|
I agree with @martinling, it should return a dedicated
|
|
Thanks for the feedback. A specific type was something I debated over too. One I considered was a How about something like this: That way on Linux the root hub device can still be obtained if desired. In answer to the above:
One issue I see with the above is that on Linux two root hubs can share the same host controller - different USB versions for example. Would we return two of the same |
|
Actually I have a idea for the multiple root hubs issue: rather than include the |
|
I would suggest putting all the PCI-specific info in a |
80251ea to
3efdb54
Compare
|
I'm happier with this and so ready for review again. I opted for a |
|
@martinling @kevinmehall not sure if you get notified by the change of status for this - sorry if this is a double notification! Would be good to get some feedback and potentially get this merged if you are happy. |
martinling
left a comment
There was a problem hiding this comment.
This looks pretty good but I think it could still do with some API tweaks.
src/enumeration.rs
Outdated
| /// System provider class name for the bus | ||
| pub(crate) provider_class: Option<String>, | ||
| /// System class name for the bus | ||
| pub(crate) class_name: Option<String>, |
There was a problem hiding this comment.
Although these two fields are populated on all systems, the names and their descriptions only make sense in the context of macOS. What's being put in these fields on Windows and Linux doesn't match the description here. I think we should just make these two fields #[cfg(target_os = "macos")] only.
Another option would be to name these two fields something more generic. At the moment though, the text that gets put here is so inconsistent in nature that I just don't think it's useful to expose it through a common API. Here's what I see for an XHCI controller on each system (provider_class, class_name):
macOS: AppleARMIODevice, AppleT1803USBXHCI
Linux: Linux 6.1.0-22-amd64 xhci-hcd, xHCI Host Controller
Windows: (Standard USB HUBs), USB Root Hub (USB 3.0)
The only actual information to be found in these strings is:
- The fact it's an XHCI / USB3 controller, which we already have an enum for.
- The class names in use on macOS - but those should be in macOS-specific fields.
- The driver name in use on Linux - but that should go in a Linux-specific field.
- The Linux kernel version - but programs have better ways to get that info than through nusb.
The USB Root Hub (USB 3.0) string is somewhat useful because it corresponds to what the user will see in Device Manager, but again, the place for that would be a Windows-specific field.
Also note that by making these fields OS-specific, we can avoid the need to make them Option.
My suggestion here would be:
/// System provider class name for the bus' host controller.
#[cfg(target_os = "macos")]
pub(crate) provider_class_name: String,
/// System class name for the bus' host controller.
#[cfg(target_os = "macos")]
pub(crate) class_name: String,
/// Kernel driver name for the bus' host controller.
#[cfg(target_os = "linux")]
pub(crate) driver_name: String,
#[cfg(target_os = "windows")]
/// Description of the root hub. This is how the bus will appear in Device Manager.
pub(crate) root_hub_description: String,And then similarly for the public accessor methods.
There was a problem hiding this comment.
Agreed, but on the other hand, I'm somewhat skeptical of having the majority of fields on this type be specific to one OS. Is any nusb user going to actually write OS-specific code against these? Or would it be better to consolidate them down to one name or description field that is a human-readable display name, not intended to map to any specific property?
There was a problem hiding this comment.
I'd be wary of getting into the business of generating human-readable text, because downstream application authors are never going to all agree on exactly what that text should look like, and that's before you even get into the can of worms that is localization.
We're already in the situation of having to consider parsing some of these details out of strings "helpfully" generated by the OS; it'd be good not to add another layer of that on top.
I think it's better to just stick to just reporting the known facts: this is bus N on an XHCI controller handled by driver Y, etc, and then everyone's free to generate text in any language to suit their preferences from the facts available.
There was a problem hiding this comment.
I had this conflict myself too, all good points. I went for the middle ground of:
- Separating
class_name,provider_class_nameto macOS with accessor methods. - Adding the field for
root_hub_descriptionon Windows. - Made
driveruniversal since it can be read from the pci driver symblic link filename. This is actually better than relying on the sysfs manufacturer field I think. - Created a
system_name()(maybe better as justname()?) accessor method for all platforms, which returnsnameon macOS,root_hub_descriptionon Windows and the root hub product string on Linux.
| devinst, | ||
| driver: Some(driver).filter(|s| !s.is_empty()), | ||
| bus_id, | ||
| controller: None, |
There was a problem hiding this comment.
It would be great if we could figure out a way to fill in the controller type on Windows.
There was a problem hiding this comment.
I considered parsing the device name on Windows; whether it has >= 3.0 (XHCI), 2.0 (EHCI) else OHCI. Should be accurate but it feels a bit dirty. I guess I could do this with a platform caveat covering this on Windows in the docs?
There was a problem hiding this comment.
This maybe should be looking at GUID_DEVINTERFACE_USB_HOST_CONTROLLER instead of GUID_DEVINTERFACE_USB_HUB and filtering for root hubs -- In a quick look at Device Manager, the host controller properties seem a lot more relevant than the root hub's, including several that say XHCI (don't know which of those would be safest to try to parse into the enum, though).
There was a problem hiding this comment.
It's a good point, the Host Controller contains a Service that seems to contain the controller type. I've updated to use this 366b2ab
Iterating over GUID_DEVINTERFACE_USB_HUB then getting the parent host controller like the Linux root hub exploration (rather than iterating over GUID_DEVINTERFACE_USB_HUB) still makes because a host controller can have more than one 'bus'. Iterating over the root hubs is the best way to build a BusInfo type I believe.
kevinmehall
left a comment
There was a problem hiding this comment.
Sorry I haven't had a chance to test this yet, but here are some initial comments (and thanks @martinling for jumping in).
To be honest, I'm wondering if your system profiler use case would be better served by grabbing some of the sysfs / cfgmgr32 / iokit bits and writing a new library that is explicitly an abstraction over the device hierarchy beyond USB. Specifically, I'm leaning towards saying no to the PCI parts here, because it's quite a bit of code and API surface that, well, isn't USB. Along those lines, people commonly ask for ways to find mass storage device drive letters / mount points, or serial port names corresponding to USB devices. Since libusb/nusb are focused on userspace device drivers for vendor class devices, those OS drivers and devices above and below USB seem out of scope, but there's a valid use case there that I don't think is served in a cross-platform way.
There's definitely some kind of BusInfo that makes sense in nusb though, and this is a useful exploration of what might belong on it.
| Ok(usb_service_iter(kAppleUSBXHCI)? | ||
| .filter_map(|h| probe_bus(h, UsbController::XHCI)) | ||
| .chain( | ||
| usb_service_iter(kAppleUSBEHCI)? | ||
| .filter_map(|h| probe_bus(h, UsbController::EHCI)) | ||
| .chain( | ||
| usb_service_iter(kAppleUSBOHCI)? | ||
| .filter_map(|h| probe_bus(h, UsbController::OHCI)) | ||
| .chain( | ||
| usb_service_iter(kAppleUSBVHCI)? | ||
| .filter_map(|h| probe_bus(h, UsbController::VHCI)), | ||
| ), | ||
| ), | ||
| )) |
There was a problem hiding this comment.
[...].into_iter().flatten() would be a better way to write this than the nested .chain()
There was a problem hiding this comment.
You mean like?
Ok([
usb_service_iter(kAppleUSBXHCI)?
.filter_map(|h| probe_bus(h, UsbControllerType::XHCI))
.collect::<Vec<_>>(),
usb_service_iter(kAppleUSBEHCI)?
.filter_map(|h| probe_bus(h, UsbControllerType::EHCI))
.collect::<Vec<_>>(),
usb_service_iter(kAppleUSBOHCI)?
.filter_map(|h| probe_bus(h, UsbControllerType::OHCI))
.collect::<Vec<_>>(),
usb_service_iter(kAppleUSBVHCI)?
.filter_map(|h| probe_bus(h, UsbControllerType::VHCI))
.collect::<Vec<_>>(),
]
.into_iter()
.flatten())
I was under the impression the chain is better because it keeps the iterator lazy? Or maybe I misunderstood and you're not suggesting collecting each step.
| devinst, | ||
| driver: Some(driver).filter(|s| !s.is_empty()), | ||
| bus_id, | ||
| controller: None, |
There was a problem hiding this comment.
This maybe should be looking at GUID_DEVINTERFACE_USB_HOST_CONTROLLER instead of GUID_DEVINTERFACE_USB_HUB and filtering for root hubs -- In a quick look at Device Manager, the host controller properties seem a lot more relevant than the root hub's, including several that say XHCI (don't know which of those would be safest to try to parse into the enum, though).
src/enumeration.rs
Outdated
| /// System provider class name for the bus | ||
| pub(crate) provider_class: Option<String>, | ||
| /// System class name for the bus | ||
| pub(crate) class_name: Option<String>, |
There was a problem hiding this comment.
Agreed, but on the other hand, I'm somewhat skeptical of having the majority of fields on this type be specific to one OS. Is any nusb user going to actually write OS-specific code against these? Or would it be better to consolidate them down to one name or description field that is a human-readable display name, not intended to map to any specific property?
f20302a to
1f67bf5
Compare
martinling
left a comment
There was a problem hiding this comment.
This looks better; the XHCI controller type is detected on Windows for me now, and the fields make a lot more sense.
I like the system_name field; I think that's a good compromise for including something that's human-readable and not OS-specific, but which matches descriptions visible elsewhere, rather than being something nusb has to invent.
However, the system_name values on macOS are a bit odd. The names I see on a Macbook M1 running 12.6.7 are usb-drd0 and usb-drd1. In System Information, these buses are both listed as USB 3.1 Bus. If we could get that latter string somehow that would be much more appropriate.
|
Thanks. Re the I searched for where System Information gets the name and can't get a good answer. I'm lead to believe it's a generated human readable name based on the bus information. It's not listed with |
|
I've made the controller type on Windows much more robust and put a note regarding where the I understand your thoughts @kevinmehall regarding the |
|
A compromise on the On Linux for instance, this could just be the value of That would give programs a way to "connect" the USB information from nusb to the rest of the system's device hierarchy, without needing to complicate the nusb API with non-USB details. |
Yeah I think that's the way to go, and |
|
Great. I've removed the Happy to rebase too, unless you're going to squash merge. |
Co-authored-by: Martin Ling <martin-github@earth.li>
|
I've also rebased with main to include the new Android support. Something I'm not fully sure of though: The path is still included in the |
|
Yeah, all of the functionality around listing devices and now buses aren't expected to work on (un-rooted) Android because it doesn't allow access to sysfs. Instead, you request device permissions and get a file descriptor to the device via Java APIs, and can use that with the new |
|
How do you want to move forwards on this @kevinmehall? The suggestion makes sense to me. I've done this for the |
|
Thanks! |
Considered this as a discussion but thought a PR is easier to show implementation. I understand the removal of root hubs from the
list_devicesfunction (since they are not devices) but I wonder if you would be open to a separate function, specifically to get the root hubs?I realise the ambitions of this crate is for low level implementation of user space drivers but I feel a lot of people are going to use it as a replacement of libusb too. Whilst root hubs are an phoney device and an abstraction of the Host Controller, I think they can be helpful:
On Linux it's easy to add this feature since I believe that's where the whole root hub concept came from and they are in sysfs. On the other platforms, I've parsed what data exists in a similar manor to how the Linux 'devices' return their descriptor. The Windows API does actually have mention of root hubs in a couple of places. I've tried to be explicit in the docs on where the data comes from.
To provide some context, I've been working on replacing libusb with nusb in my lsusb compatiable system USB profiler cyme. So for my use case it is specifically profiling the system USB. With the addition of this
list_root_hubsfunction, the output is actually more verbose than libusb because that only returns root hub information on Linux (develop PR).I understand if this is outside the scope of what you want to or too woolly do but hope since it's not impacting the other code and should be quite maintainable it's of interest. The existing code you've written in this crate (not exposed) make it easy to add but not outside the crate context. Open to changes if you like the idea but think it could be done differently - not everything I'm totally happy with.