Skip to content
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

missing vendor, product strings on windows 10. #24

Closed
Dirbaio opened this issue Jan 8, 2024 · 4 comments
Closed

missing vendor, product strings on windows 10. #24

Dirbaio opened this issue Jan 8, 2024 · 4 comments
Labels

Comments

@Dirbaio
Copy link
Contributor

Dirbaio commented Jan 8, 2024

There's something strange going on with vendor/product strings on Windows.

Example 1: with a picoprobe, running the list example on Linux gives:

DeviceInfo {
    bus_number: 1,
    device_address: 7,
    vendor_id: 0x2E8A,
    product_id: 0x000C,
    device_version: 0x0103,
    class: 0,
    subclass: 0,
    protocol: 0,
    speed: Some(
        Full,
    ),
    manufacturer_string: Some(
        "Raspberry Pi",
    ),
    product_string: Some(
        "Picoprobe (CMSIS-DAP)",
    ),
    serial_number: Some(
        "E66038B71354762F",
    ),
    path: SysfsPath(
        "/sys/bus/usb/devices/1-4.4.4",
    ),
}

on Windows:

DeviceInfo {
    bus_number: 0,
    device_address: 3,
    vendor_id: 0x2E8A,
    product_id: 0x000C,
    device_version: 0x0103,
    class: 0,
    subclass: 0,
    protocol: 0,
    speed: Some(
        Full,
    ),
    manufacturer_string: Some(
        "(Standard USB Host Controller)",
    ),
    product_string: None,
    serial_number: Some(
        "E66038B71354762F",
    ),
    instance_id: "USB\\VID_2E8A&PID_000C\\E66038B71354762F",
    parent_instance_id: "USB\\ROOT_HUB30\\4&24054718&0&0",
    port_number: 3,
    driver: Some(
        "usbccgp",
    ),
}

No product string, manufacturer string is wrong, driver is wrong.

it's a composite device, and it applies the winusb descriptor to one function only, because it has another function which is standard CDC-ACM and it wants windows to use the standard cdc-acm driver for it so it shows up as a COM port.

Example 2: I'm writing a debug probe firmware from scratch. I have both a bootloader and an application firmware.

The bootloader puts the winusb descriptors on the entire device . It shows up as this:

DeviceInfo {
    bus_number: 0,
    device_address: 2,
    vendor_id: 0xC0DE,
    product_id: 0xCAFE,
    device_version: 0x0010,
    class: 0,
    subclass: 0,
    protocol: 0,
    speed: Some(
        Full,
    ),
    manufacturer_string: Some(
        "WinUsb Device",
    ),
    product_string: Some(
        "Embassy Probe bootloader",
    ),
    serial_number: Some(
        "796F070110134753384C4E00",
    ),
    instance_id: "USB\\VID_C0DE&PID_CAFE\\796F070110134753384C4E00",
    parent_instance_id: "USB\\ROOT_HUB30\\4&24054718&0&0",
    port_number: 2,
    driver: Some(
        "WINUSB",
    ),
}

Product and driver are fine, manufacturer is still wrong.

The application is composite, and puts the winusb descriptors on the function (because I'll likely add CDC-ACM in the future too), and shows up the same as picoprobe: wrong manufacturer, product string, driver.

DeviceInfo {
    bus_number: 0,
    device_address: 2,
    vendor_id: 0xC0DE,
    product_id: 0xCAFE,
    device_version: 0x0010,
    class: 239,
    subclass: 2,
    protocol: 1,
    speed: Some(
        Full,
    ),
    manufacturer_string: Some(
        "(Standard USB Host Controller)",
    ),
    product_string: None,
    serial_number: Some(
        "796F070110134753384C4E00",
    ),
    instance_id: "USB\\VID_C0DE&PID_CAFE\\796F070110134753384C4E00",
    parent_instance_id: "USB\\ROOT_HUB30\\4&24054718&0&0",
    port_number: 2,
    driver: Some(
        "usbccgp",
    ),
}

So it seems something about composite devices is strange that confuses nusb? seems Windows treats them almost like separate devices. For the non-composite case, however, the manufacturer_string is still wrong.

also interesting: For the eprobe bootloader, Device Manager does show the product string as the device name. However, for picoprobe and eprobe application, it shows the interface string instead!

Dirbaio added a commit to Dirbaio/probe-rs that referenced this issue Jan 8, 2024
Do not ignore devices without product string, still try to check whether they have an
interface with "CMSIS-DAP" in the name.

It seems the root cause is a `nusb` issue (kevinmehall/nusb#24), which makes
`product_string()` always return None on Windows. Even when that's fixed, I
still think it's good to not bail out if there's no product string, it can help with compatibility
with weird probes.
@kevinmehall
Copy link
Owner

Related: #22 (comment)

Yeah, I recently discovered that the device properties DEVPKEY_Device_FriendlyName and DEVPKEY_Device_Manufacturer that nusb is returning come from the .inf file or the driver, not directly from the string descriptors. Zadig sets these properties using the string descriptors, but other installation methods and drivers set them to various other things.

As far as I can tell, Windows isn't reading these string descriptors at enumeration time, so if we want to retain the property that DeviceInfo doesn't perform IO, I think these will have to return None on Windows if we can't be sure the values we're getting from this API are correct. I'd document that the DeviceInfo methods return cached values if and only if available, and cross-platform apps should instead open() the device and use get_string_descriptor to request it from the device, similar to what libusb requires. I'd also add Device methods to get the device descriptor iManufacturer / iProduct fields needed for this.

Note that open() on Windows doesn't actually do much, since its openable device nodes actually correspond to interfaces. Windows may also require claiming an interface to wake the device from suspend to be able to read descriptors, but it seems like the kinds of devices you'd typically want to use with nusb don't autosuspend anyway.

You can try out the string_descriptors example app to see how the device properties and the string descriptor read compare for your devices.

I am hopeful that there is a non-blocking way to get the serial string (with correct casing) on Windows, so if that works out, that one can stay. A lot of use cases locate a device by VID + PID + Serial, so that is nice to have when listing devices.

@Dirbaio
Copy link
Contributor Author

Dirbaio commented Jan 9, 2024

#27 fixes the product string, seems Windows does cache it.

I'd document that the DeviceInfo methods return cached values if and only if available, and cross-platform apps should instead open() the device and use get_string_descriptor to request it from the device

It'd be helpful if there was a built-in way to do "just give me the manufacturer string, do IO if needed".

If you tell the user "to be cross-platform, open() and get_string_descriptor()", they'll do that unconditionally which will do IO unnecessarily on Linux. To do it right, the user would have to do "check for none, then do open, get_string_descriptor" manually, but even then that's tricky because None on linux means "no manufacturer string", on Windows it means "device might or might not have a manufacturer string, it's just not cached", so you don't want to do open, get_string_descriptor on Linux but you do on Windows.

@kevinmehall
Copy link
Owner

Do you have a concrete use case for manufacturer string?

I'm inclined to change (and document) manufacturer_string() to always return None on Windows, since it's usually wrong right now.

I want to fix this for the sake of completeness, but now that you've fixed the product string, manufacturer seems like the least useful of the three device strings, so may not be a priority right now. I'd assume that lsusb-like apps are probably fine opening the device, and are going to want to read the whole config descriptor and request arbitrary strings anyway. It seems like many apps look up the idVendor in the usb.ids list instead of using the string descriptor as well.

I get your point that if you want the value, you want it the most efficient way if possible, but otherwise you want to do what it takes to get it. I'd accept a PR adding get_manufacturer_string that returns Result and does IO on Windows, or falls back to manufacturer_string on supported OSs if someone comes across a need for this.

@Dirbaio
Copy link
Contributor Author

Dirbaio commented Jan 15, 2024

Do you have a concrete use case for manufacturer string?

not really, no. probe-rs doesn't use it. Perhaps it's useful to communicate to the user in some cases, but the product name is usually enough.

so no objection to always returning None from me 👍

@Dirbaio Dirbaio closed this as completed Jan 15, 2024
tuna-f1sh pushed a commit to tuna-f1sh/nusb that referenced this issue Sep 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants