Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 09 Aug 2026 17:01:39 +0000
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Cc:        Abdelkader Boudih <freebsd@seuros.com>
Subject:   git: 8cdcf4b27c7b - main - uvideo: convert to video(4) framework
Message-ID:  <6a78b273.3b480.31123aa6@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by adrian:

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

commit 8cdcf4b27c7b9e0266d3d37c83566016d71582b4
Author:     Abdelkader Boudih <freebsd@seuros.com>
AuthorDate: 2026-08-09 16:53:58 +0000
Commit:     Adrian Chadd <adrian@FreeBSD.org>
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 ***


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a78b273.3b480.31123aa6>