Commit 2edc1acb authored by Xu Yang's avatar Xu Yang Committed by Greg Kroah-Hartman
Browse files

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 = 4147200d / 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: 98ad0329 ("usb: gadget: uvc: set req_length based on payload by nreqs instead of req_size")
Cc: stable@vger.kernel.org
Reviewed-by: default avatarFrank Li <Frank.Li@nxp.com>
Signed-off-by: default avatarXu Yang <xu.yang_2@nxp.com>
Link: https://patch.msgid.link/20260113-uvc-gadget-fix-patch-v2-1-62950ef5bcb5@nxp.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 42c85d89
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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 */
+11 −4
Original line number Diff line number Diff line
@@ -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);

		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;
+1 −3
Original line number Diff line number Diff line
@@ -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;