* [PATCH v3] media: uvcvideo: Handle payloads starting in the middle of a bulk URB
[not found] <20261025170220.42789-1-walid.badar@gmail.com>
@ 2026-11-02 22:03 ` Muhammad Waleed Badar
2026-11-02 22:31 ` Laurent Pinchart
0 siblings, 1 reply; 2+ messages in thread
From: Muhammad Waleed Badar @ 2026-11-02 22:03 UTC (permalink / raw)
To: linux-media, Laurent Pinchart, Hans de Goede
Cc: Muhammad Waleed Badar, Ricardo Ribalda, Mauro Carvalho Chehab,
linux-kernel
The bulk decoder assumes that every payload starts at the beginning of
a URB. It detects the end of a payload either by a short URB or by the
accumulated payload size reaching dwMaxPayloadTransferSize, and then
decodes the start of the next URB as a new payload header.
The Realtek USB Camera (0bda:579f) integrated in Razer Blade laptops
sends payloads of dwMaxPayloadTransferSize bytes back to back without
aligning them to URB boundaries, which is not compliant with the UVC
specification. When a frame spans more than one payload, the next
payload header lands in the middle of a URB. The driver then copies
that header and the data that follows into the current frame, and
parses frame data at the start of the next URB as a header. This shows
up as "Marking buffer as bad (error bit set)" and "Dropping payload
(out of sync)" trace messages, and most frames larger than one payload
are returned empty or truncated. On this camera every MJPEG mode above
1280x720 is affected.
Add a UVC_QUIRK_BULK_UNALIGNED_PAYLOAD quirk for this device. When it
is set and a payload ends in the middle of a URB, split the URB at the
payload boundary and decode the remainder as a new payload, provided
that it looks like a payload header (EOH bit set and a header length
that fits the data and covers the announced PTS and SCR fields).
Otherwise the URB is processed as before. Print a non compliance
warning once when the workaround is used.
The quirk is limited to this device, and the header check is kept as a
safeguard, because other non compliant bulk devices append trailer
data to their payloads, and decoding that data as a payload header
would break them. Devices without the quirk are unaffected. The quirk
can be enabled for other devices with the quirks module parameter.
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=207045
Assisted-by: Claude:claude-opus-5.5
Signed-off-by: Muhammad Waleed Badar <walid.badar@gmail.com>
---
Changes in v3:
- Align the continuation line of the uvc_video_decode_bulk() declaration
with the open parenthesis, as reported by the Media CI checkpatch run.
- Link to v2: https://lore.kernel.org/r/20261025170220.42789-1-walid.badar@gmail.com
Changes in v2:
- Limit the workaround to the Realtek 0bda:579f camera through a new
UVC_QUIRK_BULK_UNALIGNED_PAYLOAD quirk (Hans de Goede).
- Only split a URB when the remainder looks like a valid payload header,
so devices that append trailer data are not affected, and print a
non-compliance warning once (Ricardo Ribalda).
- Link to v1: https://lore.kernel.org/r/20261009165248.56440-1-walid.badar@gmail.com
drivers/media/usb/uvc/uvc_driver.c | 9 +++
drivers/media/usb/uvc/uvc_video.c | 95 +++++++++++++++++++++++++-----
drivers/media/usb/uvc/uvcvideo.h | 2 +
3 files changed, 90 insertions(+), 16 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index e289cc71b..cfdcfd96a 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -2887,6 +2887,15 @@ static const struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
+ /* Realtek USB Camera (Razer Blade) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0bda,
+ .idProduct = 0x579f,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_BULK_UNALIGNED_PAYLOAD) },
/* Sonix Technology Co. Ltd. - 292A IPC AR0330 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index fc3536a43..8a1e4bc77 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1612,27 +1612,20 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb,
}
}
-static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb,
- struct uvc_buffer *buf, struct uvc_buffer *meta_buf)
+static void uvc_video_decode_bulk_chunk(struct uvc_urb *uvc_urb,
+ struct uvc_buffer **bufp,
+ struct uvc_buffer **meta_bufp,
+ u8 *mem, int len, bool short_urb)
{
- struct urb *urb = uvc_urb->urb;
struct uvc_streaming *stream = uvc_urb->stream;
- u8 *mem;
- int len, ret;
-
- /*
- * Ignore ZLPs if they're not part of a frame, otherwise process them
- * to trigger the end of payload detection.
- */
- if (urb->actual_length == 0 && stream->bulk.header_size == 0)
- return;
+ struct uvc_buffer *buf = *bufp;
+ struct uvc_buffer *meta_buf = *meta_bufp;
+ int ret;
- mem = urb->transfer_buffer;
- len = urb->actual_length;
stream->bulk.payload_size += len;
/*
- * If the URB is the first of its payload, decode and save the
+ * If the chunk is the first of its payload, decode and save the
* header.
*/
if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) {
@@ -1671,7 +1664,7 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb,
* Detect the payload end by a URB smaller than the maximum size (or
* a payload size equal to the maximum) and process the header again.
*/
- if (urb->actual_length < urb->transfer_buffer_length ||
+ if (short_urb ||
stream->bulk.payload_size >= stream->bulk.max_payload_size) {
if (!stream->bulk.skip_payload && buf != NULL) {
uvc_video_decode_end(stream, buf, stream->bulk.header,
@@ -1684,6 +1677,76 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb,
stream->bulk.skip_payload = 0;
stream->bulk.payload_size = 0;
}
+
+ *bufp = buf;
+ *meta_bufp = meta_buf;
+}
+
+/*
+ * Check whether data looks like the start of a payload: a header with the
+ * EOH bit set, and a length that fits in the data and is large enough for
+ * the PTS and SCR fields it announces.
+ */
+static bool uvc_video_bulk_is_header(const u8 *data, int len)
+{
+ unsigned int min_len = 2;
+
+ if (len < 2 || !(data[1] & UVC_STREAM_EOH))
+ return false;
+
+ if (data[1] & UVC_STREAM_PTS)
+ min_len += 4;
+ if (data[1] & UVC_STREAM_SCR)
+ min_len += 6;
+
+ return data[0] >= min_len && data[0] <= len;
+}
+
+static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb,
+ struct uvc_buffer *buf, struct uvc_buffer *meta_buf)
+{
+ struct urb *urb = uvc_urb->urb;
+ struct uvc_streaming *stream = uvc_urb->stream;
+ bool short_urb = urb->actual_length < urb->transfer_buffer_length;
+ u8 *mem;
+ int len;
+
+ /*
+ * Ignore ZLPs if they're not part of a frame, otherwise process them
+ * to trigger the end of payload detection.
+ */
+ if (urb->actual_length == 0 && stream->bulk.header_size == 0)
+ return;
+
+ mem = urb->transfer_buffer;
+ len = urb->actual_length;
+
+ do {
+ int chunk = len;
+ int rem = stream->bulk.max_payload_size -
+ stream->bulk.payload_size;
+
+ /*
+ * Some devices (such as the Realtek 0bda:579f) start the next
+ * payload immediately after the previous one instead of in a
+ * new URB, so a payload boundary can fall in the middle of a
+ * URB. For those, split the URB at the payload boundary if
+ * the data that follows looks like a payload header. Other
+ * devices may append trailer data to a payload, which must
+ * not be decoded as a header, so leave them alone.
+ */
+ if ((stream->dev->quirks & UVC_QUIRK_BULK_UNALIGNED_PAYLOAD) &&
+ len > rem && uvc_video_bulk_is_header(mem + rem, len - rem)) {
+ uvc_warn_once(stream->dev, UVC_WARN_BULK_UNALIGNED,
+ "UVC non compliance - bulk payload starts in the middle of a URB. Enabling workaround.\n");
+ chunk = rem;
+ }
+
+ uvc_video_decode_bulk_chunk(uvc_urb, &buf, &meta_buf, mem,
+ chunk, short_urb && chunk == len);
+ mem += chunk;
+ len -= chunk;
+ } while (len > 0);
}
static void uvc_video_encode_bulk(struct uvc_urb *uvc_urb,
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index b6bcee4a2..59ad516ae 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_BULK_UNALIGNED_PAYLOAD 0x00080000
/* Format flags */
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
@@ -661,6 +662,7 @@ static inline struct uvc_fh *to_uvc_fh(struct file *filp)
#define UVC_WARN_MINMAX 0
#define UVC_WARN_PROBE_DEF 1
#define UVC_WARN_XU_GET_RES 2
+#define UVC_WARN_BULK_UNALIGNED 3
extern unsigned int uvc_clock_param;
extern unsigned int uvc_no_drop_param;
--
2.56.0
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH v3] media: uvcvideo: Handle payloads starting in the middle of a bulk URB
2026-11-02 22:03 ` [PATCH v3] media: uvcvideo: Handle payloads starting in the middle of a bulk URB Muhammad Waleed Badar
@ 2026-11-02 22:31 ` Laurent Pinchart
0 siblings, 0 replies; 2+ messages in thread
From: Laurent Pinchart @ 2026-11-02 22:31 UTC (permalink / raw)
To: Muhammad Waleed Badar
Cc: linux-media, Hans de Goede, Muhammad Waleed Badar,
Ricardo Ribalda, Mauro Carvalho Chehab, linux-kernel
Hello,
This is an automated reply.
You have sent a patch or bug report that appears to have been generated
by an LLM. The commit message, code and/or problem description show
little to no real understanding of the issue and changes being proposed.
Such patches or reports require a large amount of work to handle. They
rely on maintainers to propose - and often even implement - a proper
design. The probability that the author will learn from the experience
to improve their future contributions is close to zero.
As a result, to make good use of maintainer time and keep the kernel
development process sustainable, your contribution has been rejected.
If you believe the change you proposed is important, take time to study
the problem, learn, and develop a solution without AI assistance.
Answering this e-mail or generating a new version of a patch by feeding
this reply to an LLM will only result in your e-mails being ignored.
Please note that if your patch attempted to address an important
problem, maintainers or other contributors can submit and merge a
similar or entirely different fix without crediting you.
Your contributions to the Linux kernel will be welcome if you genuinely
desire to invest your time and learn.
Thank you,
Laurent's mail bot
On Tue, 03 Nov 2026 03:03:36 +0500, Muhammad Waleed Badar <walid.badar@gmail.com> wrote:
> The bulk decoder assumes that every payload starts at the beginning of
> a URB. It detects the end of a payload either by a short URB or by the
> accumulated payload size reaching dwMaxPayloadTransferSize, and then
> decodes the start of the next URB as a new payload header.
>
> The Realtek USB Camera (0bda:579f) integrated in Razer Blade laptops
> sends payloads of dwMaxPayloadTransferSize bytes back to back without
> aligning them to URB boundaries, which is not compliant with the UVC
> specification. When a frame spans more than one payload, the next
> payload header lands in the middle of a URB. The driver then copies
> that header and the data that follows into the current frame, and
> parses frame data at the start of the next URB as a header. This shows
> up as "Marking buffer as bad (error bit set)" and "Dropping payload
> (out of sync)" trace messages, and most frames larger than one payload
> are returned empty or truncated. On this camera every MJPEG mode above
> 1280x720 is affected.
>
> Add a UVC_QUIRK_BULK_UNALIGNED_PAYLOAD quirk for this device. When it
> is set and a payload ends in the middle of a URB, split the URB at the
> payload boundary and decode the remainder as a new payload, provided
> that it looks like a payload header (EOH bit set and a header length
> that fits the data and covers the announced PTS and SCR fields).
> Otherwise the URB is processed as before. Print a non compliance
> warning once when the workaround is used.
>
> The quirk is limited to this device, and the header check is kept as a
> safeguard, because other non compliant bulk devices append trailer
> data to their payloads, and decoding that data as a payload header
> would break them. Devices without the quirk are unaffected. The quirk
> can be enabled for other devices with the quirks module parameter.
>
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=207045
> Assisted-by: Claude:claude-opus-5.5
> Signed-off-by: Muhammad Waleed Badar <walid.badar@gmail.com>
> ---
> Changes in v3:
> - Align the continuation line of the uvc_video_decode_bulk() declaration
> with the open parenthesis, as reported by the Media CI checkpatch run.
> - Link to v2: https://lore.kernel.org/r/20261025170220.42789-1-walid.badar@gmail.com
>
> Changes in v2:
> - Limit the workaround to the Realtek 0bda:579f camera through a new
> UVC_QUIRK_BULK_UNALIGNED_PAYLOAD quirk (Hans de Goede).
> - Only split a URB when the remainder looks like a valid payload header,
> so devices that append trailer data are not affected, and print a
> non-compliance warning once (Ricardo Ribalda).
> - Link to v1: https://lore.kernel.org/r/20261009165248.56440-1-walid.badar@gmail.com
>
> drivers/media/usb/uvc/uvc_driver.c | 9 +++
> drivers/media/usb/uvc/uvc_video.c | 95 +++++++++++++++++++++++++-----
> drivers/media/usb/uvc/uvcvideo.h | 2 +
> 3 files changed, 90 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> index e289cc71b..cfdcfd96a 100644
> --- a/drivers/media/usb/uvc/uvc_driver.c
> +++ b/drivers/media/usb/uvc/uvc_driver.c
> @@ -2887,6 +2887,15 @@ static const struct usb_device_id uvc_ids[] = {
> .bInterfaceSubClass = 1,
> .bInterfaceProtocol = 0,
> .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
> + /* Realtek USB Camera (Razer Blade) */
> + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
> + | USB_DEVICE_ID_MATCH_INT_INFO,
> + .idVendor = 0x0bda,
> + .idProduct = 0x579f,
> + .bInterfaceClass = USB_CLASS_VIDEO,
> + .bInterfaceSubClass = 1,
> + .bInterfaceProtocol = 0,
> + .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_BULK_UNALIGNED_PAYLOAD) },
> /* Sonix Technology Co. Ltd. - 292A IPC AR0330 */
> { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
> | USB_DEVICE_ID_MATCH_INT_INFO,
> diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
> index fc3536a43..8a1e4bc77 100644
> --- a/drivers/media/usb/uvc/uvc_video.c
> +++ b/drivers/media/usb/uvc/uvc_video.c
> @@ -1612,27 +1612,20 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb,
> }
> }
>
> -static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb,
> - struct uvc_buffer *buf, struct uvc_buffer *meta_buf)
> +static void uvc_video_decode_bulk_chunk(struct uvc_urb *uvc_urb,
> + struct uvc_buffer **bufp,
> + struct uvc_buffer **meta_bufp,
> + u8 *mem, int len, bool short_urb)
> {
> - struct urb *urb = uvc_urb->urb;
> struct uvc_streaming *stream = uvc_urb->stream;
> - u8 *mem;
> - int len, ret;
> -
> - /*
> - * Ignore ZLPs if they're not part of a frame, otherwise process them
> - * to trigger the end of payload detection.
> - */
> - if (urb->actual_length == 0 && stream->bulk.header_size == 0)
> - return;
> + struct uvc_buffer *buf = *bufp;
> + struct uvc_buffer *meta_buf = *meta_bufp;
> + int ret;
>
> - mem = urb->transfer_buffer;
> - len = urb->actual_length;
> stream->bulk.payload_size += len;
>
> /*
> - * If the URB is the first of its payload, decode and save the
> + * If the chunk is the first of its payload, decode and save the
> * header.
> */
> if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) {
> @@ -1671,7 +1664,7 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb,
> * Detect the payload end by a URB smaller than the maximum size (or
> * a payload size equal to the maximum) and process the header again.
> */
> - if (urb->actual_length < urb->transfer_buffer_length ||
> + if (short_urb ||
> stream->bulk.payload_size >= stream->bulk.max_payload_size) {
> if (!stream->bulk.skip_payload && buf != NULL) {
> uvc_video_decode_end(stream, buf, stream->bulk.header,
> @@ -1684,6 +1677,76 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb,
> stream->bulk.skip_payload = 0;
> stream->bulk.payload_size = 0;
> }
> +
> + *bufp = buf;
> + *meta_bufp = meta_buf;
> +}
> +
> +/*
> + * Check whether data looks like the start of a payload: a header with the
> + * EOH bit set, and a length that fits in the data and is large enough for
> + * the PTS and SCR fields it announces.
> + */
> +static bool uvc_video_bulk_is_header(const u8 *data, int len)
> +{
> + unsigned int min_len = 2;
> +
> + if (len < 2 || !(data[1] & UVC_STREAM_EOH))
> + return false;
> +
> + if (data[1] & UVC_STREAM_PTS)
> + min_len += 4;
> + if (data[1] & UVC_STREAM_SCR)
> + min_len += 6;
> +
> + return data[0] >= min_len && data[0] <= len;
> +}
> +
> +static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb,
> + struct uvc_buffer *buf, struct uvc_buffer *meta_buf)
> +{
> + struct urb *urb = uvc_urb->urb;
> + struct uvc_streaming *stream = uvc_urb->stream;
> + bool short_urb = urb->actual_length < urb->transfer_buffer_length;
> + u8 *mem;
> + int len;
> +
> + /*
> + * Ignore ZLPs if they're not part of a frame, otherwise process them
> + * to trigger the end of payload detection.
> + */
> + if (urb->actual_length == 0 && stream->bulk.header_size == 0)
> + return;
> +
> + mem = urb->transfer_buffer;
> + len = urb->actual_length;
> +
> + do {
> + int chunk = len;
> + int rem = stream->bulk.max_payload_size -
> + stream->bulk.payload_size;
> +
> + /*
> + * Some devices (such as the Realtek 0bda:579f) start the next
> + * payload immediately after the previous one instead of in a
> + * new URB, so a payload boundary can fall in the middle of a
> + * URB. For those, split the URB at the payload boundary if
> + * the data that follows looks like a payload header. Other
> + * devices may append trailer data to a payload, which must
> + * not be decoded as a header, so leave them alone.
> + */
> + if ((stream->dev->quirks & UVC_QUIRK_BULK_UNALIGNED_PAYLOAD) &&
> + len > rem && uvc_video_bulk_is_header(mem + rem, len - rem)) {
> + uvc_warn_once(stream->dev, UVC_WARN_BULK_UNALIGNED,
> + "UVC non compliance - bulk payload starts in the middle of a URB. Enabling workaround.\n");
> + chunk = rem;
> + }
> +
> + uvc_video_decode_bulk_chunk(uvc_urb, &buf, &meta_buf, mem,
> + chunk, short_urb && chunk == len);
> + mem += chunk;
> + len -= chunk;
> + } while (len > 0);
> }
>
> static void uvc_video_encode_bulk(struct uvc_urb *uvc_urb,
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index b6bcee4a2..59ad516ae 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_BULK_UNALIGNED_PAYLOAD 0x00080000
>
> /* Format flags */
> #define UVC_FMT_FLAG_COMPRESSED 0x00000001
> @@ -661,6 +662,7 @@ static inline struct uvc_fh *to_uvc_fh(struct file *filp)
> #define UVC_WARN_MINMAX 0
> #define UVC_WARN_PROBE_DEF 1
> #define UVC_WARN_XU_GET_RES 2
> +#define UVC_WARN_BULK_UNALIGNED 3
>
> extern unsigned int uvc_clock_param;
> extern unsigned int uvc_no_drop_param;
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-02 22:31 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <20261025170220.42789-1-walid.badar@gmail.com>
2026-11-02 22:03 ` [PATCH v3] media: uvcvideo: Handle payloads starting in the middle of a bulk URB Muhammad Waleed Badar
2026-11-02 22:31 ` Laurent Pinchart
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®