Skip to content

windows: fix winusb_get_device_string failing for strict USB devices#1796

Open
sonatique wants to merge 1 commit intolibusb:masterfrom
sonatique:fix-winusb-get-device-string-fail-issue-1794
Open

windows: fix winusb_get_device_string failing for strict USB devices#1796
sonatique wants to merge 1 commit intolibusb:masterfrom
sonatique:fix-winusb-get-device-string-fail-issue-1794

Conversation

@sonatique
Copy link
Copy Markdown
Member

@sonatique sonatique commented Apr 9, 2026

[EDITED for description of the third issue]
Three bugs in winusb_get_device_string caused libusb_get_device_string
to return empty strings instead of actual device strings on Windows:

  1. The function used wIndex=0 (invalid LANGID) when requesting string
    descriptors via IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION. While
    some devices tolerate LANGID 0, strict USB devices correctly STALL
    the request (Windows error 31). Fix: first fetch string descriptor 0
    to get the device's language table (USB 2.0 section 9.6.7), then use
    the primary LANGID for actual string requests. The LANGID is cached
    per device to avoid redundant IOCTLs.

  2. On IOCTL failure, the function returned 0 instead of a negative
    LIBUSB_ERROR code. The caller in libusb_get_device_string only
    checks for rv < 0, so 0 was treated as success: an empty string
    was permanently cached, and subsequent calls returned 1 (empty
    string + null terminator) without ever retrying. Fix: return
    LIBUSB_ERROR_IO on both error paths.

  3. When a device has no string descriptor of a given type
    (descriptor index is 0 in the device descriptor), the function
    returned 0 instead of an error. This caused an empty string to be
    cached and returned as success, while Linux and Darwin return an
    error in the same situation. Fix: return LIBUSB_ERROR_NOT_FOUND
    for consistency with other backends (using Darwing value which seemed
    more precise than Linux.

Closes #1794

@tormodvolden
Copy link
Copy Markdown
Contributor

I thought libusb_get_device_string() would not create I/O and use cached values from the OS, but here I see a DeviceIoControl() call. Does this call create I/O? Or is libusb_get_device_string() not I/O free on Windows anyway?

@mcuee mcuee added the windows label Apr 9, 2026
@mcuee
Copy link
Copy Markdown
Member

mcuee commented Apr 9, 2026

I thought libusb_get_device_string() would not create I/O and use cached values from the OS, but here I see a DeviceIoControl() call. Does this call create I/O? Or is libusb_get_device_string() not I/O free on Windows anyway?

As per the comments in PR #1532, no better ways for Windows now.

From PR #1532.
https://github.com/libusb/libusb/pull/1532/changes#diff-bd7e9ed3e7d8a4a69d0664da0f6b8dbbff0b2c608d8dd63de02c379c98cf88b4R2201-R2222


/*
 * Backend implementation for libusb_get_device_string().
 * 
 * Windows makes getting the common device strings
 * very difficult.  DEVPKEY_Device_* does not have SerialNumber,
 * and it reports the driver manufacturer, not the device manufacturer.
 * 
 * We could parse the serial number from the DEVICE_ID string:
 * https://learn.microsoft.com/en-us/windows-hardware/drivers/install/device-instance-ids
 * https://learn.microsoft.com/en-us/windows-hardware/drivers/install/standard-usb-identifiers
 * 
 * However, using the dev_id for getting the serial number is 
 * definitely not recommended.
 *
 * The following implementation uses an IOCTL
 * to the parent USB hub to perform the USB control request for the
 * string descriptor without opening the USB device.
 * While we would rather not invoke USB IO, we currently lack a 
 * better option.
 */
static int winusb_get_device_string(libusb_device *dev,
	enum libusb_device_string_type string_type, char *data, int length)
{

@mcuee
Copy link
Copy Markdown
Member

mcuee commented Apr 9, 2026

Not able to recreate the issues for Issue #1794, but at least no regressions now.

PS C:\work\libusb\libusb> .\build\v145\x64\Release-Hotplug-MT\testlibusb.exe
Dev (bus 2, device 12): 1EA7 - 0064 speed: 12M
  Product:                   2.4G Mouse
Dev (bus 2, device 11): 25A7 - FA61 speed: 12M
  Manufacturer:              Compx
  Product:                   2.4G Receiver
Dev (bus 2, device 9): 046D - C548 speed: 12M
  Manufacturer:              Logitech
  Product:                   USB Receiver
Dev (bus 1, device 7): 05E3 - 0747 speed: 5G
Dev (bus 2, device 3): 0408 - 4044 speed: 480M
  Manufacturer:              Quanta
  Product:                   ACER QHD User Facing
Dev (bus 2, device 10): 16C0 - 05DC speed: 1.5M
  Manufacturer:              www.fischl.de
  Product:                   USBasp
Dev (bus 2, device 4): 0BDA - 0129 speed: 480M
Dev (bus 2, device 1): 248A - 5B2F speed: 12M
  Manufacturer:              SSCYPL
  Product:                   Wireless-Receiver
Dev (bus 2, device 2): 1C7A - 0584 speed: 12M
Dev (bus 2, device 5): 8087 - 0036 speed: 12M
Dev (bus 2, device 7): 03EB - 2106 speed: 12M
  Manufacturer:              ATMEL
  Product:                   STK600
Dev (bus 1, device 6): 0BDA - 8156 speed: 5G
Dev (bus 1, device 5): 05E3 - 0625 speed: 10G
Dev (bus 2, device 8): 05E3 - 0610 speed: 480M
Dev (bus 2, device 0): 8086 - 7E7D speed: 10G
Dev (bus 1, device 0): 8086 - 7EC0 speed: 10G
Dev (bus 2, device 6): 05E3 - 0610 speed: 480M
Dev (bus 1, device 8): 05E3 - 0616 speed: 5G

PS C:\work\libusb\libusb> cd ..
PS C:\work\libusb> cd .\libusb_pr1796\

PS C:\work\libusb\libusb_pr1796> .\build\v145\x64\Release-Hotplug-MT\testlibusb.exe
Dev (bus 2, device 12): 1EA7 - 0064 speed: 12M
  Product:                   2.4G Mouse
Dev (bus 2, device 11): 25A7 - FA61 speed: 12M
  Manufacturer:              Compx
  Product:                   2.4G Receiver
Dev (bus 2, device 9): 046D - C548 speed: 12M
  Manufacturer:              Logitech
  Product:                   USB Receiver
Dev (bus 1, device 7): 05E3 - 0747 speed: 5G
Dev (bus 2, device 3): 0408 - 4044 speed: 480M
  Manufacturer:              Quanta
  Product:                   ACER QHD User Facing
Dev (bus 2, device 10): 16C0 - 05DC speed: 1.5M
  Manufacturer:              www.fischl.de
  Product:                   USBasp
Dev (bus 2, device 4): 0BDA - 0129 speed: 480M
Dev (bus 2, device 1): 248A - 5B2F speed: 12M
  Manufacturer:              SSCYPL
  Product:                   Wireless-Receiver
Dev (bus 2, device 2): 1C7A - 0584 speed: 12M
Dev (bus 2, device 5): 8087 - 0036 speed: 12M
Dev (bus 2, device 7): 03EB - 2106 speed: 12M
  Manufacturer:              ATMEL
  Product:                   STK600
Dev (bus 1, device 6): 0BDA - 8156 speed: 5G
Dev (bus 1, device 5): 05E3 - 0625 speed: 10G
Dev (bus 2, device 8): 05E3 - 0610 speed: 480M
Dev (bus 2, device 0): 8086 - 7E7D speed: 10G
Dev (bus 1, device 0): 8086 - 7EC0 speed: 10G
Dev (bus 2, device 6): 05E3 - 0610 speed: 480M
Dev (bus 1, device 8): 05E3 - 0616 speed: 5G

@mcuee
Copy link
Copy Markdown
Member

mcuee commented Apr 9, 2026

Changing my Windows 11 system to Singapore Chinese -- still the same.

PS C:\work\libusb\libusb_pr1796> Get-Culture

LCID             Name             DisplayName
----             ----             -----------
4100             zh-SG            中文(新加坡)

PS C:\work\libusb\libusb_pr1796> .\build\v145\x64\Release-Hotplug-MT\testlibusb.exe
Dev (bus 2, device 12): 1EA7 - 0064 speed: 12M
  Product:                   2.4G Mouse
Dev (bus 2, device 11): 25A7 - FA61 speed: 12M
  Manufacturer:              Compx
  Product:                   2.4G Receiver
Dev (bus 2, device 9): 046D - C548 speed: 12M
  Manufacturer:              Logitech
  Product:                   USB Receiver
Dev (bus 1, device 7): 05E3 - 0747 speed: 5G
Dev (bus 2, device 3): 0408 - 4044 speed: 480M
  Manufacturer:              Quanta
  Product:                   ACER QHD User Facing
Dev (bus 2, device 10): 16C0 - 05DC speed: 1.5M
  Manufacturer:              www.fischl.de
  Product:                   USBasp
Dev (bus 2, device 4): 0BDA - 0129 speed: 480M
Dev (bus 2, device 1): 248A - 5B2F speed: 12M
  Manufacturer:              SSCYPL
  Product:                   Wireless-Receiver
Dev (bus 2, device 2): 1C7A - 0584 speed: 12M
Dev (bus 2, device 5): 8087 - 0036 speed: 12M
Dev (bus 2, device 7): 03EB - 2106 speed: 12M
  Manufacturer:              ATMEL
  Product:                   STK600
Dev (bus 1, device 6): 0BDA - 8156 speed: 5G
Dev (bus 1, device 5): 05E3 - 0625 speed: 10G
Dev (bus 2, device 8): 05E3 - 0610 speed: 480M
Dev (bus 2, device 0): 8086 - 7E7D speed: 10G
Dev (bus 1, device 0): 8086 - 7EC0 speed: 10G
Dev (bus 2, device 6): 05E3 - 0610 speed: 480M
Dev (bus 1, device 8): 05E3 - 0616 speed: 5G

PS C:\work\libusb\libusb_pr1796> cd ..

PS C:\work\libusb> cd libusb

PS C:\work\libusb\libusb> .\build\v145\x64\Release-Hotplug-MT\testlibusb.exe
Dev (bus 2, device 12): 1EA7 - 0064 speed: 12M
  Product:                   2.4G Mouse
Dev (bus 2, device 11): 25A7 - FA61 speed: 12M
  Manufacturer:              Compx
  Product:                   2.4G Receiver
Dev (bus 2, device 9): 046D - C548 speed: 12M
  Manufacturer:              Logitech
  Product:                   USB Receiver
Dev (bus 1, device 7): 05E3 - 0747 speed: 5G
Dev (bus 2, device 3): 0408 - 4044 speed: 480M
  Manufacturer:              Quanta
  Product:                   ACER QHD User Facing
Dev (bus 2, device 10): 16C0 - 05DC speed: 1.5M
  Manufacturer:              www.fischl.de
  Product:                   USBasp
Dev (bus 2, device 4): 0BDA - 0129 speed: 480M
Dev (bus 2, device 1): 248A - 5B2F speed: 12M
  Manufacturer:              SSCYPL
  Product:                   Wireless-Receiver
Dev (bus 2, device 2): 1C7A - 0584 speed: 12M
Dev (bus 2, device 5): 8087 - 0036 speed: 12M
Dev (bus 2, device 7): 03EB - 2106 speed: 12M
  Manufacturer:              ATMEL
  Product:                   STK600
Dev (bus 1, device 6): 0BDA - 8156 speed: 5G
Dev (bus 1, device 5): 05E3 - 0625 speed: 10G
Dev (bus 2, device 8): 05E3 - 0610 speed: 480M
Dev (bus 2, device 0): 8086 - 7E7D speed: 10G
Dev (bus 1, device 0): 8086 - 7EC0 speed: 10G
Dev (bus 2, device 6): 05E3 - 0610 speed: 480M
Dev (bus 1, device 8): 05E3 - 0616 speed: 5G

@mcuee
Copy link
Copy Markdown
Member

mcuee commented Apr 9, 2026

BTW, another interesting finding.

For thebuilt-in USB devices of the Acer Swift Go 14 laptop (2024 model) like the WebCAM, USBview and USBTreeView will not be able to get the string descriptors since they are in Low Power mode.

But all the different versions of libusb testlibusb example will be able to get the string descriptors. Looks like libusb Windows will wake up the device and get the string descriptors. I have tested libusb git, this PR, 1.0.29 release and 1.0.28 release.

Dev (bus 2, device 3): 0408 - 4044 speed: 480M
  Manufacturer:              Quanta
  Product:                   ACER QHD User Facing

@mcuee
Copy link
Copy Markdown
Member

mcuee commented Apr 9, 2026

I will check the behavior of the dual boot Ubuntu 26.04 beta version as well tomorrow.

sd.req.ConnectionIndex = (ULONG)dev->port_number;
sd.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN;
sd.req.SetupPacket.bRequest = LIBUSB_REQUEST_GET_DESCRIPTOR;
sd.req.SetupPacket.wValue = (LIBUSB_DT_STRING << 8) | 0;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

| 0? Is that a typo?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is just a notation to clarify that whatever goes in the LSB is 0, to follow a pattern of a << 8 | b.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed: Writing (LIBUSB_DT_STRING << 8) | 0 makes the structure explicit: we're requesting string descriptor index 0 (the language table).
But it is a matter of preference, we may remove it if this is more confusing than self-explaining.
or maybe I can add: /* index 0 = language table */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding that index 0 = language table comment would be nice.

@tormodvolden
Copy link
Copy Markdown
Contributor

As per the comments in PR #1532, no better ways for Windows now.

From PR #1532. https://github.com/libusb/libusb/pull/1532/changes#diff-bd7e9ed3e7d8a4a69d0664da0f6b8dbbff0b2c608d8dd63de02c379c98cf88b4R2201-R2222

Thanks for the pointer! At least it should work without opening the device (which on Windows means opening an interface).

@sonatique
Copy link
Copy Markdown
Member Author

sonatique commented Apr 9, 2026

@smarvonohr @mcuee @seanm @tormodvolden @Fishi96 : I think I am doing too many things at the same time: I totally forgot we had #1790 pending (and even that I had commented it) when I saw #1794 which was open afterward, and since @mcuee requested my help, I jumped into it and didn't make the connection immediately. I thus try to solve #1794 on my own, resulting in this PR.

And now, when browsing recent PRs because something looked familiar after all, I realized some of the issues were already fixed by #1790, so I could have built upon it rather than creating a whole new PR, this may look a bit rude, sorry.

Anyway, now that we are in this situation, I compared both PR, and found that, in my opinion this one is actually going a little bit further in solving the problems of #1790:

So for these reason I would recommend merging this one and unfortunately dropping #1790, but I am open to any other solution.

@sonatique sonatique force-pushed the fix-winusb-get-device-string-fail-issue-1794 branch from 5b0c1b8 to 186ea85 Compare April 9, 2026 21:57
Three bugs in winusb_get_device_string caused libusb_get_device_string
to return empty strings instead of actual device strings on Windows:

1. The function used wIndex=0 (invalid LANGID) when requesting string
   descriptors via IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION. While
   some devices tolerate LANGID 0, strict USB devices correctly STALL
   the request (Windows error 31). Fix: first fetch string descriptor 0
   to get the device's language table (USB 2.0 section 9.6.7), then use
   the primary LANGID for actual string requests. The LANGID is cached
   per device to avoid redundant IOCTLs.

2. On IOCTL failure, the function returned 0 instead of a negative
   LIBUSB_ERROR code. The caller in libusb_get_device_string only
   checks for rv < 0, so 0 was treated as success: an empty string
   was permanently cached, and subsequent calls returned 1 (empty
   string + null terminator) without ever retrying. Fix: return
   LIBUSB_ERROR_IO on both error paths.

3. When a device has no string descriptor of a given type
   (descriptor index is 0 in the device descriptor), the function
   returned 0 instead of an error. This caused an empty string to be
   cached and returned as success, while Linux and Darwin return an
   error in the same situation. Fix: return LIBUSB_ERROR_NOT_FOUND
   for consistency with other backends (using Darwing value which seemed
   more precise than Linux.

Closes libusb#1794
@sonatique
Copy link
Copy Markdown
Member Author

Note: amended my commit to fix this: #1794 (comment)

@mcuee
Copy link
Copy Markdown
Member

mcuee commented Apr 10, 2026

So for these reason I would recommend merging this one and unfortunately dropping #1790, but I am open to any other solution.

I agree.

@mcuee mcuee added this to the 1.0.30 milestone Apr 10, 2026
@mcuee
Copy link
Copy Markdown
Member

mcuee commented Apr 10, 2026

As Issue #1794 is a regression, I have added #1794 and #1796 to milestone 1.0.30.

@smarvonohr
Copy link
Copy Markdown

smarvonohr commented Apr 10, 2026

Looks good to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New libusb_get_device_string function does not work for all devices

5 participants