Re: [PATCH v5 2/3] media: uvcvideo: add UVC_QUIRK_CTRL_THROTTLE for fragile firmware

Ricardo Ribalda
2026-04-09T06:45:35Z

Hi JP

On Tue, 31 Mar 2026 at 02:38, JP Hein <jp@jphein.com> wrote:
>
> Some USB webcams have firmware that crashes when it receives rapid
> consecutive UVC control transfers (SET_CUR). The Razer Kiyo Pro
> (1532:0e05) is one such device -- after several hundred rapid control
> changes over a few seconds, the device stops responding entirely,
> triggering an xHCI stop-endpoint command timeout that causes the host
> controller to be declared dead, disconnecting every USB device on the
> bus.

A usb device shall not be able crash the whole USB host. I believe
that you already captured some logs and the USB guys are looking into
it. I'd really like to hear what they have to say after reviewing
them.

>
> The failure is amplified by the standard UVC error-code query: when a
> SET_CUR fails with EPIPE, the driver sends a second transfer (GET_CUR
> on UVC_VC_REQUEST_ERROR_CODE_CONTROL) to read the UVC error code. On a
> device that is already stalling, this second transfer pushes the
> firmware into a full lockup.
>
> Introduce UVC_QUIRK_CTRL_THROTTLE (0x00080000) to address both issues:
>
> - Enforce a minimum 50ms interval between SET_CUR control transfers,
> preventing the rapid-fire pattern that overwhelms the firmware.
> 50ms allows up to 20 control changes per second, which is sufficient
> for interactive slider adjustments while keeping the device stable.
>
> - Skip the UVC_VC_REQUEST_ERROR_CODE_CONTROL query after EPIPE errors
> on devices with this quirk. EPIPE is returned directly without the
> follow-up query that would amplify the failure.
>
> The UVC control path is serialized by ctrl_mutex, so last_ctrl_set_jiffies
> does not require additional locking.
>
> Cc: stable@vger.kernel.org
> Signed-off-by: JP Hein <jp@jphein.com>
> ---
> drivers/media/usb/uvc/uvc_video.c | 32 +++++++++++++++++++++++++++++++
> drivers/media/usb/uvc/uvcvideo.h | 3 +++
> 2 files changed, 35 insertions(+)
>
> diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
> index 40c76c051..9f402f55e 100644
> --- a/drivers/media/usb/uvc/uvc_video.c
> +++ b/drivers/media/usb/uvc/uvc_video.c
> @@ -75,8 +75,30 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
> u8 error;
> u8 tmp;
>

Why don't do you do the rate-limit in __uvc_query_ctrl()?

Are you sure that you only have to limit UVC_SET_CUR?

> + /*
> + * Rate-limit SET_CUR operations for devices with fragile firmware.
> + * The Razer Kiyo Pro locks up under sustained rapid SET_CUR
> + * transfers (hundreds without delay), crashing the xHCI controller.
> + */
> + if (query == UVC_SET_CUR &&
> + (dev->quirks & UVC_QUIRK_CTRL_THROTTLE)) {
> + unsigned long min_interval = msecs_to_jiffies(50);
> +
> + if (dev->last_ctrl_set_jiffies &&
> + time_before(jiffies,
> + dev->last_ctrl_set_jiffies + min_interval)) {
> + unsigned long elapsed = dev->last_ctrl_set_jiffies +
> + min_interval - jiffies;
> + msleep(jiffies_to_msecs(elapsed));
> + }
> + }
> +
> ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
> UVC_CTRL_CONTROL_TIMEOUT);
> +
> + if (query == UVC_SET_CUR &&
> + (dev->quirks & UVC_QUIRK_CTRL_THROTTLE))
> + dev->last_ctrl_set_jiffies = jiffies;
> if (likely(ret == size))
> return 0;
>
> @@ -108,6 +130,16 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
> return ret < 0 ? ret : -EPIPE;
> }
>
> + /*
> + * Skip the error code query for devices that crash under load.
> + * The standard error-code query (GET_CUR on
> + * UVC_VC_REQUEST_ERROR_CODE_CONTROL) sends a second USB transfer to
> + * a device that is already stalling, which can amplify the failure
> + * into a full firmware lockup and xHCI controller death.
> + */
> + if (dev->quirks & UVC_QUIRK_CTRL_THROTTLE)
> + return -EPIPE;
> +
> /* Reuse data[0] to request the error code. */
> tmp = *(u8 *)data;
>
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 8480d65ec..cafc71457 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -81,6 +81,7 @@
> #define UVC_QUIRK_INVALID_DEVICE_SOF 0x00010000
> #define UVC_QUIRK_MJPEG_NO_EOF 0x00020000
> #define UVC_QUIRK_MSXU_META 0x00040000
> +#define UVC_QUIRK_CTRL_THROTTLE 0x00080000
>
> /* Format flags */
> #define UVC_FMT_FLAG_COMPRESSED 0x00000001
> @@ -579,6 +580,8 @@ struct uvc_device {
> struct usb_interface *intf;
> unsigned long warnings;
> u32 quirks;
> + /* Control transfer throttling (UVC_QUIRK_CTRL_THROTTLE) */
> + unsigned long last_ctrl_set_jiffies;
> int intfnum;
> char name[32];
>
> --
> 2.43.0
>

--
Ricardo Ribalda