Re: [PATCH] usb: core: add USB_QUIRK_CONFIG_DESC_READ_255 for Razer BlackShark V3 Pro
From: Alan Stern
Date: Mon Jul 20 2026 - 12:44:56 EST
On Mon, Jul 20, 2026 at 11:01:26AM -0400, Charles Daoust wrote:
> From: Charles D'Aoust <charles.daoust@xxxxxxxxx>
>
> The Razer BlackShark V3 Pro wireless headset dongle (1532:0577)
> fingerprints its host during the first enumeration after power-on: if
> the first GET_DESCRIPTOR(CONFIGURATION) request asks for 255 bytes, as
> Windows does, the dongle enables its vendor HID command channel; if it
> sees the 9-byte header-only read that Linux issues, it disables that
> channel for the rest of the power session. Audio still works in that
> state, but battery reporting and all vendor commands are silently
> ignored, and nothing short of removing power recovers the device:
> resets, re-enumerations and byte-exact replays of complete Windows
> control sessions were all verified not to help.
>
> Both read lengths are spec-compliant (the device truncates the reply
> to wLength); the firmware was evidently only validated against the
> larger request. The kernel already accommodates this class of
> firmware assumption during enumeration: hub_port_init() reads the
> device descriptor with a 64-byte request because that is what Windows
> does and what many devices expect.
>
> Add USB_QUIRK_CONFIG_DESC_READ_255, which makes usb_get_configuration()
> request 255 bytes for the initial configuration descriptor read rather
> than USB_DT_CONFIG_SIZE, apply it to 1532:0577, and expose it as
> runtime quirk letter 'r'. Devices without the quirk are unaffected.
>
> The trigger was isolated by single-variable bisection on otherwise
> unmodified kernels: with only the widened initial read, the dongle's
> vendor channel comes up enabled on a cold plug with no interface
> drivers bound (bare enumeration only); without it, it never does.
>
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Charles D'Aoust <charles.daoust@xxxxxxxxx>
> ---
Is this not the same as
https://lore.kernel.org/linux-usb/20260717195336.98500-2-nikhilsolanke5@xxxxxxxxx/
?
Alan Stern
> Documentation/admin-guide/kernel-parameters.txt | 3 +++
> drivers/usb/core/config.c | 13 ++++++++++---
> drivers/usb/core/quirks.c | 6 ++++++
> include/linux/usb/quirks.h | 3 +++
> 4 files changed, 22 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index b5493a7f8..1bd138ba6 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -8169,6 +8169,9 @@ Kernel parameters
> q = USB_QUIRK_FORCE_ONE_CONFIG (Device
> claims zero configurations,
> forcing to 1);
> + r = USB_QUIRK_CONFIG_DESC_READ_255 (initial
> + configuration descriptor read must
> + request 255 bytes, as Windows does);
> Example: quirks=0781:5580:bk,0a5c:5834:gij
>
> usbhid.mousepoll=
> diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
> index cd3231d21..c7e21cc76 100644
> --- a/drivers/usb/core/config.c
> +++ b/drivers/usb/core/config.c
> @@ -908,7 +908,7 @@ int usb_get_configuration(struct usb_device *dev)
> {
> struct device *ddev = &dev->dev;
> int ncfg = dev->descriptor.bNumConfigurations;
> - unsigned int cfgno, length;
> + unsigned int cfgno, length, first_read_len;
> unsigned char *bigbuffer;
> struct usb_config_descriptor *desc;
> int result;
> @@ -938,7 +938,14 @@ int usb_get_configuration(struct usb_device *dev)
> if (!dev->rawdescriptors)
> return -ENOMEM;
>
> - desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
> + /*
> + * Some devices (see USB_QUIRK_CONFIG_DESC_READ_255) malfunction unless
> + * the initial configuration descriptor read requests 255 bytes, as
> + * Windows does. Only the header is consumed here either way.
> + */
> + first_read_len = (dev->quirks & USB_QUIRK_CONFIG_DESC_READ_255) ?
> + 255 : USB_DT_CONFIG_SIZE;
> + desc = kmalloc(first_read_len, GFP_KERNEL);
> if (!desc)
> return -ENOMEM;
>
> @@ -946,7 +953,7 @@ int usb_get_configuration(struct usb_device *dev)
> /* We grab just the first descriptor so we know how long
> * the whole configuration is */
> result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
> - desc, USB_DT_CONFIG_SIZE);
> + desc, first_read_len);
> if (result < 0) {
> dev_err(ddev, "unable to read config index %d "
> "descriptor/%s: %d\n", cfgno, "start", result);
> diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
> index 87ee2d938..c8f71b825 100644
> --- a/drivers/usb/core/quirks.c
> +++ b/drivers/usb/core/quirks.c
> @@ -142,6 +142,9 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp)
> break;
> case 'q':
> flags |= USB_QUIRK_FORCE_ONE_CONFIG;
> + break;
> + case 'r':
> + flags |= USB_QUIRK_CONFIG_DESC_READ_255;
> /* Ignore unrecognized flag characters */
> }
> }
> @@ -496,6 +499,9 @@ static const struct usb_device_id usb_quirk_list[] = {
> /* Razer - Razer Blade Keyboard */
> { USB_DEVICE(0x1532, 0x0116), .driver_info =
> USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
> + /* Razer - Razer BlackShark V3 Pro wireless headset dongle */
> + { USB_DEVICE(0x1532, 0x0577), .driver_info =
> + USB_QUIRK_CONFIG_DESC_READ_255 },
> /* Razer - Razer Kiyo Pro Webcam */
> { USB_DEVICE(0x1532, 0x0e05), .driver_info = USB_QUIRK_NO_LPM },
>
> diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
> index b3cc7beab..02de5ed48 100644
> --- a/include/linux/usb/quirks.h
> +++ b/include/linux/usb/quirks.h
> @@ -81,4 +81,7 @@
> /* Device claims zero configurations, forcing to 1 */
> #define USB_QUIRK_FORCE_ONE_CONFIG BIT(18)
>
> +/* initial configuration descriptor read must request 255 bytes (Windows) */
> +#define USB_QUIRK_CONFIG_DESC_READ_255 BIT(19)
> +
> #endif /* __LINUX_USB_QUIRKS_H */
> --
> 2.55.0
>
>