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>
