mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 18:43:33 +02:00
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: 98ad032915 ("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>
This commit is contained in:
parent
42c85d89b8
commit
2edc1acb1a
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user