Re: [PATCH v1 2/2] media: uvcvideo: Refactor frame parsing code into a uvc_parse_frame function

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Benoît,

Thank you for the patch.

On Thu, Nov 07, 2024 at 02:22:03PM +0000, Benoit Sevens wrote:
> The ftype value does not change in the while loop so we can check it
> before entering the while loop. Refactoring the frame parsing code into
> a dedicated uvc_parse_frame function makes this more readable.
> 
> Signed-off-by: Benoit Sevens <bsevens@xxxxxxxxxx>
> ---
>  drivers/media/usb/uvc/uvc_driver.c | 228 ++++++++++++++++-------------
>  1 file changed, 123 insertions(+), 105 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> index 13db0026dc1a..99f811ace5d6 100644
> --- a/drivers/media/usb/uvc/uvc_driver.c
> +++ b/drivers/media/usb/uvc/uvc_driver.c
> @@ -220,6 +220,117 @@ static struct uvc_streaming *uvc_stream_new(struct uvc_device *dev,
>   * Descriptors parsing
>   */
>  
> +static int uvc_parse_frame(struct uvc_device *dev, struct uvc_streaming *streaming,
> +		struct uvc_format *format, struct uvc_frame *frame, u32 **intervals,
> +		u8 ftype, int width_multiplier, const unsigned char *buffer, int buflen)
> +{
> +	struct usb_interface *intf = streaming->intf;
> +	struct usb_host_interface *alts = intf->cur_altsetting;

The intf variable is only used here, so you can write

	struct usb_host_interface *alts = streaming->intf->cur_altsetting;

> +	unsigned int i, n;
> +	unsigned int interval;
> +	unsigned int maxIntervalIndex;

We usually sort variables in (more or less) decreasing line length order
if nothing makes that inpractical.

> +
> +	if (ftype != UVC_VS_FRAME_FRAME_BASED)
> +		n = buflen > 25 ? buffer[25] : 0;
> +	else
> +		n = buflen > 21 ? buffer[21] : 0;
> +
> +	n = n ? n : 3;
> +
> +	if (buflen < 26 + 4*n) {
> +		uvc_dbg(dev, DESCR,
> +			"device %d videostreaming interface %d FRAME error\n",
> +			dev->udev->devnum,
> +			alts->desc.bInterfaceNumber);

We can now reflow the code as the indentation level has decreased, for instance

			dev->udev->devnum, alts->desc.bInterfaceNumber);

> +		return -EINVAL;
> +	}
> +
> +	frame->bFrameIndex = buffer[3];
> +	frame->bmCapabilities = buffer[4];
> +	frame->wWidth = get_unaligned_le16(&buffer[5])
> +		      * width_multiplier;
> +	frame->wHeight = get_unaligned_le16(&buffer[7]);
> +	frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
> +	frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
> +	if (ftype != UVC_VS_FRAME_FRAME_BASED) {
> +		frame->dwMaxVideoFrameBufferSize =
> +			get_unaligned_le32(&buffer[17]);
> +		frame->dwDefaultFrameInterval =
> +			get_unaligned_le32(&buffer[21]);
> +		frame->bFrameIntervalType = buffer[25];
> +	} else {
> +		frame->dwMaxVideoFrameBufferSize = 0;
> +		frame->dwDefaultFrameInterval =
> +			get_unaligned_le32(&buffer[17]);
> +		frame->bFrameIntervalType = buffer[21];
> +	}
> +
> +	/*
> +	 * Copy the frame intervals.
> +	 *
> +	 * Some bogus devices report dwMinFrameInterval equal to
> +	 * dwMaxFrameInterval and have dwFrameIntervalStep set to
> +	 * zero. Setting all null intervals to 1 fixes the problem and
> +	 * some other divisions by zero that could happen.
> +	 */
> +	frame->dwFrameInterval = *intervals;
> +
> +	for (i = 0; i < n; ++i) {
> +		interval = get_unaligned_le32(&buffer[26+4*i]);
> +		(*intervals)[i] = interval ? interval : 1;
> +	}
> +
> +	/*
> +	 * Apply more fixes, quirks and workarounds to handle incorrect
> +	 * or broken descriptors.
> +	 */
> +
> +	/*
> +	 * Several UVC chipsets screw up dwMaxVideoFrameBufferSize
> +	 * completely. Observed behaviours range from setting the
> +	 * value to 1.1x the actual frame size to hardwiring the
> +	 * 16 low bits to 0. This results in a higher than necessary
> +	 * memory usage as well as a wrong image size information. For
> +	 * uncompressed formats this can be fixed by computing the
> +	 * value from the frame size.
> +	 */
> +	if (!(format->flags & UVC_FMT_FLAG_COMPRESSED))
> +		frame->dwMaxVideoFrameBufferSize = format->bpp
> +			* frame->wWidth * frame->wHeight / 8;
> +
> +	/*
> +	 * Clamp the default frame interval to the boundaries. A zero
> +	 * bFrameIntervalType value indicates a continuous frame
> +	 * interval range, with dwFrameInterval[0] storing the minimum
> +	 * value and dwFrameInterval[1] storing the maximum value.
> +	 */
> +	maxIntervalIndex = frame->bFrameIntervalType ? n - 1 : 1;
> +	frame->dwDefaultFrameInterval =
> +		clamp(frame->dwDefaultFrameInterval,
> +		      frame->dwFrameInterval[0],
> +		      frame->dwFrameInterval[maxIntervalIndex]);
> +
> +	/*
> +	 * Some devices report frame intervals that are not functional.
> +	 * If the corresponding quirk is set, restrict operation to the
> +	 * first interval only.
> +	 */
> +	if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) {
> +		frame->bFrameIntervalType = 1;
> +		(*intervals)[0] = frame->dwDefaultFrameInterval;
> +	}
> +
> +	uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n",
> +		frame->wWidth, frame->wHeight,
> +		10000000 / frame->dwDefaultFrameInterval,
> +		(100000000 / frame->dwDefaultFrameInterval) % 10);
> +
> +	*intervals += n;
> +
> +	return buffer[0];
> +}
> +
> +
>  static int uvc_parse_format(struct uvc_device *dev,
>  	struct uvc_streaming *streaming, struct uvc_format *format,
>  	struct uvc_frame *frames, u32 **intervals, const unsigned char *buffer,
> @@ -231,9 +342,9 @@ static int uvc_parse_format(struct uvc_device *dev,

While at it, we can also merge the intf and alts variables here too.

With this addressed,

Reviewed-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>

I can make all those small changes locally when applying the patch,
unless you prefer submitting a new version yourself.

>  	struct uvc_frame *frame;
>  	const unsigned char *start = buffer;
>  	unsigned int width_multiplier = 1;
> -	unsigned int interval;
>  	unsigned int i, n;
>  	u8 ftype;
> +	int ret;
>  
>  	format->type = buffer[2];
>  	format->index = buffer[3];
> @@ -371,111 +482,18 @@ static int uvc_parse_format(struct uvc_device *dev,
>  	 * Parse the frame descriptors. Only uncompressed, MJPEG and frame
>  	 * based formats have frame descriptors.
>  	 */
> -	while (ftype && buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
> -	       buffer[2] == ftype) {
> -		unsigned int maxIntervalIndex;
> -
> -		frame = &frames[format->nframes];
> -		if (ftype != UVC_VS_FRAME_FRAME_BASED)
> -			n = buflen > 25 ? buffer[25] : 0;
> -		else
> -			n = buflen > 21 ? buffer[21] : 0;
> -
> -		n = n ? n : 3;
> -
> -		if (buflen < 26 + 4*n) {
> -			uvc_dbg(dev, DESCR,
> -				"device %d videostreaming interface %d FRAME error\n",
> -				dev->udev->devnum,
> -				alts->desc.bInterfaceNumber);
> -			return -EINVAL;
> -		}
> -
> -		frame->bFrameIndex = buffer[3];
> -		frame->bmCapabilities = buffer[4];
> -		frame->wWidth = get_unaligned_le16(&buffer[5])
> -			      * width_multiplier;
> -		frame->wHeight = get_unaligned_le16(&buffer[7]);
> -		frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
> -		frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
> -		if (ftype != UVC_VS_FRAME_FRAME_BASED) {
> -			frame->dwMaxVideoFrameBufferSize =
> -				get_unaligned_le32(&buffer[17]);
> -			frame->dwDefaultFrameInterval =
> -				get_unaligned_le32(&buffer[21]);
> -			frame->bFrameIntervalType = buffer[25];
> -		} else {
> -			frame->dwMaxVideoFrameBufferSize = 0;
> -			frame->dwDefaultFrameInterval =
> -				get_unaligned_le32(&buffer[17]);
> -			frame->bFrameIntervalType = buffer[21];
> -		}
> -
> -		/*
> -		 * Copy the frame intervals.
> -		 *
> -		 * Some bogus devices report dwMinFrameInterval equal to
> -		 * dwMaxFrameInterval and have dwFrameIntervalStep set to
> -		 * zero. Setting all null intervals to 1 fixes the problem and
> -		 * some other divisions by zero that could happen.
> -		 */
> -		frame->dwFrameInterval = *intervals;
> -
> -		for (i = 0; i < n; ++i) {
> -			interval = get_unaligned_le32(&buffer[26+4*i]);
> -			(*intervals)[i] = interval ? interval : 1;
> -		}
> -
> -		/*
> -		 * Apply more fixes, quirks and workarounds to handle incorrect
> -		 * or broken descriptors.
> -		 */
> -
> -		/*
> -		 * Several UVC chipsets screw up dwMaxVideoFrameBufferSize
> -		 * completely. Observed behaviours range from setting the
> -		 * value to 1.1x the actual frame size to hardwiring the
> -		 * 16 low bits to 0. This results in a higher than necessary
> -		 * memory usage as well as a wrong image size information. For
> -		 * uncompressed formats this can be fixed by computing the
> -		 * value from the frame size.
> -		 */
> -		if (!(format->flags & UVC_FMT_FLAG_COMPRESSED))
> -			frame->dwMaxVideoFrameBufferSize = format->bpp
> -				* frame->wWidth * frame->wHeight / 8;
> -
> -		/*
> -		 * Clamp the default frame interval to the boundaries. A zero
> -		 * bFrameIntervalType value indicates a continuous frame
> -		 * interval range, with dwFrameInterval[0] storing the minimum
> -		 * value and dwFrameInterval[1] storing the maximum value.
> -		 */
> -		maxIntervalIndex = frame->bFrameIntervalType ? n - 1 : 1;
> -		frame->dwDefaultFrameInterval =
> -			clamp(frame->dwDefaultFrameInterval,
> -			      frame->dwFrameInterval[0],
> -			      frame->dwFrameInterval[maxIntervalIndex]);
> -
> -		/*
> -		 * Some devices report frame intervals that are not functional.
> -		 * If the corresponding quirk is set, restrict operation to the
> -		 * first interval only.
> -		 */
> -		if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) {
> -			frame->bFrameIntervalType = 1;
> -			(*intervals)[0] = frame->dwDefaultFrameInterval;
> +	if (ftype) {
> +		while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
> +		       buffer[2] == ftype) {
> +			frame = &frames[format->nframes];
> +			ret = uvc_parse_frame(dev, streaming, format, frame, intervals, ftype,
> +					width_multiplier, buffer, buflen);
> +			if (ret < 0)
> +				return ret;
> +			format->nframes++;
> +			buflen -= ret;
> +			buffer += ret;
>  		}
> -
> -		uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n",
> -			frame->wWidth, frame->wHeight,
> -			10000000 / frame->dwDefaultFrameInterval,
> -			(100000000 / frame->dwDefaultFrameInterval) % 10);
> -
> -		format->nframes++;
> -		*intervals += n;
> -
> -		buflen -= buffer[0];
> -		buffer += buffer[0];
>  	}
>  
>  	if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&

-- 
Regards,

Laurent Pinchart




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux