git: 8cdcf4b27c7b - main - uvideo: convert to video(4) framework
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 09 Aug 2026 17:01:39 UTC
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 ***