git: 33fd5d168d32 - main - uvideo: do not reject unsupported pixel formats

Adrian Chadd <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a78b278.3b176.4fdc42cf__15335.6410841056$1786295006$gmane$org@gitrepo.freebsd.org>
The branch main has been updated by adrian:

URL: https://cgit.FreeBSD.org/src/commit/?id=33fd5d168d329f861e679a50c4b2431334bfcaf7

commit 33fd5d168d329f861e679a50c4b2431334bfcaf7
Author:     Abdelkader Boudih <[email protected]>
AuthorDate: 2026-08-09 16:59:44 +0000
Commit:     Adrian Chadd <[email protected]>
CommitDate: 2026-08-09 16:59:46 +0000

    uvideo: do not reject unsupported pixel formats
    
    Reviewed by:    adrian, bapt
    Differential Revision:  https://reviews.freebsd.org/D58503
---
 sys/dev/usb/video/uvideo.c | 90 ++++++++++++++++++++++++++++++++++------------
 1 file changed, 67 insertions(+), 23 deletions(-)

diff --git a/sys/dev/usb/video/uvideo.c b/sys/dev/usb/video/uvideo.c
index 1e19ab42d869..e682f0ae6952 100644
--- a/sys/dev/usb/video/uvideo.c
+++ b/sys/dev/usb/video/uvideo.c
@@ -150,6 +150,9 @@ static int	uvideo_hw_querycap(device_t, struct video_caps *);
 static int	uvideo_hw_enum_format(device_t, uint32_t, struct video_format *);
 static int	uvideo_hw_get_format(device_t, struct video_format *);
 static int	uvideo_hw_try_format(device_t, struct video_format *);
+static int	uvideo_select_fmtgrp(struct uvideo_softc *, uint32_t);
+static uint32_t	uvideo_bytesperline(struct uvideo_format_group *, uint32_t);
+static uint32_t	uvideo_frame_sizeimage(struct usb_video_frame_desc *);
 static int	uvideo_hw_set_format(device_t, const struct video_format *);
 static int	uvideo_hw_enum_framesizes(device_t, struct video_frmsizeenum *);
 static int	uvideo_hw_enum_frameintervals(device_t,
@@ -2754,6 +2757,8 @@ uvideo_hw_get_format(device_t dev, struct video_format *fmt)
 	if (frame != NULL) {
 		fmt->width = UGETW(UVIDEO_FRAME_FIELD(frame, wWidth));
 		fmt->height = UGETW(UVIDEO_FRAME_FIELD(frame, wHeight));
+		fmt->bytesperline = uvideo_bytesperline(sc->sc_fmtgrp_cur,
+		    fmt->width);
 	}
 	fmt->sizeimage = UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize);
 
@@ -2766,31 +2771,78 @@ uvideo_hw_get_format(device_t dev, struct video_format *fmt)
 	return (0);
 }
 
+/*
+ * VIDIOC_TRY_FMT and VIDIOC_S_FMT must not fail on an unsupported
+ * pixel format; they substitute one the device does support.
+ */
+static int
+uvideo_select_fmtgrp(struct uvideo_softc *sc, uint32_t pixelformat)
+{
+	int i;
+
+	for (i = 0; i < sc->sc_fmtgrp_num; i++) {
+		if (pixelformat == sc->sc_fmtgrp[i].pixelformat)
+			return (i);
+	}
+	if (sc->sc_fmtgrp_cur != NULL)
+		return (sc->sc_fmtgrp_cur - sc->sc_fmtgrp);
+	return (0);
+}
+
+static uint32_t
+uvideo_bytesperline(struct uvideo_format_group *fmtgrp, uint32_t width)
+{
+	uint8_t bpp;
+
+	switch (fmtgrp->format->bDescriptorSubtype) {
+	case UDESCSUB_VS_FORMAT_UNCOMPRESSED:
+		bpp = fmtgrp->format->u.uc.bBitsPerPixel;
+		break;
+	case UDESCSUB_VS_FORMAT_FRAME_BASED:
+		bpp = fmtgrp->format->u.fb.bBitsPerPixel;
+		break;
+	default:
+		return (0);	/* compressed */
+	}
+	return (width * bpp / 8);
+}
+
+static uint32_t
+uvideo_frame_sizeimage(struct usb_video_frame_desc *frame)
+{
+
+	switch (frame->bDescriptorSubtype) {
+	case UDESCSUB_VS_FRAME_UNCOMPRESSED:
+	case UDESCSUB_VS_FRAME_MJPEG:
+		return (UGETDW(frame->u.uc.dwMaxVideoFrameBufferSize));
+	default:
+		return (0);
+	}
+}
+
 static int
 uvideo_hw_try_format(device_t dev, struct video_format *fmt)
 {
 	struct uvideo_softc *sc = device_get_softc(dev);
+	struct usb_video_frame_desc *frame;
 	struct uvideo_res r;
-	int found, i;
-
-	for (found = 0, i = 0; i < sc->sc_fmtgrp_num; i++) {
-		if (fmt->pixelformat == sc->sc_fmtgrp[i].pixelformat) {
-			found = 1;
-			break;
-		}
-	}
-	if (found == 0)
-		return (EINVAL);
+	int i;
 
-	if (sc->sc_fmtgrp[i].frame_num == 0)
+	i = uvideo_select_fmtgrp(sc, fmt->pixelformat);
+	if (i >= sc->sc_fmtgrp_num || sc->sc_fmtgrp[i].frame_num == 0)
 		return (EINVAL);
 
 	uvideo_find_res(sc, i, fmt->width, fmt->height, &r);
+	frame = sc->sc_fmtgrp[i].frame[r.fidx];
 
+	fmt->pixelformat = sc->sc_fmtgrp[i].pixelformat;
 	fmt->width = r.width;
 	fmt->height = r.height;
 	fmt->field = V4L2_FIELD_NONE;
-	fmt->sizeimage = UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize);
+	fmt->bytesperline = uvideo_bytesperline(&sc->sc_fmtgrp[i], r.width);
+	fmt->sizeimage = uvideo_frame_sizeimage(frame);
+	if (fmt->sizeimage == 0)
+		fmt->sizeimage = UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize);
 
 	if (sc->sc_fmtgrp[i].has_colorformat) {
 		fmt->colorspace = sc->sc_fmtgrp[i].colorspace;
@@ -2808,19 +2860,11 @@ uvideo_hw_set_format(device_t dev, const struct video_format *fmt)
 	struct uvideo_format_group *fmtgrp_save;
 	struct usb_video_frame_desc *frame_save;
 	struct uvideo_res r;
-	int found, i;
+	int i;
 	usb_error_t error;
 
-	for (found = 0, i = 0; i < sc->sc_fmtgrp_num; i++) {
-		if (fmt->pixelformat == sc->sc_fmtgrp[i].pixelformat) {
-			found = 1;
-			break;
-		}
-	}
-	if (found == 0)
-		return (EINVAL);
-
-	if (sc->sc_fmtgrp[i].frame_num == 0)
+	i = uvideo_select_fmtgrp(sc, fmt->pixelformat);
+	if (i >= sc->sc_fmtgrp_num || sc->sc_fmtgrp[i].frame_num == 0)
 		return (EINVAL);
 
 	uvideo_find_res(sc, i, fmt->width, fmt->height, &r);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.