git: 8cdcf4b27c7b - main - uvideo: convert to video(4) framework

Adrian Chadd <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a78b273.3b480.31123aa6__27942.0970635087$1786294937$gmane$org@gitrepo.freebsd.org>
The branch main has been updated by adrian:

URL: https://cgit.FreeBSD.org/src/commit/?id=8cdcf4b27c7b9e0266d3d37c83566016d71582b4

commit 8cdcf4b27c7b9e0266d3d37c83566016d71582b4
Author:     Abdelkader Boudih <[email protected]>
AuthorDate: 2026-08-09 16:53:58 +0000
Commit:     Adrian Chadd <[email protected]>
CommitDate: 2026-08-09 16:54:00 +0000

    uvideo: convert to video(4) framework
    
    Replaced the monolithic cdevsw implementation with the video(4)
    framework.
    
    Reviewed by:    adrian
    Differential Revision:  https://reviews.freebsd.org/D58368
---
 share/man/man4/uvideo.4         |    5 +-
 sys/dev/usb/video/uvideo.c      | 1698 ++++++++++-----------------------------
 sys/modules/usb/uvideo/Makefile |    3 +-
 3 files changed, 419 insertions(+), 1287 deletions(-)

diff --git a/share/man/man4/uvideo.4 b/share/man/man4/uvideo.4
index c7de2d7a0859..254b0429ce0f 100644
--- a/share/man/man4/uvideo.4
+++ b/share/man/man4/uvideo.4
@@ -3,14 +3,15 @@
 .\"
 .\" SPDX-License-Identifier: BSD-2-Clause
 .\"
-.Dd June 17, 2026
+.Dd July 27, 2026
 .Dt UVIDEO 4
 .Os
 .Sh NAME
 .Nm uvideo
 .Nd USB video class webcam and capture driver
 .Sh SYNOPSIS
-.Cd device uvideo
+.Cd "device video"
+.Cd "device uvideo"
 .Sh HARDWARE
 The
 .Nm
diff --git a/sys/dev/usb/video/uvideo.c b/sys/dev/usb/video/uvideo.c
index 6bed4b4acee3..1e19ab42d869 100644
--- a/sys/dev/usb/video/uvideo.c
+++ b/sys/dev/usb/video/uvideo.c
@@ -23,7 +23,6 @@
  * USB Video Class (UVC) driver.
  *
  * Implements standard UVC 1.0/1.1/1.5 devices only.
- * Creates /dev/videoN character devices with V4L2 ioctl interface.
  */
 
 #include <sys/param.h>
@@ -36,25 +35,12 @@
 #include <sys/malloc.h>
 #include <sys/module.h>
 #include <sys/mutex.h>
-#include <sys/poll.h>
 #include <sys/proc.h>
-#include <sys/event.h>
-#include <sys/selinfo.h>
 #include <sys/limits.h>
 #include <sys/sysctl.h>
 #include <sys/uio.h>
 #include <sys/rwlock.h>
 
-#include <vm/vm.h>
-#include <vm/vm_extern.h>
-#include <vm/vm_kern.h>
-#include <vm/vm_object.h>
-#include <vm/vm_page.h>
-#include <vm/vm_pager.h>
-#include <vm/vm_param.h>
-#include <vm/pmap.h>
-#include <vm/vm_map.h>
-
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>
 #include <dev/usb/usbdi_util.h>
@@ -62,6 +48,9 @@
 #include "usbdevs.h"
 
 #include <dev/usb/video/uvideo.h>
+#include <dev/video/video.h>
+
+#include "video_if.h"
 
 #define	USB_DEBUG_VAR uvideo_debug
 #include <dev/usb/usb_debug.h>
@@ -83,11 +72,6 @@ SYSCTL_INT(_hw_usb_uvideo, OID_AUTO, debug, CTLFLAG_RWTUN,
 #define	UE_GET_SIZE(x)	((x) & 0x7FF)
 #define	UE_GET_TRANS(x)	(((x) >> 11) & 0x03)
 
-/* IO_NDELAY from sys/vnode.h - avoid pulling in vnode_if.h dependency */
-#ifndef IO_NDELAY
-#define	IO_NDELAY	0x0004
-#endif
-
 /* Forward declarations */
 struct uvideo_softc;
 
@@ -154,50 +138,32 @@ static usb_error_t	uvideo_vs_alloc_frame(struct uvideo_softc *);
 static void		uvideo_vs_free_frame(struct uvideo_softc *);
 static usb_error_t	uvideo_vs_open(struct uvideo_softc *);
 static void		uvideo_vs_close(struct uvideo_softc *);
-static usb_error_t	uvideo_vs_init(struct uvideo_softc *);
 static void		uvideo_vs_decode_stream_header(struct uvideo_softc *,
 			    uint8_t *, int);
 static void		uvideo_vs_decode_stream_header_isight(
 			    struct uvideo_softc *, uint8_t *, int);
 static void		uvideo_isoc_decode(struct uvideo_softc *,
 			    struct usb_page_cache *, int, int);
-static uint8_t		*uvideo_mmap_getbuf(struct uvideo_softc *);
-static void		uvideo_mmap_queue(struct uvideo_softc *, int, int);
-static void		uvideo_read_frame(struct uvideo_softc *, uint8_t *, int);
-
-static d_open_t		uvideo_cdev_open;
-static d_close_t	uvideo_cdev_close;
-static d_read_t		uvideo_cdev_read;
-static d_ioctl_t	uvideo_cdev_ioctl;
-static d_poll_t		uvideo_cdev_poll;
-static d_kqfilter_t	uvideo_cdev_kqfilter;
-static d_mmap_single_t	uvideo_cdev_mmap_single;
-
-static int	uvideo_querycap(struct uvideo_softc *, struct v4l2_capability *);
-static int	uvideo_enum_fmt(struct uvideo_softc *, struct v4l2_fmtdesc *);
-static int	uvideo_enum_fsizes(struct uvideo_softc *,
-		    struct v4l2_frmsizeenum *);
-static int	uvideo_enum_fivals(struct uvideo_softc *,
-		    struct v4l2_frmivalenum *);
-static int	uvideo_s_fmt(struct uvideo_softc *, struct v4l2_format *);
-static int	uvideo_g_fmt(struct uvideo_softc *, struct v4l2_format *);
-static int	uvideo_s_parm(struct uvideo_softc *, struct v4l2_streamparm *);
-static int	uvideo_g_parm(struct uvideo_softc *, struct v4l2_streamparm *);
-static int	uvideo_enum_input(struct uvideo_softc *, struct v4l2_input *);
-static int	uvideo_s_input(struct uvideo_softc *, int);
-static int	uvideo_g_input(struct uvideo_softc *, int *);
-static int	uvideo_reqbufs(struct uvideo_softc *,
-		    struct v4l2_requestbuffers *);
-static int	uvideo_querybuf(struct uvideo_softc *, struct v4l2_buffer *);
-static int	uvideo_qbuf(struct uvideo_softc *, struct v4l2_buffer *);
-static int	uvideo_dqbuf(struct uvideo_softc *, struct v4l2_buffer *);
-static int	uvideo_streamon(struct uvideo_softc *, int);
-static int	uvideo_streamoff(struct uvideo_softc *, int);
-static int	uvideo_try_fmt(struct uvideo_softc *, struct v4l2_format *);
-static int	uvideo_queryctrl(struct uvideo_softc *,
-		    struct v4l2_queryctrl *);
-static int	uvideo_g_ctrl(struct uvideo_softc *, struct v4l2_control *);
-static int	uvideo_s_ctrl(struct uvideo_softc *, struct v4l2_control *);
+static void		uvideo_frame_done(struct uvideo_softc *);
+
+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_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,
+		    struct video_frmivalenum *);
+static int	uvideo_hw_get_parm(device_t, struct video_fract *);
+static int	uvideo_hw_set_parm(device_t, struct video_fract *);
+static int	uvideo_hw_enum_input(device_t, uint32_t, struct video_input *);
+static int	uvideo_hw_get_input(device_t, uint32_t *);
+static int	uvideo_hw_set_input(device_t, uint32_t);
+static int	uvideo_hw_query_control(device_t, struct video_control_desc *);
+static int	uvideo_hw_get_control(device_t, struct video_control *);
+static int	uvideo_hw_set_control(device_t, const struct video_control *);
+static int	uvideo_hw_start_stream(device_t);
+static void	uvideo_hw_stop_stream(device_t);
 
 /*
  * Transfer configuration indices.
@@ -219,15 +185,13 @@ struct uvideo_softc {
 	device_t		sc_dev;
 	struct usb_device	*sc_udev;
 	struct mtx		sc_mtx;
-	struct cdev		*sc_cdev;
-	int			sc_unit;
+
+	struct video_device	*sc_vd;
+	uint32_t		sc_sequence;
 
 	uint8_t			sc_iface_index;
 	uint8_t			sc_nifaces;
 	int			sc_dying;
-	int			sc_open;
-	uint32_t		sc_priority;
-	struct proc		*sc_owner;
 
 	struct usb_xfer		*sc_xfer[UVIDEO_N_XFER];
 	int			sc_streaming;
@@ -239,17 +203,6 @@ struct uvideo_softc {
 
 	struct uvideo_frame_buffer sc_frame_buffer;
 
-	struct uvideo_mmap	sc_mmap[UVIDEO_MAX_BUFFERS];
-	struct uvideo_mmap	*sc_mmap_cur;
-	uint8_t			*sc_mmap_buffer;
-	size_t			sc_mmap_buffer_size;
-	vm_offset_t		sc_mmap_kva;
-	int			sc_mmap_buffer_idx;
-	q_mmap			sc_mmap_q;
-	size_t			sc_mmap_count;
-	int			sc_mmap_flag;
-	vm_object_t			sc_mmap_object;
-
 	uint8_t			*sc_tmpbuf;
 	int			sc_tmpbuf_size;
 
@@ -278,17 +231,6 @@ struct uvideo_softc {
 	struct uvideo_vs_iface	*sc_vs_cur;
 	struct uvideo_vs_iface	sc_vs_coll[UVIDEO_MAX_VS_NUM];
 
-	int			sc_fsize;
-	uint8_t			*sc_fbuffer;
-	size_t			sc_fbufferlen;
-	int			sc_vidmode;
-#define	VIDMODE_NONE	0
-#define	VIDMODE_MMAP	1
-#define	VIDMODE_READ	2
-	int			sc_frames_ready;
-
-	struct selinfo		sc_selinfo;
-
 	const struct uvideo_quirk *sc_quirk;
 
 	void			(*sc_decode_stream_header)(
@@ -655,6 +597,26 @@ static device_method_t uvideo_methods[] = {
 	DEVMETHOD(device_probe, uvideo_probe),
 	DEVMETHOD(device_attach, uvideo_attach),
 	DEVMETHOD(device_detach, uvideo_detach),
+
+	/* video(4) interface */
+	DEVMETHOD(video_querycap, uvideo_hw_querycap),
+	DEVMETHOD(video_enum_format, uvideo_hw_enum_format),
+	DEVMETHOD(video_get_format, uvideo_hw_get_format),
+	DEVMETHOD(video_try_format, uvideo_hw_try_format),
+	DEVMETHOD(video_set_format, uvideo_hw_set_format),
+	DEVMETHOD(video_enum_framesizes, uvideo_hw_enum_framesizes),
+	DEVMETHOD(video_enum_frameintervals, uvideo_hw_enum_frameintervals),
+	DEVMETHOD(video_get_parm, uvideo_hw_get_parm),
+	DEVMETHOD(video_set_parm, uvideo_hw_set_parm),
+	DEVMETHOD(video_enum_input, uvideo_hw_enum_input),
+	DEVMETHOD(video_get_input, uvideo_hw_get_input),
+	DEVMETHOD(video_set_input, uvideo_hw_set_input),
+	DEVMETHOD(video_query_control, uvideo_hw_query_control),
+	DEVMETHOD(video_get_control, uvideo_hw_get_control),
+	DEVMETHOD(video_set_control, uvideo_hw_set_control),
+	DEVMETHOD(video_start_stream, uvideo_hw_start_stream),
+	DEVMETHOD(video_stop_stream, uvideo_hw_stop_stream),
+
 	DEVMETHOD_END
 };
 
@@ -666,6 +628,7 @@ static driver_t uvideo_driver = {
 
 DRIVER_MODULE(uvideo, uhub, uvideo_driver, NULL, NULL);
 MODULE_DEPEND(uvideo, usb, 1, 1, 1);
+MODULE_DEPEND(uvideo, video, 1, 1, 1);
 MODULE_VERSION(uvideo, 1);
 USB_PNP_HOST_INFO(uvideo_devs);
 
@@ -731,26 +694,6 @@ static const struct usb_config uvideo_bulk_config[1] = {
 	},
 };
 
-/*
- * Character device switch
- */
-static struct cdevsw uvideo_cdevsw = {
-	.d_version = D_VERSION,
-	.d_open = uvideo_cdev_open,
-	.d_close = uvideo_cdev_close,
-	.d_read = uvideo_cdev_read,
-	.d_ioctl = uvideo_cdev_ioctl,
-	.d_poll = uvideo_cdev_poll,
-	.d_kqfilter = uvideo_cdev_kqfilter,
-	.d_mmap_single = uvideo_cdev_mmap_single,
-	.d_name = "video",
-};
-
-/*
- * Unit number allocator
- */
-/* Unit number allocation is handled by scanning for free /dev/videoN names */
-
 /* ---------------------------------------------------------------- */
 /*  Probe / Attach / Detach                                         */
 /* ---------------------------------------------------------------- */
@@ -792,7 +735,6 @@ uvideo_attach(device_t dev)
 	struct usb_config_descriptor *cdesc;
 	struct usb_descriptor *desc;
 	struct usb_interface_assoc_descriptor *iad;
-	struct make_dev_args args;
 	usb_error_t error;
 	int first_iface, nifaces;
 	int i;
@@ -803,7 +745,6 @@ uvideo_attach(device_t dev)
 
 	device_set_usb_desc(dev);
 	mtx_init(&sc->sc_mtx, "uvideo", NULL, MTX_DEF);
-	knlist_init_mtx(&sc->sc_selinfo.si_note, &sc->sc_mtx);
 
 	/* Look up quirks for this device */
 	sc->sc_quirk = uvideo_lookup_quirk(uaa);
@@ -917,35 +858,12 @@ uvideo_attach(device_t dev)
 		}
 	}
 
-	/* Init mmap queue */
-	STAILQ_INIT(&sc->sc_mmap_q);
-	sc->sc_mmap_count = 0;
-	sc->sc_mmap_kva = 0;
-	sc->sc_mmap_object = NULL;
-
-	/* Allocate unit number and create character device */
-	make_dev_args_init(&args);
-	args.mda_devsw = &uvideo_cdevsw;
-	args.mda_uid = UID_ROOT;
-	args.mda_gid = GID_VIDEO;
-	args.mda_mode = 0660;
-	args.mda_si_drv1 = sc;
-	args.mda_flags = MAKEDEV_CHECKNAME;
-
-	sc->sc_unit = -1;
-	for (i = 0; i < 256; i++) {
-		if (make_dev_s(&args, &sc->sc_cdev, "video%d", i) == 0) {
-			sc->sc_unit = i;
-			break;
-		}
-	}
-	if (sc->sc_unit < 0) {
-		device_printf(dev, "failed to create /dev/video device\n");
+	i = video_register(dev, &sc->sc_vd);
+	if (i != 0) {
+		device_printf(dev, "failed to register video device\n");
 		goto detach;
 	}
 
-	device_printf(dev, "UVC camera on /dev/video%d\n", sc->sc_unit);
-
 	return (0);
 
 detach:
@@ -960,29 +878,22 @@ uvideo_detach(device_t dev)
 
 	sc->sc_dying = 1;
 
-	/* Destroy character device */
-	if (sc->sc_cdev != NULL) {
-		destroy_dev(sc->sc_cdev);
-		sc->sc_cdev = NULL;
-	}
+	if (sc->sc_vd != NULL)
+		video_unregister(sc->sc_vd);
 
-	/* Stop streaming if still active (e.g. detached while idle). */
-	mtx_lock(&sc->sc_mtx);
 	if (sc->sc_streaming) {
+		mtx_lock(&sc->sc_mtx);
 		sc->sc_streaming = 0;
 		mtx_unlock(&sc->sc_mtx);
 		uvideo_vs_close(sc);
-	} else
-		mtx_unlock(&sc->sc_mtx);
+	}
+
 
-	/* Free frame buffers */
 	uvideo_vs_free_frame(sc);
 
 	/* Unsetup USB transfers */
 	usbd_transfer_unsetup(sc->sc_xfer, UVIDEO_N_XFER);
 
-	seldrain(&sc->sc_selinfo);
-	knlist_destroy(&sc->sc_selinfo.si_note);
 	mtx_destroy(&sc->sc_mtx);
 
 	return (0);
@@ -1813,7 +1724,8 @@ uvideo_vs_parse_desc_frame_max_rate(struct uvideo_softc *sc,
 		length -= sizeof(uDWord);
 	}
 
-	fbuf_size = UGETDW(UVIDEO_FRAME_FIELD(fd, dwMaxBitRate)) * frame_ival;
+	fbuf_size = (uint64_t)UGETDW(UVIDEO_FRAME_FIELD(fd, dwMaxBitRate)) *
+	    frame_ival;
 	fbuf_size /= 8 * 10000000;
 
 	if (fbuf_size > sc->sc_max_fbuf_size)
@@ -1826,6 +1738,68 @@ uvideo_vs_parse_desc_frame_max_rate(struct uvideo_softc *sc,
 	return (USB_ERR_NORMAL_COMPLETION);
 }
 
+/*
+ * Smallest alt satisfying dwMaxPayloadTransferSize.  A larger one loses the
+ * payload framing.  XXX high speed only: bMaxBurst from the SuperSpeed
+ * endpoint companion descriptor is not accounted for.
+ */
+static void
+uvideo_vs_select_alt(struct uvideo_softc *sc, uint32_t payload)
+{
+	struct uvideo_vs_iface *vs = sc->sc_vs_cur;
+	struct usb_config_descriptor *cdesc;
+	struct usb_descriptor *desc;
+	struct usb_interface_descriptor *id;
+	struct usb_endpoint_descriptor *ed;
+	uint32_t psize, best_psize;
+	int best_alt;
+
+	if (vs->bulk_endpoint || payload == 0)
+		return;
+
+	cdesc = usbd_get_config_descriptor(sc->sc_udev);
+	if (cdesc == NULL)
+		return;
+
+	best_alt = -1;
+	best_psize = 0;
+
+	desc = NULL;
+	id = NULL;
+	while ((desc = usb_desc_foreach(cdesc, desc)) != NULL) {
+		if (desc->bDescriptorType == UDESC_INTERFACE) {
+			id = (struct usb_interface_descriptor *)desc;
+			continue;
+		}
+		if (desc->bDescriptorType != UDESC_ENDPOINT || id == NULL)
+			continue;
+		if (id->bInterfaceNumber != vs->iface_index)
+			continue;
+
+		ed = (struct usb_endpoint_descriptor *)desc;
+		if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
+		    UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS)
+			continue;
+
+		psize = UGETW(ed->wMaxPacketSize);
+		psize = UE_GET_SIZE(psize) * (1 + UE_GET_TRANS(psize));
+		if (psize < payload)
+			continue;
+		if (best_alt < 0 || psize < best_psize) {
+			best_alt = id->bAlternateSetting;
+			best_psize = psize;
+		}
+	}
+
+	if (best_alt >= 0) {
+		DPRINTFN(1, "alt %d psize %u for payload %u (was alt %d "
+		    "psize %u)\n", best_alt, best_psize, payload, vs->curalt,
+		    vs->psize);
+		vs->curalt = best_alt;
+		vs->psize = best_psize;
+	}
+}
+
 static usb_error_t
 uvideo_vs_parse_desc_alt(struct uvideo_softc *sc, int vs_nr, int iface,
     int numalts)
@@ -2262,6 +2236,7 @@ uvideo_vs_alloc_frame(struct uvideo_softc *sc)
 		return (USB_ERR_NOMEM);
 	}
 
+
 	fb->buf = malloc(fb->buf_size, M_USBDEV, M_WAITOK | M_ZERO);
 	if (fb->buf == NULL) {
 		device_printf(sc->sc_dev,
@@ -2291,20 +2266,6 @@ uvideo_vs_free_frame(struct uvideo_softc *sc)
 		free(fb->buf, M_USBDEV);
 		fb->buf = NULL;
 	}
-
-	if (sc->sc_mmap_kva != 0) {
-		vm_map_remove(kernel_map, sc->sc_mmap_kva,
-		    sc->sc_mmap_kva + sc->sc_mmap_buffer_size);
-		sc->sc_mmap_buffer = NULL;
-		sc->sc_mmap_kva = 0;
-		sc->sc_mmap_buffer_size = 0;
-	}
-	sc->sc_mmap_object = NULL;
-
-	while (!STAILQ_EMPTY(&sc->sc_mmap_q))
-		STAILQ_REMOVE_HEAD(&sc->sc_mmap_q, q_frames);
-
-	sc->sc_mmap_count = 0;
 }
 
 static usb_error_t
@@ -2322,6 +2283,9 @@ uvideo_vs_open(struct uvideo_softc *sc)
 			return (error);
 	}
 
+	uvideo_vs_select_alt(sc,
+	    UGETDW(sc->sc_desc_probe.dwMaxPayloadTransferSize));
+
 	/* For bulk endpoints, alt 0 is always used */
 	if (!sc->sc_vs_cur->bulk_endpoint) {
 		/*
@@ -2405,22 +2369,6 @@ uvideo_vs_close(struct uvideo_softc *sc)
 	}
 }
 
-static usb_error_t
-uvideo_vs_init(struct uvideo_softc *sc)
-{
-	usb_error_t error;
-
-	error = uvideo_vs_open(sc);
-	if (error != USB_ERR_NORMAL_COMPLETION)
-		return (USB_ERR_INVAL);
-
-	error = uvideo_vs_alloc_frame(sc);
-	if (error != USB_ERR_NORMAL_COMPLETION)
-		return (USB_ERR_INVAL);
-
-	return (USB_ERR_NORMAL_COMPLETION);
-}
-
 /* ---------------------------------------------------------------- */
 /*  Transfer Callbacks                                              */
 /* ---------------------------------------------------------------- */
@@ -2437,7 +2385,6 @@ uvideo_isoc_decode(struct uvideo_softc *sc, struct usb_page_cache *pc,
 	struct uvideo_frame_buffer *fb = &sc->sc_frame_buffer;
 	uint8_t shdr[2];
 	uint8_t flags;
-	uint8_t *buf;
 	int hdrlen, payload_len;
 
 	if (len < UVIDEO_SH_MIN_LEN)
@@ -2471,16 +2418,6 @@ uvideo_isoc_decode(struct uvideo_softc *sc, struct usb_page_cache *pc,
 		fb->error = 1;
 	}
 
-	/* Get destination buffer */
-	if (sc->sc_mmap_flag) {
-		if (!fb->mmap_q_full) {
-			buf = uvideo_mmap_getbuf(sc);
-			if (buf == NULL)
-				fb->mmap_q_full = 1;
-		}
-	} else
-		buf = fb->buf;
-
 	/* Copy payload directly from USB DMA into frame buffer */
 	payload_len = len - hdrlen;
 	if (payload_len > fb->buf_size - fb->offset) {
@@ -2488,9 +2425,9 @@ uvideo_isoc_decode(struct uvideo_softc *sc, struct usb_page_cache *pc,
 		payload_len = fb->buf_size - fb->offset;
 		fb->error = 1;
 	}
-	if (!fb->mmap_q_full && payload_len > 0) {
+	if (payload_len > 0) {
 		usbd_copy_out(pc, offset + hdrlen,
-		    buf + fb->offset, payload_len);
+		    fb->buf + fb->offset, payload_len);
 		fb->offset += payload_len;
 	}
 
@@ -2503,13 +2440,14 @@ uvideo_isoc_decode(struct uvideo_softc *sc, struct usb_page_cache *pc,
 			fb->error = 1;
 		}
 
-		if (sc->sc_mmap_flag) {
-			if (!fb->mmap_q_full)
-				uvideo_mmap_queue(sc, fb->offset, fb->error);
-		} else if (fb->error) {
-			DPRINTFN(1, "error frame, skipped\n");
+		if (!fb->error) {
+			uvideo_frame_done(sc);
 		} else {
-			uvideo_read_frame(sc, fb->buf, fb->offset);
+			struct video_buf *vb;
+
+			vb = video_buf_acquire(sc->sc_vd);
+			if (vb != NULL)
+				video_buf_error(vb);
 		}
 
 		fb->sample = 0;
@@ -2524,12 +2462,15 @@ uvideo_isoc_callback(struct usb_xfer *xfer, usb_error_t error)
 {
 	struct uvideo_softc *sc = usbd_xfer_softc(xfer);
 	struct usb_page_cache *pc;
+	usb_frcount_t maxframes;
 	int nframes, i, offset, len;
 
+	maxframes = usbd_xfer_max_frames(xfer);
+
 	switch (USB_GET_STATE(xfer)) {
 	case USB_ST_TRANSFERRED:
+		usbd_xfer_status(xfer, NULL, NULL, NULL, &nframes);
 		pc = usbd_xfer_get_frame(xfer, 0);
-		nframes = usbd_xfer_max_frames(xfer);
 		offset = 0;
 		for (i = 0; i < nframes; i++) {
 			len = usbd_xfer_frame_len(xfer, i);
@@ -2540,7 +2481,11 @@ uvideo_isoc_callback(struct usb_xfer *xfer, usb_error_t error)
 		/* FALLTHROUGH */
 	case USB_ST_SETUP:
 tr_setup:
-		nframes = usbd_xfer_max_frames(xfer);
+		nframes = sc->sc_nframes;
+		if (nframes > (int)maxframes)
+			nframes = maxframes;
+		if (nframes < 1)
+			nframes = 1;
 		usbd_xfer_set_frames(xfer, nframes);
 		for (i = 0; i < nframes; i++)
 			usbd_xfer_set_frame_len(xfer, i,
@@ -2600,7 +2545,6 @@ uvideo_vs_decode_stream_header(struct uvideo_softc *sc, uint8_t *frame,
 	struct uvideo_frame_buffer *fb = &sc->sc_frame_buffer;
 	struct usb_video_stream_header *sh;
 	int sample_len;
-	uint8_t *buf;
 
 	if (frame_size < UVIDEO_SH_MIN_LEN)
 		return;
@@ -2634,15 +2578,6 @@ uvideo_vs_decode_stream_header(struct uvideo_softc *sc, uint8_t *frame,
 		fb->error = 1;
 	}
 
-	if (sc->sc_mmap_flag) {
-		if (!fb->mmap_q_full) {
-			buf = uvideo_mmap_getbuf(sc);
-			if (buf == NULL)
-				fb->mmap_q_full = 1;
-		}
-	} else
-		buf = sc->sc_frame_buffer.buf;
-
 	/* Save sample data */
 	sample_len = frame_size - sh->bLength;
 	if (sample_len > fb->buf_size - fb->offset) {
@@ -2650,8 +2585,8 @@ uvideo_vs_decode_stream_header(struct uvideo_softc *sc, uint8_t *frame,
 		sample_len = fb->buf_size - fb->offset;
 		fb->error = 1;
 	}
-	if (!fb->mmap_q_full && sample_len > 0) {
-		bcopy(frame + sh->bLength, buf + fb->offset, sample_len);
+	if (sample_len > 0) {
+		bcopy(frame + sh->bLength, fb->buf + fb->offset, sample_len);
 		fb->offset += sample_len;
 	}
 
@@ -2665,13 +2600,14 @@ uvideo_vs_decode_stream_header(struct uvideo_softc *sc, uint8_t *frame,
 			fb->error = 1;
 		}
 
-		if (sc->sc_mmap_flag) {
-			if (!fb->mmap_q_full)
-				uvideo_mmap_queue(sc, fb->offset, fb->error);
-		} else if (fb->error) {
-			DPRINTFN(1, "error frame, skipped\n");
+		if (!fb->error) {
+			uvideo_frame_done(sc);
 		} else {
-			uvideo_read_frame(sc, fb->buf, fb->offset);
+			struct video_buf *vb;
+
+			vb = video_buf_acquire(sc->sc_vd);
+			if (vb != NULL)
+				video_buf_error(vb);
 		}
 
 		fb->sample = 0;
@@ -2681,25 +2617,12 @@ uvideo_vs_decode_stream_header(struct uvideo_softc *sc, uint8_t *frame,
 	}
 }
 
-/*
- * The iSight first generation device uses a non-standard streaming
- * protocol. The stream header is sent once per image and looks like:
- *
- * uByte header length
- * uByte flags
- * uByte magic1[4] always "11223344"
- * uByte magic2[8] always "deadbeefdeadface"
- * uByte unknown[16]
- *
- * Sometimes the stream header is prefixed by an unknown byte.
- */
 static void
 uvideo_vs_decode_stream_header_isight(struct uvideo_softc *sc,
     uint8_t *frame, int frame_size)
 {
 	struct uvideo_frame_buffer *fb = &sc->sc_frame_buffer;
 	int sample_len, header = 0;
-	uint8_t *buf;
 	uint8_t magic[] = { 0x11, 0x22, 0x33, 0x44, 0xde, 0xad, 0xbe,
 	    0xef, 0xde, 0xad, 0xfa, 0xce };
 
@@ -2714,640 +2637,220 @@ uvideo_vs_decode_stream_header_isight(struct uvideo_softc *sc,
 	}
 
 	if (header) {
-		if (sc->sc_mmap_flag) {
-			if (!fb->mmap_q_full)
-				uvideo_mmap_queue(sc, fb->offset, 0);
-		} else {
-			uvideo_read_frame(sc, fb->buf, fb->offset);
-		}
+		if (fb->offset > 0)
+			uvideo_frame_done(sc);
 		fb->offset = 0;
-		fb->mmap_q_full = 0;
 	} else {
-		if (sc->sc_mmap_flag) {
-			if (!fb->mmap_q_full) {
-				buf = uvideo_mmap_getbuf(sc);
-				if (buf == NULL)
-					fb->mmap_q_full = 1;
-			}
-		} else
-			buf = sc->sc_frame_buffer.buf;
-
-		/* Save sample */
 		sample_len = frame_size;
-		if (!fb->mmap_q_full &&
-		    (fb->offset + sample_len) < fb->buf_size) {
-			bcopy(frame, buf + fb->offset, sample_len);
+		if ((fb->offset + sample_len) < fb->buf_size) {
+			bcopy(frame, fb->buf + fb->offset, sample_len);
 			fb->offset += sample_len;
 		}
 	}
 }
 
-static uint8_t *
-uvideo_mmap_getbuf(struct uvideo_softc *sc)
+static void
+uvideo_frame_done(struct uvideo_softc *sc)
 {
-	int i, idx;
-
-	/*
-	 * Multiple frames per transfer / multiple transfers per frame.
-	 */
-	if (sc->sc_mmap_cur != NULL)
-		return (sc->sc_mmap_cur->buf);
-
-	if (sc->sc_mmap_count == 0 || sc->sc_mmap_buffer == NULL)
-		return (NULL);
-
-	idx = sc->sc_mmap_buffer_idx;
+	struct video_buf *vb;
 
-	/* Find a buffer which is queued and ready */
-	for (i = 0; i < sc->sc_mmap_count; i++) {
-		if (sc->sc_mmap[sc->sc_mmap_buffer_idx].v4l2_buf.flags &
-		    V4L2_BUF_FLAG_QUEUED) {
-			idx = sc->sc_mmap_buffer_idx;
-			if (++sc->sc_mmap_buffer_idx == sc->sc_mmap_count)
-				sc->sc_mmap_buffer_idx = 0;
-			break;
-		}
-		if (++sc->sc_mmap_buffer_idx == sc->sc_mmap_count)
-			sc->sc_mmap_buffer_idx = 0;
-	}
+	vb = video_buf_acquire(sc->sc_vd);
+	if (vb == NULL)
+		return;
 
-	if (i == sc->sc_mmap_count) {
-		DPRINTFN(1, "mmap queue is full!\n");
-		return (NULL);
+	if (video_buf_write(vb, 0, sc->sc_frame_buffer.buf,
+	    sc->sc_frame_buffer.offset) != 0) {
+		video_buf_error(vb);
+		return;
 	}
-
-	sc->sc_mmap_cur = &sc->sc_mmap[idx];
-	return (sc->sc_mmap_cur->buf);
+	video_buf_done(vb, sc->sc_frame_buffer.offset, sc->sc_sequence++);
 }
 
-static void
-uvideo_mmap_queue(struct uvideo_softc *sc, int len, int err)
+static int
+uvideo_hw_querycap(device_t dev, struct video_caps *caps)
 {
+	struct uvideo_softc *sc = device_get_softc(dev);
 
-	if (sc->sc_mmap_cur == NULL)
-		return;
-
-	sc->sc_mmap_cur->v4l2_buf.bytesused = len;
-
-	getmicrouptime(&sc->sc_mmap_cur->v4l2_buf.timestamp);
-	sc->sc_mmap_cur->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TIMESTAMP_MASK;
-	sc->sc_mmap_cur->v4l2_buf.flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-	sc->sc_mmap_cur->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
-	sc->sc_mmap_cur->v4l2_buf.flags |= V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
-	sc->sc_mmap_cur->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TIMECODE;
-
-	sc->sc_mmap_cur->v4l2_buf.flags &= ~V4L2_BUF_FLAG_ERROR;
-	if (err)
-		sc->sc_mmap_cur->v4l2_buf.flags |= V4L2_BUF_FLAG_ERROR;
-
-	sc->sc_mmap_cur->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE;
-	sc->sc_mmap_cur->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED;
-	STAILQ_INSERT_TAIL(&sc->sc_mmap_q, sc->sc_mmap_cur, q_frames);
-	sc->sc_mmap_cur = NULL;
-
-	DPRINTFN(2, "frame queued\n");
+	bzero(caps, sizeof(*caps));
+	strlcpy(caps->driver, "uvideo", sizeof(caps->driver));
+	strlcpy(caps->card, usb_get_product(sc->sc_udev),
+	    sizeof(caps->card));
+	snprintf(caps->bus_info, sizeof(caps->bus_info), "usb-%s",
+	    device_get_nameunit(sc->sc_dev));
+	caps->version = (5 << 16) | (0 << 8) | 0;	/* 5.0.0 */
+	caps->capabilities = VIDEO_CAP_CAPTURE |
+	    VIDEO_CAP_READWRITE | VIDEO_CAP_STREAMING;
 
-	wakeup(sc);
-	selwakeup(&sc->sc_selinfo);
-	KNOTE_LOCKED(&sc->sc_selinfo.si_note, 0);
+	return (0);
 }
 
-static void
-uvideo_read_frame(struct uvideo_softc *sc, uint8_t *buf, int len)
+static int
+uvideo_hw_enum_format(device_t dev, uint32_t index, struct video_format *fmt)
 {
+	struct uvideo_softc *sc = device_get_softc(dev);
+	struct uvideo_format_group *fmtgrp;
+	struct usb_video_frame_desc *frame;
 
-	/*
-	 * In read mode, copy the frame into the upper-layer buffer
-	 * so the USB callback can start assembling the next frame
-	 * without racing with the cdev read.
-	 */
-	if (sc->sc_fbuffer == NULL || len > sc->sc_fbufferlen)
-		return;
-
-	bcopy(buf, sc->sc_fbuffer, len);
-	sc->sc_fsize = len;
-	sc->sc_frames_ready++;
-
-	wakeup(sc);
-	selwakeup(&sc->sc_selinfo);
-	KNOTE_LOCKED(&sc->sc_selinfo.si_note, 0);
-}
+	if (index >= (uint32_t)sc->sc_fmtgrp_num)
+		return (EINVAL);
 
-/* ---------------------------------------------------------------- */
-/*  Character Device Operations                                     */
-/* ---------------------------------------------------------------- */
+	fmtgrp = &sc->sc_fmtgrp[index];
+	frame = fmtgrp->frame_cur;
 
-/*
- * Per-fd state (via devfs cdevpriv).  Tracks whether this fd started
- * streaming so that STREAMOFF or close from a non-streaming fd (e.g. a
- * second tab that failed REQBUFS) does not tear down the active stream
- * owned by another fd.
- */
-struct uvideo_cdevpriv {
-	int			streaming;
-};
+	bzero(fmt, sizeof(*fmt));
+	fmt->pixelformat = fmtgrp->pixelformat;
+	if (frame != NULL) {
+		fmt->width = UGETW(UVIDEO_FRAME_FIELD(frame, wWidth));
+		fmt->height = UGETW(UVIDEO_FRAME_FIELD(frame, wHeight));
+	}
+	fmt->sizeimage = UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize);
+	fmt->field = V4L2_FIELD_NONE;
 
-static void	uvideo_cdevpriv_dtor(void *);
+	if (fmtgrp->has_colorformat) {
+		fmt->colorspace = fmtgrp->colorspace;
+		fmt->xfer_func = fmtgrp->xfer_func;
+		fmt->ycbcr_enc = fmtgrp->ycbcr_enc;
+	}
 
-static void
-uvideo_cdevpriv_dtor(void *data)
-{
+	switch (fmtgrp->format->bDescriptorSubtype) {
+	case UDESCSUB_VS_FORMAT_MJPEG:
+		fmt->flags = V4L2_FMT_FLAG_COMPRESSED;
+		strlcpy(fmt->description, "Motion-JPEG",
+		    sizeof(fmt->description));
+		break;
+	case UDESCSUB_VS_FORMAT_H264:
+	case UDESCSUB_VS_FORMAT_H264_SIMULCAST:
+		fmt->flags = V4L2_FMT_FLAG_COMPRESSED;
+		strlcpy(fmt->description, "H.264",
+		    sizeof(fmt->description));
+		break;
+	case UDESCSUB_VS_FORMAT_FRAME_BASED:
+		if (fmtgrp->format->u.fb.bVariableSize)
+			fmt->flags = V4L2_FMT_FLAG_COMPRESSED;
+		break;
+	default:
+		strlcpy(fmt->description, "YUV",
+		    sizeof(fmt->description));
+		break;
+	}
 
-	free(data, M_USBDEV);
+	return (0);
 }
 
 static int
-uvideo_cdev_open(struct cdev *dev, int flags, int fmt, struct thread *td)
+uvideo_hw_get_format(device_t dev, struct video_format *fmt)
 {
-	struct uvideo_softc *sc = dev->si_drv1;
-	struct uvideo_cdevpriv *priv;
-	int error;
-
-	if (sc == NULL || sc->sc_dying)
-		return (ENXIO);
+	struct uvideo_softc *sc = device_get_softc(dev);
+	struct usb_video_frame_desc *frame;
 
-	if (sc->sc_vs_cur == NULL)
+	if (sc->sc_fmtgrp_cur == NULL)
 		return (EIO);
 
-	priv = malloc(sizeof(*priv), M_USBDEV, M_WAITOK | M_ZERO);
-	error = devfs_set_cdevpriv(priv, uvideo_cdevpriv_dtor);
-	if (error != 0) {
-		free(priv, M_USBDEV);
-		return (error);
+	frame = sc->sc_fmtgrp_cur->frame_cur;
+
+	bzero(fmt, sizeof(*fmt));
+	fmt->pixelformat = sc->sc_fmtgrp_cur->pixelformat;
+	fmt->field = V4L2_FIELD_NONE;
+	if (frame != NULL) {
+		fmt->width = UGETW(UVIDEO_FRAME_FIELD(frame, wWidth));
+		fmt->height = UGETW(UVIDEO_FRAME_FIELD(frame, wHeight));
 	}
+	fmt->sizeimage = UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize);
 
-	mtx_lock(&sc->sc_mtx);
-	if (sc->sc_open == 0) {
-		/* First open: initialize state */
-		sc->sc_owner = td->td_proc;
-		sc->sc_mmap_flag = 0;
-		sc->sc_negotiated_flag = 0;
-		sc->sc_vidmode = VIDMODE_NONE;
-		sc->sc_frames_ready = 0;
-		sc->sc_priority = 1;	/* V4L2_PRIORITY_DEFAULT */
+	if (sc->sc_fmtgrp_cur->has_colorformat) {
+		fmt->colorspace = sc->sc_fmtgrp_cur->colorspace;
+		fmt->xfer_func = sc->sc_fmtgrp_cur->xfer_func;
+		fmt->ycbcr_enc = sc->sc_fmtgrp_cur->ycbcr_enc;
 	}
-	sc->sc_open++;
-	mtx_unlock(&sc->sc_mtx);
 
 	return (0);
 }
 
 static int
-uvideo_cdev_close(struct cdev *dev, int flags, int fmt, struct thread *td)
+uvideo_hw_try_format(device_t dev, struct video_format *fmt)
 {
-	struct uvideo_softc *sc = dev->si_drv1;
-	struct uvideo_cdevpriv *priv;
-
-	if (sc == NULL)
-		return (0);
+	struct uvideo_softc *sc = device_get_softc(dev);
+	struct uvideo_res r;
+	int found, i;
 
-	/*
-	 * If this fd started streaming, stop the stream and free the
-	 * buffers so that a new fd (e.g. a refreshed browser tab) can
-	 * re-acquire the camera.  Other fds sharing the stream will get
-	 * EPIPE on DQBUF and should re-open.
-	 */
-	if (devfs_get_cdevpriv((void **)&priv) == 0 && priv != NULL &&
-	    priv->streaming) {
*** 1357 LINES SKIPPED ***
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.