Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

usb: gadget: uvc: fix req_payload_size calculation

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>
Link: https://patch.msgid.link/20260113-uvc-gadget-fix-patch-v2-1-62950ef5bcb5@nxp.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Xu Yang and committed by
Greg Kroah-Hartman
2edc1acb 42c85d89

+17 -7
+4
drivers/usb/gadget/function/f_uvc.c
··· 362 362 return ret; 363 363 usb_ep_enable(uvc->video.ep); 364 364 365 + uvc->video.max_req_size = uvc->video.ep->maxpacket 366 + * max_t(unsigned int, uvc->video.ep->maxburst, 1) 367 + * (uvc->video.ep->mult); 368 + 365 369 memset(&v4l2_event, 0, sizeof(v4l2_event)); 366 370 v4l2_event.type = UVC_EVENT_STREAMON; 367 371 v4l2_event_queue(&uvc->vdev, &v4l2_event);
+1
drivers/usb/gadget/function/uvc.h
··· 117 117 /* Requests */ 118 118 bool is_enabled; /* tracks whether video stream is enabled */ 119 119 unsigned int req_size; 120 + unsigned int max_req_size; 120 121 struct list_head ureqs; /* all uvc_requests allocated by uvc_video */ 121 122 122 123 /* USB requests that the video pump thread can encode into */
+11 -4
drivers/usb/gadget/function/uvc_queue.c
··· 86 86 buf->bytesused = 0; 87 87 } else { 88 88 buf->bytesused = vb2_get_plane_payload(vb, 0); 89 - buf->req_payload_size = 90 - DIV_ROUND_UP(buf->bytesused + 91 - (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN), 92 - video->reqs_per_frame); 89 + 90 + if (video->reqs_per_frame != 0) { 91 + buf->req_payload_size = 92 + DIV_ROUND_UP(buf->bytesused + 93 + (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN), 94 + video->reqs_per_frame); 95 + if (buf->req_payload_size > video->req_size) 96 + buf->req_payload_size = video->req_size; 97 + } else { 98 + buf->req_payload_size = video->max_req_size; 99 + } 93 100 } 94 101 95 102 return 0;
+1 -3
drivers/usb/gadget/function/uvc_video.c
··· 503 503 unsigned int max_req_size, req_size, header_size; 504 504 unsigned int nreq; 505 505 506 - max_req_size = video->ep->maxpacket 507 - * max_t(unsigned int, video->ep->maxburst, 1) 508 - * (video->ep->mult); 506 + max_req_size = video->max_req_size; 509 507 510 508 if (!usb_endpoint_xfer_isoc(video->ep->desc)) { 511 509 video->req_size = max_req_size;