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 ***

Reply via email to