* [PATCH v2 1/4] usb: gadget: uvc: fix req_payload_size calculation
2026-01-13 9:53 [PATCH v2 0/4] Some fix patches for uvc gadget function Xu Yang
@ 2026-01-13 9:53 ` Xu Yang
2026-01-13 9:53 ` [PATCH v2 2/4] usb: gadget: uvc: fix interval_duration calculation Xu Yang
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Xu Yang @ 2026-01-13 9:53 UTC (permalink / raw)
To: Greg Kroah-Hartman, Michael Grzeschik
Cc: jun.li, imx, linux-usb, linux-kernel, Xu Yang, stable, Frank Li
Current req_payload_size calculation has 2 issue:
(1) When the first time calculate req_payload_size for all the buffers,
reqs_per_frame = 0 will be the divisor of DIV_ROUND_UP(). So
the result is undefined.
This happens because VIDIOC_STREAMON is always executed after
VIDIOC_QBUF. So video->reqs_per_frame will be 0 until VIDIOC_STREAMON
is run.
(2) The buf->req_payload_size may be bigger than max_req_size.
Take YUYV pixel format as example:
If bInterval = 1, video->interval = 666666, high-speed:
video->reqs_per_frame = 666666 / 1250 = 534
720p: buf->req_payload_size = 1843200 / 534 = 3452
1080p: buf->req_payload_size = 4147200 / 534 = 7766
Based on such req_payload_size, the controller can't run normally.
To fix above issue, assign max_req_size to buf->req_payload_size when
video->reqs_per_frame = 0. And limit buf->req_payload_size to
video->req_size if it's large than video->req_size. Since max_req_size
is used at many place, add it to struct uvc_video and set the value once
endpoint is enabled.
Fixes: 98ad03291560 ("usb: gadget: uvc: set req_length based on payload by nreqs instead of req_size")
Cc: stable@vger.kernel.org
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
Changes in v2:
- add R-b tag
---
drivers/usb/gadget/function/f_uvc.c | 4 ++++
drivers/usb/gadget/function/uvc.h | 1 +
drivers/usb/gadget/function/uvc_queue.c | 15 +++++++++++----
drivers/usb/gadget/function/uvc_video.c | 4 +---
4 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index aa6ab666741a9518690995ccdc04e742b4359a0e..a96476507d2fdf4eb0817f3aac09b7ee08df593a 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -362,6 +362,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
return ret;
usb_ep_enable(uvc->video.ep);
+ uvc->video.max_req_size = uvc->video.ep->maxpacket
+ * max_t(unsigned int, uvc->video.ep->maxburst, 1)
+ * (uvc->video.ep->mult);
+
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_STREAMON;
v4l2_event_queue(&uvc->vdev, &v4l2_event);
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 9e79cbe50715791a7f7ddd3bc20e9a28d221db61..b3f88670bff801a43d084646974602e5995bb192 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -117,6 +117,7 @@ struct uvc_video {
/* Requests */
bool is_enabled; /* tracks whether video stream is enabled */
unsigned int req_size;
+ unsigned int max_req_size;
struct list_head ureqs; /* all uvc_requests allocated by uvc_video */
/* USB requests that the video pump thread can encode into */
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 9a1bbd79ff5af945bdd5dcf0c1cb1b6dbdc12a9c..21d80322cb6148ed87eb77f453a1f1644e4923ae 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -86,10 +86,17 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb)
buf->bytesused = 0;
} else {
buf->bytesused = vb2_get_plane_payload(vb, 0);
- buf->req_payload_size =
- DIV_ROUND_UP(buf->bytesused +
- (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN),
- video->reqs_per_frame);
+
+ if (video->reqs_per_frame != 0) {
+ buf->req_payload_size =
+ DIV_ROUND_UP(buf->bytesused +
+ (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN),
+ video->reqs_per_frame);
+ if (buf->req_payload_size > video->req_size)
+ buf->req_payload_size = video->req_size;
+ } else {
+ buf->req_payload_size = video->max_req_size;
+ }
}
return 0;
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index fb77b0b21790178751d36a23f07d5b1efff5c25f..1c0672f707e4e5f29c937a1868f0400aad62e5cb 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -503,9 +503,7 @@ uvc_video_prep_requests(struct uvc_video *video)
unsigned int max_req_size, req_size, header_size;
unsigned int nreq;
- max_req_size = video->ep->maxpacket
- * max_t(unsigned int, video->ep->maxburst, 1)
- * (video->ep->mult);
+ max_req_size = video->max_req_size;
if (!usb_endpoint_xfer_isoc(video->ep->desc)) {
video->req_size = max_req_size;
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v2 2/4] usb: gadget: uvc: fix interval_duration calculation
2026-01-13 9:53 [PATCH v2 0/4] Some fix patches for uvc gadget function Xu Yang
2026-01-13 9:53 ` [PATCH v2 1/4] usb: gadget: uvc: fix req_payload_size calculation Xu Yang
@ 2026-01-13 9:53 ` Xu Yang
2026-01-13 9:53 ` [PATCH v2 3/4] usb: gadget: uvc: return error from uvcg_queue_init() Xu Yang
2026-01-13 9:53 ` [PATCH v2 4/4] usb: gadget: uvc: retry vb2_reqbufs() with vb_vmalloc_memops if use_sg fail Xu Yang
3 siblings, 0 replies; 6+ messages in thread
From: Xu Yang @ 2026-01-13 9:53 UTC (permalink / raw)
To: Greg Kroah-Hartman, Michael Grzeschik
Cc: jun.li, imx, linux-usb, linux-kernel, Xu Yang, stable, Frank Li
According to USB specification:
For full-/high-speed isochronous endpoints, the bInterval value is
used as the exponent for a 2^(bInterval-1) value.
To correctly convert bInterval as interval_duration:
interval_duration = 2^(bInterval-1) * frame_interval
Because the unit of video->interval is 100ns, add a comment info to
make it clear.
Fixes: 48dbe731171e ("usb: gadget: uvc: set req_size and n_requests based on the frame interval")
Cc: stable@vger.kernel.org
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
Changes in v2:
- replace int_pow with left shift
- add R-b tag
---
drivers/usb/gadget/function/uvc.h | 2 +-
drivers/usb/gadget/function/uvc_video.c | 7 +++++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index b3f88670bff801a43d084646974602e5995bb192..676419a049762f9eb59e1ac68b19fa34f153b793 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -107,7 +107,7 @@ struct uvc_video {
unsigned int width;
unsigned int height;
unsigned int imagesize;
- unsigned int interval;
+ unsigned int interval; /* in 100ns units */
struct mutex mutex; /* protects frame parameters */
unsigned int uvc_num_requests;
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 1c0672f707e4e5f29c937a1868f0400aad62e5cb..9dc3af16e2f38957198bf579987f4324fc552c5d 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -499,7 +499,7 @@ uvc_video_prep_requests(struct uvc_video *video)
{
struct uvc_device *uvc = container_of(video, struct uvc_device, video);
struct usb_composite_dev *cdev = uvc->func.config->cdev;
- unsigned int interval_duration = video->ep->desc->bInterval * 1250;
+ unsigned int interval_duration;
unsigned int max_req_size, req_size, header_size;
unsigned int nreq;
@@ -513,8 +513,11 @@ uvc_video_prep_requests(struct uvc_video *video)
return;
}
+ interval_duration = 2 << (video->ep->desc->bInterval - 1);
if (cdev->gadget->speed < USB_SPEED_HIGH)
- interval_duration = video->ep->desc->bInterval * 10000;
+ interval_duration *= 10000;
+ else
+ interval_duration *= 1250;
nreq = DIV_ROUND_UP(video->interval, interval_duration);
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v2 3/4] usb: gadget: uvc: return error from uvcg_queue_init()
2026-01-13 9:53 [PATCH v2 0/4] Some fix patches for uvc gadget function Xu Yang
2026-01-13 9:53 ` [PATCH v2 1/4] usb: gadget: uvc: fix req_payload_size calculation Xu Yang
2026-01-13 9:53 ` [PATCH v2 2/4] usb: gadget: uvc: fix interval_duration calculation Xu Yang
@ 2026-01-13 9:53 ` Xu Yang
2026-01-13 9:53 ` [PATCH v2 4/4] usb: gadget: uvc: retry vb2_reqbufs() with vb_vmalloc_memops if use_sg fail Xu Yang
3 siblings, 0 replies; 6+ messages in thread
From: Xu Yang @ 2026-01-13 9:53 UTC (permalink / raw)
To: Greg Kroah-Hartman, Michael Grzeschik
Cc: jun.li, imx, linux-usb, linux-kernel, Xu Yang
uvcg_queue_init() may fail, but its return value is currently ignored.
Propagate the error code from uvcg_queue_init() to correctly report
initialization failures.
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
Changes in v2:
- improve commit message
---
drivers/usb/gadget/function/uvc_video.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 9dc3af16e2f38957198bf579987f4324fc552c5d..f568dee08b3b7273be124a673682c02d20f7c77b 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -838,7 +838,6 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
video->interval = 666666;
/* Initialize the video buffers queue. */
- uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent,
+ return uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent,
V4L2_BUF_TYPE_VIDEO_OUTPUT, &video->mutex);
- return 0;
}
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 4/4] usb: gadget: uvc: retry vb2_reqbufs() with vb_vmalloc_memops if use_sg fail
2026-01-13 9:53 [PATCH v2 0/4] Some fix patches for uvc gadget function Xu Yang
` (2 preceding siblings ...)
2026-01-13 9:53 ` [PATCH v2 3/4] usb: gadget: uvc: return error from uvcg_queue_init() Xu Yang
@ 2026-01-13 9:53 ` Xu Yang
2026-01-13 15:44 ` Frank Li
3 siblings, 1 reply; 6+ messages in thread
From: Xu Yang @ 2026-01-13 9:53 UTC (permalink / raw)
To: Greg Kroah-Hartman, Michael Grzeschik
Cc: jun.li, imx, linux-usb, linux-kernel, Xu Yang
Based on the reality[1][2] that vb2_dma_sg_alloc() can't alloc buffer with
device DMA limits, those device will always get below error: "swiotlb
buffer is full (sz: 393216 bytes), total 65536 (slots), used 2358 (slots)"
and the uvc gadget function can't work at all.
The videobuf2-dma-sg.c driver doesn't has a formal improve about this issue
till now. For UVC gadget, the videobuf2 subsystem doesn't do dma_map() on
vmalloc returned big buffer when allocate the video buffers, however, it do
it for dma_sg returned buffer. So the issue happens for vb2_dma_sg_alloc().
To workaround the issue, lets retry vb2_reqbufs() with
vb_vmalloc_memops if it fails to allocate buffer with vb2_dma_sg_memops.
If use vmalloced buffer, UVC gadget will allocate some small buffers for
each usb_request to do dma transfer, then uvc driver will memcopy data
from big buffer to small buffer.
Link[1]: https://lore.kernel.org/linux-media/20230828075420.2009568-1-anle.pan@nxp.com/
Link[2]: https://lore.kernel.org/linux-media/20230914145812.12851-1-hui.fang@nxp.com/
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
Changes in v2:
- add more information in commit message
---
drivers/usb/gadget/function/uvc_queue.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 21d80322cb6148ed87eb77f453a1f1644e4923ae..586e5524c171f115d98af5dda43fb800466f46d2 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -182,7 +182,15 @@ int uvcg_alloc_buffers(struct uvc_video_queue *queue,
{
int ret;
+retry:
ret = vb2_reqbufs(&queue->queue, rb);
+ if (ret < 0 && queue->use_sg) {
+ uvc_trace(UVC_TRACE_IOCTL,
+ "failed to alloc buffer with sg enabled, try non-sg mode\n");
+ queue->use_sg = 0;
+ queue->queue.mem_ops = &vb2_vmalloc_memops;
+ goto retry;
+ }
return ret ? ret : rb->count;
}
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH v2 4/4] usb: gadget: uvc: retry vb2_reqbufs() with vb_vmalloc_memops if use_sg fail
2026-01-13 9:53 ` [PATCH v2 4/4] usb: gadget: uvc: retry vb2_reqbufs() with vb_vmalloc_memops if use_sg fail Xu Yang
@ 2026-01-13 15:44 ` Frank Li
0 siblings, 0 replies; 6+ messages in thread
From: Frank Li @ 2026-01-13 15:44 UTC (permalink / raw)
To: Xu Yang
Cc: Greg Kroah-Hartman, Michael Grzeschik, jun.li, imx, linux-usb,
linux-kernel
On Tue, Jan 13, 2026 at 05:53:10PM +0800, Xu Yang wrote:
> Based on the reality[1][2] that vb2_dma_sg_alloc() can't alloc buffer with
> device DMA limits, those device will always get below error: "swiotlb
> buffer is full (sz: 393216 bytes), total 65536 (slots), used 2358 (slots)"
> and the uvc gadget function can't work at all.
>
> The videobuf2-dma-sg.c driver doesn't has a formal improve about this issue
> till now. For UVC gadget, the videobuf2 subsystem doesn't do dma_map() on
> vmalloc returned big buffer when allocate the video buffers, however, it do
> it for dma_sg returned buffer. So the issue happens for vb2_dma_sg_alloc().
>
> To workaround the issue, lets retry vb2_reqbufs() with
> vb_vmalloc_memops if it fails to allocate buffer with vb2_dma_sg_memops.
> If use vmalloced buffer, UVC gadget will allocate some small buffers for
> each usb_request to do dma transfer, then uvc driver will memcopy data
> from big buffer to small buffer.
>
> Link[1]: https://lore.kernel.org/linux-media/20230828075420.2009568-1-anle.pan@nxp.com/
> Link[2]: https://lore.kernel.org/linux-media/20230914145812.12851-1-hui.fang@nxp.com/
> Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
> ---
> Changes in v2:
> - add more information in commit message
> ---
> drivers/usb/gadget/function/uvc_queue.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
> index 21d80322cb6148ed87eb77f453a1f1644e4923ae..586e5524c171f115d98af5dda43fb800466f46d2 100644
> --- a/drivers/usb/gadget/function/uvc_queue.c
> +++ b/drivers/usb/gadget/function/uvc_queue.c
> @@ -182,7 +182,15 @@ int uvcg_alloc_buffers(struct uvc_video_queue *queue,
> {
> int ret;
>
> +retry:
> ret = vb2_reqbufs(&queue->queue, rb);
> + if (ret < 0 && queue->use_sg) {
> + uvc_trace(UVC_TRACE_IOCTL,
> + "failed to alloc buffer with sg enabled, try non-sg mode\n");
> + queue->use_sg = 0;
> + queue->queue.mem_ops = &vb2_vmalloc_memops;
> + goto retry;
> + }
>
> return ret ? ret : rb->count;
> }
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 6+ messages in thread