Date: Sun, 09 Aug 2026 17:01:40 +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: f5dc2263ab1b - main - fwcam: convert to video(4) framework Message-ID: <6a78b274.3adef.62ed7ead@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=f5dc2263ab1be8a35a7e27e82103f9ccd41ae584 commit f5dc2263ab1be8a35a7e27e82103f9ccd41ae584 Author: Abdelkader Boudih <freebsd@seuros.com> AuthorDate: 2026-08-09 16:57:05 +0000 Commit: Adrian Chadd <adrian@FreeBSD.org> CommitDate: 2026-08-09 16:57:07 +0000 fwcam: convert to video(4) framework Replaced the monolithic cdevsw implementation with the video(4) framework. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D58369 --- share/man/man4/fwcam.4 | 28 +- sys/dev/firewire/fwcam.c | 799 +++++++++++++++++++++++------------- sys/dev/firewire/fwcam.h | 15 +- sys/modules/firewire/fwcam/Makefile | 3 +- 4 files changed, 542 insertions(+), 303 deletions(-) diff --git a/share/man/man4/fwcam.4 b/share/man/man4/fwcam.4 index 1c66c96d622f..2fa97da918fa 100644 --- a/share/man/man4/fwcam.4 +++ b/share/man/man4/fwcam.4 @@ -3,7 +3,7 @@ .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" -.Dd July 12, 2026 +.Dd July 27, 2026 .Dt FWCAM 4 .Os .Sh NAME @@ -11,6 +11,7 @@ .Nd IIDC FireWire digital camera driver .Sh SYNOPSIS .Cd "device firewire" +.Cd "device video" .Cd "device fwcam" .Sh DESCRIPTION The @@ -31,12 +32,13 @@ Camera hardware probing is deferred until the device is first opened. This avoids bus resets on controllers where early register access destabilizes the link. .Pp -Video frames are received over an isochronous DMA channel and exposed -through a character device. -Applications read raw frames from -.Pa /dev/fwcamX -and configure the camera via -.Xr ioctl 2 . +Video frames are received over an isochronous DMA channel and delivered +through the +.Xr video 4 +framework. +Applications access the camera via +.Pa /dev/videoN +using the V4L2 interface. .Pp The driver supports Format_0 (VGA non-compressed) video with the following modes: @@ -109,14 +111,16 @@ Can be set as a tunable in .Xr loader.conf 5 . .El .Sh FILES -.Bl -tag -width "/dev/fwcamX" -compact -.It Pa /dev/fwcamX -Camera character device. -Read to obtain raw video frames. +.Bl -tag -width "/dev/videoN" -compact +.It Pa /dev/videoN +Video capture device provided by the +.Xr video 4 +framework. .El .Sh SEE ALSO .Xr firewire 4 , -.Xr fwohci 4 +.Xr fwohci 4 , +.Xr video 4 .Pp IIDC 1394-based Digital Camera Specification v1.30, 1394 Trade Association Document 1999023. diff --git a/sys/dev/firewire/fwcam.c b/sys/dev/firewire/fwcam.c index 72bf429f061a..c98eb7a9894c 100644 --- a/sys/dev/firewire/fwcam.c +++ b/sys/dev/firewire/fwcam.c @@ -16,17 +16,13 @@ #include <sys/module.h> #include <sys/bus.h> #include <sys/kernel.h> -#include <sys/conf.h> #include <sys/malloc.h> #include <sys/lock.h> #include <sys/mutex.h> #include <sys/sysctl.h> #include <sys/taskqueue.h> -#include <sys/fcntl.h> -#include <sys/poll.h> -#include <sys/selinfo.h> -#include <sys/uio.h> #include <sys/mbuf.h> +#include <sys/videoio.h> #include <dev/firewire/firewire.h> #include <dev/firewire/firewirereg.h> @@ -34,6 +30,10 @@ #include <dev/firewire/fwcam.h> #include <dev/firewire/fw_helpers.h> +#include <dev/video/video.h> + +#include "video_if.h" + static MALLOC_DEFINE(M_FWCAM, "fwcam", "IIDC FireWire Camera"); static int debug = 0; @@ -59,24 +59,54 @@ static void fwcam_probe_task(void *, int); static int fwcam_iso_start(struct fwcam_softc *); static void fwcam_iso_stop(struct fwcam_softc *); static void fwcam_iso_input(struct fw_xferq *); +static void fwcam_frame_done(struct fwcam_softc *); + +static int fwcam_hw_open(device_t); +static int fwcam_hw_querycap(device_t, struct video_caps *); +static int fwcam_hw_enum_format(device_t, uint32_t, struct video_format *); +static int fwcam_hw_get_format(device_t, struct video_format *); +static int fwcam_hw_try_format(device_t, struct video_format *); +static int fwcam_hw_set_format(device_t, const struct video_format *); +static int fwcam_hw_enum_framesizes(device_t, struct video_frmsizeenum *); +static int fwcam_hw_enum_input(device_t, uint32_t, struct video_input *); +static int fwcam_hw_get_input(device_t, uint32_t *); +static int fwcam_hw_set_input(device_t, uint32_t); +static int fwcam_hw_query_control(device_t, struct video_control_desc *); +static int fwcam_hw_get_control(device_t, struct video_control *); +static int fwcam_hw_set_control(device_t, const struct video_control *); +static int fwcam_hw_start_stream(device_t); +static void fwcam_hw_stop_stream(device_t); + +/* + * Format_0 (VGA) mode descriptors for V4L2 mapping. + */ +struct fwcam_v4l2_mode { + uint32_t pixelformat; /* V4L2 fourcc */ + uint32_t width; + uint32_t height; + uint32_t bytesperline; + uint32_t sizeimage; +}; -static d_open_t fwcam_cdev_open; -static d_close_t fwcam_cdev_close; -static d_read_t fwcam_cdev_read; -static d_poll_t fwcam_cdev_poll; -static d_ioctl_t fwcam_cdev_ioctl; - -static struct cdevsw fwcam_cdevsw = { - .d_version = D_VERSION, - .d_flags = D_TRACKCLOSE, - .d_open = fwcam_cdev_open, - .d_close = fwcam_cdev_close, - .d_read = fwcam_cdev_read, - .d_poll = fwcam_cdev_poll, - .d_ioctl = fwcam_cdev_ioctl, - .d_name = "fwcam", +static const struct fwcam_v4l2_mode fwcam_fmt0_v4l2[] = { + /* mode 0: 160x120 YUV444 (24bpp packed) */ + { V4L2_PIX_FMT_YUV444, 160, 120, 160 * 3, 160 * 120 * 3 }, + /* mode 1: 320x240 YUV422 (UYVY, 16bpp packed) */ + { V4L2_PIX_FMT_UYVY, 320, 240, 320 * 2, 320 * 240 * 2 }, + /* mode 2: 640x480 YUV411 (12bpp) */ + { V4L2_PIX_FMT_Y41P, 640, 480, 640 * 3 / 2, 640 * 480 * 3 / 2 }, + /* mode 3: 640x480 YUV422 (UYVY, 16bpp packed) */ + { V4L2_PIX_FMT_UYVY, 640, 480, 640 * 2, 640 * 480 * 2 }, + /* mode 4: 640x480 RGB8 (24bpp) */ + { V4L2_PIX_FMT_RGB24, 640, 480, 640 * 3, 640 * 480 * 3 }, + /* mode 5: 640x480 Mono8 */ + { V4L2_PIX_FMT_GREY, 640, 480, 640, 640 * 480 }, + /* mode 6: 640x480 Mono16 */ + { V4L2_PIX_FMT_Y16, 640, 480, 640 * 2, 640 * 480 * 2 }, }; +#define FWCAM_FMT0_V4L2_NMODES nitems(fwcam_fmt0_v4l2) + /* * Search a CSR directory for the IIDC command base register (key 0x40). * The iSight places cmd_base inside a logical_unit_directory nested @@ -347,17 +377,10 @@ fwcam_probe_task(void *arg, int pending __unused) static uint32_t fwcam_frame_size(struct fwcam_softc *sc) { - static const uint32_t fmt0_sizes[] = { - 160 * 120 * 3, /* mode 0: YUV444 */ - 320 * 240 * 2, /* mode 1: YUV422 */ - 640 * 480 * 3 / 2, /* mode 2: YUV411 */ - 640 * 480 * 2, /* mode 3: YUV422 */ - 640 * 480 * 3, /* mode 4: RGB8 */ - 640 * 480, /* mode 5: Mono8 */ - }; - - if (sc->cur_format == IIDC_FMT_VGA && sc->cur_mode < nitems(fmt0_sizes)) - return (fmt0_sizes[sc->cur_mode]); + + if (sc->cur_format == IIDC_FMT_VGA && + sc->cur_mode < FWCAM_FMT0_V4L2_NMODES) + return (fwcam_fmt0_v4l2[sc->cur_mode].sizeimage); /* Default to largest VGA mode */ return (FWCAM_MAX_FRAME_SIZE); @@ -420,9 +443,7 @@ fwcam_iso_start(struct fwcam_softc *sc) sc->frame_size = fwcam_frame_size(sc); sc->frame_buf = malloc(sc->frame_size, M_FWCAM, M_WAITOK | M_ZERO); - sc->read_buf = malloc(sc->frame_size, M_FWCAM, M_WAITOK | M_ZERO); sc->frame_offset = 0; - sc->frame_ready = 0; sc->frame_dropped = 0; /* IIDC spec s3.1: set video mode registers before ISO enable */ @@ -519,9 +540,7 @@ fail: xferq->hand = NULL; free(sc->frame_buf, M_FWCAM); - free(sc->read_buf, M_FWCAM); sc->frame_buf = NULL; - sc->read_buf = NULL; sc->dma_ch = -1; return (err); @@ -553,9 +572,6 @@ fwcam_iso_stop(struct fwcam_softc *sc) FWCAM_LOCK(sc); fw_iso_wait_inactive_locked(&sc->mtx, &sc->iso_active, "fwcamis"); - sc->frame_ready = 0; - while (sc->read_in_progress) - msleep(&sc->read_in_progress, &sc->mtx, PWAIT, "fwcamst", hz); FWCAM_UNLOCK(sc); xferq->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_OPEN | FWXFERQ_STREAM | @@ -565,9 +581,23 @@ fwcam_iso_stop(struct fwcam_softc *sc) fw_iso_free_chunks(xferq, M_FWCAM); free(sc->frame_buf, M_FWCAM); - free(sc->read_buf, M_FWCAM); sc->frame_buf = NULL; - sc->read_buf = NULL; +} + +static void +fwcam_frame_done(struct fwcam_softc *sc) +{ + struct video_buf *vb; + + vb = video_buf_acquire(sc->sc_vd); + if (vb == NULL) + return; + + if (video_buf_write(vb, 0, sc->frame_buf, sc->frame_offset) != 0) { + video_buf_error(vb); + return; + } + video_buf_done(vb, sc->frame_offset, sc->sc_sequence++); } static void @@ -579,7 +609,6 @@ fwcam_iso_input(struct fw_xferq *xferq) struct mbuf *m; uint8_t *payload; uint32_t plen; - uint8_t *tmp; int dma_ch; FWCAM_LOCK(sc); @@ -606,25 +635,16 @@ fwcam_iso_input(struct fw_xferq *xferq) } if (fp->mode.stream.sy == 1) { - if (sc->frame_offset > 0) { - if (sc->frame_offset == sc->frame_size) { - FWCAM_LOCK(sc); - if (sc->read_in_progress) { - sc->frame_dropped++; - } else { - if (sc->frame_ready) - sc->frame_dropped++; - tmp = sc->read_buf; - sc->read_buf = sc->frame_buf; - sc->frame_buf = tmp; - sc->frame_ready = 1; - wakeup(sc); - selwakeup(&sc->rsel); - } - FWCAM_UNLOCK(sc); - } else { - sc->frame_dropped++; - } + if (sc->frame_offset > 0 && + sc->frame_offset == sc->frame_size) { + fwcam_frame_done(sc); + } else if (sc->frame_offset > 0) { + struct video_buf *vb; + + vb = video_buf_acquire(sc->sc_vd); + if (vb != NULL) + video_buf_error(vb); + sc->frame_dropped++; } sc->frame_offset = 0; } @@ -641,6 +661,11 @@ fwcam_iso_input(struct fw_xferq *xferq) memcpy(sc->frame_buf + sc->frame_offset, payload, plen); sc->frame_offset += plen; } else { + struct video_buf *vb; + + vb = video_buf_acquire(sc->sc_vd); + if (vb != NULL) + video_buf_error(vb); sc->frame_dropped++; sc->frame_offset = 0; } @@ -652,131 +677,6 @@ fwcam_iso_input(struct fw_xferq *xferq) &sc->dma_ch, dma_ch); } -static int -fwcam_cdev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) -{ - struct fwcam_softc *sc = dev->si_drv1; - int err; - - FWCAM_LOCK(sc); - if (sc->state == FWCAM_STATE_DETACHING) { - FWCAM_UNLOCK(sc); - return (ENXIO); - } - - if (sc->state == FWCAM_STATE_IDLE) { - sc->state = FWCAM_STATE_PROBING; - FWCAM_UNLOCK(sc); - taskqueue_enqueue(taskqueue_thread, &sc->probe_task); - FWCAM_LOCK(sc); - } - - while (sc->state == FWCAM_STATE_PROBING) { - err = msleep(sc, &sc->mtx, PCATCH, "fwcampr", 10 * hz); - if (err) { - FWCAM_UNLOCK(sc); - return (err == EWOULDBLOCK ? ETIMEDOUT : err); - } - } - - if (sc->state != FWCAM_STATE_PROBED && - sc->state != FWCAM_STATE_STREAMING) { - FWCAM_UNLOCK(sc); - return (ENXIO); - } - - sc->open_count++; - FWCAM_UNLOCK(sc); - return (0); -} - -static int -fwcam_cdev_close(struct cdev *dev, int fflag, int devtype, struct thread *td) -{ - struct fwcam_softc *sc = dev->si_drv1; - - FWCAM_LOCK(sc); - sc->open_count--; - if (sc->open_count <= 0) { - sc->open_count = 0; - if (sc->state == FWCAM_STATE_STREAMING) { - FWCAM_UNLOCK(sc); - fwcam_iso_stop(sc); - FWCAM_LOCK(sc); - if (sc->state != FWCAM_STATE_DETACHING) - sc->state = FWCAM_STATE_PROBED; - } - } - FWCAM_UNLOCK(sc); - return (0); -} - -static int -fwcam_cdev_read(struct cdev *dev, struct uio *uio, int ioflag) -{ - struct fwcam_softc *sc = dev->si_drv1; - int err; - - FWCAM_LOCK(sc); - if (sc->state == FWCAM_STATE_PROBED) { - FWCAM_UNLOCK(sc); - err = fwcam_iso_start(sc); - if (err) - return (err); - FWCAM_LOCK(sc); - } - while (!sc->frame_ready) { - if (sc->state != FWCAM_STATE_STREAMING) { - FWCAM_UNLOCK(sc); - return (ENXIO); - } - if (ioflag & FNONBLOCK) { - FWCAM_UNLOCK(sc); - return (EAGAIN); - } - err = msleep(sc, &sc->mtx, PCATCH, "fwcamrd", 5 * hz); - if (err) { - FWCAM_UNLOCK(sc); - return (err); - } - } - - sc->frame_ready = 0; - if (sc->read_buf == NULL) { - FWCAM_UNLOCK(sc); - return (ENXIO); - } - sc->read_in_progress = 1; - FWCAM_UNLOCK(sc); - - err = uiomove(sc->read_buf, - MIN(uio->uio_resid, sc->frame_size), uio); - - FWCAM_LOCK(sc); - sc->read_in_progress = 0; - wakeup(&sc->read_in_progress); - FWCAM_UNLOCK(sc); - - return (err); -} - -static int -fwcam_cdev_poll(struct cdev *dev, int events, struct thread *td) -{ - struct fwcam_softc *sc = dev->si_drv1; - int revents = 0; - - FWCAM_LOCK(sc); - if (events & (POLLIN | POLLRDNORM)) { - if (sc->frame_ready) - revents |= events & (POLLIN | POLLRDNORM); - else - selrecord(td, &sc->rsel); - } - FWCAM_UNLOCK(sc); - return (revents); -} - static const uint32_t fwcam_feat_inq[] = { [FWCAM_FEAT_BRIGHTNESS] = IIDC_BRIGHTNESS_INQ, [FWCAM_FEAT_AUTO_EXPOSURE] = IIDC_AUTO_EXPOSURE_INQ, @@ -812,6 +712,40 @@ static const uint32_t fwcam_feat_ctrl[] = { [FWCAM_FEAT_TILT] = IIDC_TILT, }; +/* + * Map IIDC feature IDs to V4L2 control IDs. + */ +static const uint32_t fwcam_feat_v4l2[] = { + [FWCAM_FEAT_BRIGHTNESS] = V4L2_CID_BRIGHTNESS, + [FWCAM_FEAT_AUTO_EXPOSURE] = V4L2_CID_EXPOSURE_AUTO, + [FWCAM_FEAT_SHARPNESS] = V4L2_CID_SHARPNESS, + [FWCAM_FEAT_WHITE_BALANCE] = V4L2_CID_AUTO_WHITE_BALANCE, + [FWCAM_FEAT_HUE] = V4L2_CID_HUE, + [FWCAM_FEAT_SATURATION] = V4L2_CID_SATURATION, + [FWCAM_FEAT_GAMMA] = V4L2_CID_GAMMA, + [FWCAM_FEAT_SHUTTER] = V4L2_CID_EXPOSURE_ABSOLUTE, + [FWCAM_FEAT_GAIN] = V4L2_CID_GAIN, + [FWCAM_FEAT_FOCUS] = V4L2_CID_FOCUS_ABSOLUTE, +}; + +#define FWCAM_V4L2_CTRL_COUNT nitems(fwcam_feat_v4l2) + +/* + * Find the IIDC feature ID for a V4L2 control ID. + * Returns -1 if not found. + */ +static int +fwcam_find_feat_by_v4l2(uint32_t v4l2_id) +{ + int i; + + for (i = 0; i < (int)FWCAM_V4L2_CTRL_COUNT; i++) { + if (fwcam_feat_v4l2[i] == v4l2_id) + return (i); + } + return (-1); +} + static int fwcam_get_feature(struct fwcam_softc *sc, struct fwcam_feature *feat) { @@ -889,111 +823,392 @@ fwcam_set_feature(struct fwcam_softc *sc, struct fwcam_feature *feat) return (fwcam_write_quadlet(sc, fwcam_feat_ctrl[feat->id], val)); } +/* + * Ensure the camera has been probed (powered on, capabilities read). + * Called from hw callbacks that need device state. + */ static int -fwcam_cdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, - int fflag, struct thread *td) +fwcam_ensure_probed(struct fwcam_softc *sc) { - struct fwcam_softc *sc = dev->si_drv1; - struct fwcam_mode *mode; - struct fwcam_feature *feat; - struct fwcam_info *info; int err; - if (sc->fwdev == NULL) + FWCAM_LOCK(sc); + if (sc->state == FWCAM_STATE_DETACHING) { + FWCAM_UNLOCK(sc); return (ENXIO); + } - switch (cmd) { - case FWCAM_GMODE: - mode = (struct fwcam_mode *)data; - mode->format = sc->cur_format; - mode->mode = sc->cur_mode; - mode->framerate = sc->cur_framerate; - mode->frame_size = sc->frame_size ? - sc->frame_size : fwcam_frame_size(sc); - return (0); + if (sc->state == FWCAM_STATE_IDLE) { + sc->state = FWCAM_STATE_PROBING; + FWCAM_UNLOCK(sc); + taskqueue_enqueue(taskqueue_thread, &sc->probe_task); + FWCAM_LOCK(sc); + } + + while (sc->state == FWCAM_STATE_PROBING) { + err = msleep(sc, &sc->mtx, PCATCH, "fwcampr", 10 * hz); + if (err) { + FWCAM_UNLOCK(sc); + return (err == EWOULDBLOCK ? ETIMEDOUT : err); + } + } + + if (sc->state != FWCAM_STATE_PROBED && + sc->state != FWCAM_STATE_STREAMING) { + FWCAM_UNLOCK(sc); + return (ENXIO); + } + + FWCAM_UNLOCK(sc); + return (0); +} + +static int +fwcam_hw_open(device_t dev) +{ + struct fwcam_softc *sc = device_get_softc(dev); - case FWCAM_SMODE: - { - int was_streaming = 0; + return (fwcam_ensure_probed(sc)); +} - mode = (struct fwcam_mode *)data; - if (mode->format > 7 || mode->mode > 7 || mode->framerate > 7) - return (EINVAL); +static int +fwcam_hw_querycap(device_t dev, struct video_caps *caps) +{ - if (!(sc->formats & (1 << (31 - mode->format)))) - return (EINVAL); - if (!(sc->modes[mode->format] & (1 << (31 - mode->mode)))) - return (EINVAL); - if (!(sc->rates[mode->format][mode->mode] & - (1 << (31 - mode->framerate)))) - return (EINVAL); + bzero(caps, sizeof(*caps)); + strlcpy(caps->driver, "fwcam", sizeof(caps->driver)); + strlcpy(caps->card, "IIDC FireWire Camera", sizeof(caps->card)); + strlcpy(caps->bus_info, "firewire", sizeof(caps->bus_info)); + caps->version = (1 << 16) | (0 << 8) | 0; /* 1.0.0 */ + caps->capabilities = VIDEO_CAP_CAPTURE | + VIDEO_CAP_READWRITE | VIDEO_CAP_STREAMING; - FWCAM_LOCK(sc); - if (sc->state == FWCAM_STATE_DETACHING) { - FWCAM_UNLOCK(sc); - return (ENXIO); + return (0); +} + +/* + * Map a sequential enum index to an IIDC mode number. + * Returns -1 if index is out of range. + */ +static int +fwcam_index_to_mode(struct fwcam_softc *sc, uint32_t index) +{ + int m; + uint32_t count = 0; + + if (!(sc->formats & IIDC_FORMAT_VGA)) + return (-1); + + for (m = 0; m < (int)FWCAM_FMT0_V4L2_NMODES; m++) { + if (sc->modes[IIDC_FMT_VGA] & (1 << (31 - m))) { + if (count == index) + return (m); + count++; } - if (sc->state == FWCAM_STATE_STREAMING) { - was_streaming = 1; - FWCAM_UNLOCK(sc); - fwcam_iso_stop(sc); - FWCAM_LOCK(sc); - if (sc->state != FWCAM_STATE_DETACHING) - sc->state = FWCAM_STATE_PROBED; + } + return (-1); +} + +static int +fwcam_hw_enum_format(device_t dev, uint32_t index, struct video_format *fmt) +{ + struct fwcam_softc *sc = device_get_softc(dev); + const struct fwcam_v4l2_mode *vm; + int m; + + m = fwcam_index_to_mode(sc, index); + if (m < 0) + return (EINVAL); + + vm = &fwcam_fmt0_v4l2[m]; + + bzero(fmt, sizeof(*fmt)); + fmt->pixelformat = vm->pixelformat; + fmt->width = vm->width; + fmt->height = vm->height; + fmt->bytesperline = vm->bytesperline; + fmt->sizeimage = vm->sizeimage; + fmt->field = V4L2_FIELD_NONE; + + return (0); +} + +static int +fwcam_hw_get_format(device_t dev, struct video_format *fmt) +{ + struct fwcam_softc *sc = device_get_softc(dev); + const struct fwcam_v4l2_mode *vm; + + if (sc->cur_format != IIDC_FMT_VGA || + sc->cur_mode >= FWCAM_FMT0_V4L2_NMODES) + return (EIO); + + vm = &fwcam_fmt0_v4l2[sc->cur_mode]; + + bzero(fmt, sizeof(*fmt)); + fmt->pixelformat = vm->pixelformat; + fmt->width = vm->width; + fmt->height = vm->height; + fmt->bytesperline = vm->bytesperline; + fmt->sizeimage = vm->sizeimage; + fmt->field = V4L2_FIELD_NONE; + + return (0); +} + +/* + * Find the IIDC mode that best matches a V4L2 format request. + * Returns -1 if no match. + */ +static int +fwcam_find_mode_for_format(struct fwcam_softc *sc, + const struct video_format *fmt) +{ + int m, best = -1; + + if (!(sc->formats & IIDC_FORMAT_VGA)) + return (-1); + + for (m = 0; m < (int)FWCAM_FMT0_V4L2_NMODES; m++) { + if (!(sc->modes[IIDC_FMT_VGA] & (1 << (31 - m)))) + continue; + if (fwcam_fmt0_v4l2[m].pixelformat == fmt->pixelformat && + fwcam_fmt0_v4l2[m].width == fmt->width && + fwcam_fmt0_v4l2[m].height == fmt->height) { + best = m; + break; } - FWCAM_UNLOCK(sc); + } - err = fwcam_write_quadlet(sc, IIDC_CUR_V_FORMAT, - (uint32_t)mode->format << IIDC_CUR_V_SHIFT); - if (err == 0) - err = fwcam_write_quadlet(sc, IIDC_CUR_V_MODE, - (uint32_t)mode->mode << IIDC_CUR_V_SHIFT); - if (err == 0) - err = fwcam_write_quadlet(sc, IIDC_CUR_V_FRM_RATE, - (uint32_t)mode->framerate << IIDC_CUR_V_SHIFT); - - if (err == 0) { - sc->cur_format = mode->format; - sc->cur_mode = mode->mode; - sc->cur_framerate = mode->framerate; - mode->frame_size = fwcam_frame_size(sc); + /* If exact match failed, try matching just pixelformat */ + if (best < 0) { + for (m = 0; m < (int)FWCAM_FMT0_V4L2_NMODES; m++) { + if (!(sc->modes[IIDC_FMT_VGA] & (1 << (31 - m)))) + continue; + if (fwcam_fmt0_v4l2[m].pixelformat == + fmt->pixelformat) { + best = m; + break; + } } + } - if (was_streaming) - fwcam_iso_start(sc); - return (err); - } - - case FWCAM_GFEAT: - feat = (struct fwcam_feature *)data; - return (fwcam_get_feature(sc, feat)); - - case FWCAM_SFEAT: - feat = (struct fwcam_feature *)data; - return (fwcam_set_feature(sc, feat)); - - case FWCAM_GINFO: - info = (struct fwcam_info *)data; - info->formats = sc->formats; - info->basic_func = sc->basic_func; - info->features_hi = sc->features_hi; - info->features_lo = sc->features_lo; - info->cur_format = sc->cur_format; - info->cur_mode = sc->cur_mode; - info->cur_framerate = sc->cur_framerate; - info->state = sc->state; - info->frame_size = sc->frame_size ? - sc->frame_size : fwcam_frame_size(sc); - info->frame_dropped = sc->frame_dropped; - info->iso_channel = sc->iso_channel; - info->_pad[0] = info->_pad[1] = info->_pad[2] = 0; - return (0); + return (best); +} + +static int +fwcam_hw_try_format(device_t dev, struct video_format *fmt) +{ + struct fwcam_softc *sc = device_get_softc(dev); + const struct fwcam_v4l2_mode *vm; + int m; + + m = fwcam_find_mode_for_format(sc, fmt); + if (m < 0) + return (EINVAL); + + vm = &fwcam_fmt0_v4l2[m]; + + fmt->pixelformat = vm->pixelformat; + fmt->width = vm->width; + fmt->height = vm->height; + fmt->bytesperline = vm->bytesperline; + fmt->sizeimage = vm->sizeimage; + fmt->field = V4L2_FIELD_NONE; + + return (0); +} + +static int +fwcam_hw_set_format(device_t dev, const struct video_format *fmt) +{ + struct fwcam_softc *sc = device_get_softc(dev); + int m, err; + + m = fwcam_find_mode_for_format(sc, fmt); + if (m < 0) + return (EINVAL); + + if (!(sc->rates[IIDC_FMT_VGA][m] & + (1 << (31 - sc->cur_framerate)))) { + /* Current framerate not supported in new mode, pick first */ + int r; + for (r = 0; r < 8; r++) { + if (sc->rates[IIDC_FMT_VGA][m] & (1 << (31 - r))) { + sc->cur_framerate = r; + break; + } + } + } + + err = fwcam_write_quadlet(sc, IIDC_CUR_V_FORMAT, + (uint32_t)IIDC_FMT_VGA << IIDC_CUR_V_SHIFT); + if (err == 0) + err = fwcam_write_quadlet(sc, IIDC_CUR_V_MODE, + (uint32_t)m << IIDC_CUR_V_SHIFT); + if (err == 0) + err = fwcam_write_quadlet(sc, IIDC_CUR_V_FRM_RATE, + (uint32_t)sc->cur_framerate << IIDC_CUR_V_SHIFT); + + if (err == 0) { + sc->cur_format = IIDC_FMT_VGA; + sc->cur_mode = m; + } - default: - return (ENOTTY); + return (err); +} + +static int +fwcam_hw_enum_framesizes(device_t dev, struct video_frmsizeenum *fse) +{ + struct fwcam_softc *sc = device_get_softc(dev); + int m; + uint32_t count = 0; + + if (!(sc->formats & IIDC_FORMAT_VGA)) + return (EINVAL); + + for (m = 0; m < (int)FWCAM_FMT0_V4L2_NMODES; m++) { + if (!(sc->modes[IIDC_FMT_VGA] & (1 << (31 - m)))) + continue; + if (fwcam_fmt0_v4l2[m].pixelformat != fse->pixelformat) + continue; + if (count == fse->index) { + fse->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fse->discrete.width = fwcam_fmt0_v4l2[m].width; + fse->discrete.height = fwcam_fmt0_v4l2[m].height; + return (0); + } + count++; } + + return (EINVAL); } +static int +fwcam_hw_enum_input(device_t dev, uint32_t index, struct video_input *inp) +{ + + if (index != 0) + return (EINVAL); + + bzero(inp, sizeof(*inp)); + inp->index = 0; + strlcpy(inp->name, "IIDC Camera", sizeof(inp->name)); + inp->type = VIDEO_INPUT_TYPE_CAMERA; + + return (0); +} + +static int +fwcam_hw_get_input(device_t dev, uint32_t *index) +{ + + *index = 0; + return (0); +} + +static int +fwcam_hw_set_input(device_t dev, uint32_t index) +{ + + if (index != 0) + return (EINVAL); + return (0); +} + +static int +fwcam_hw_query_control(device_t dev, struct video_control_desc *qc) +{ + struct fwcam_softc *sc = device_get_softc(dev); + struct fwcam_feature feat; + int fid; + + fid = fwcam_find_feat_by_v4l2(qc->id); + if (fid < 0) + return (EINVAL); + + feat.id = fid; + if (fwcam_get_feature(sc, &feat) != 0) + return (EINVAL); + + if (!(feat.flags & FWCAM_FEATF_PRESENT)) + return (EINVAL); + + qc->type = V4L2_CTRL_TYPE_INTEGER; + strlcpy(qc->name, fwcam_feat_names[fid], sizeof(qc->name)); + qc->minimum = feat.min; + qc->maximum = feat.max; + qc->step = 1; + qc->default_value = feat.min; + qc->flags = 0; + + return (0); +} + +static int +fwcam_hw_get_control(device_t dev, struct video_control *ctrl) +{ + struct fwcam_softc *sc = device_get_softc(dev); + struct fwcam_feature feat; + int fid; + + fid = fwcam_find_feat_by_v4l2(ctrl->id); + if (fid < 0) + return (EINVAL); + + feat.id = fid; + if (fwcam_get_feature(sc, &feat) != 0) + return (EINVAL); + + ctrl->value = feat.value; + return (0); +} + +static int +fwcam_hw_set_control(device_t dev, const struct video_control *ctrl) +{ + struct fwcam_softc *sc = device_get_softc(dev); + struct fwcam_feature feat; + int fid; + + fid = fwcam_find_feat_by_v4l2(ctrl->id); + if (fid < 0) + return (EINVAL); + + feat.id = fid; + feat.value = ctrl->value; + feat.value2 = 0; + return (fwcam_set_feature(sc, &feat)); +} + +static int +fwcam_hw_start_stream(device_t dev) +{ + struct fwcam_softc *sc = device_get_softc(dev); + int err; + + err = fwcam_ensure_probed(sc); + if (err) + return (err); + + sc->sc_sequence = 0; + + return (fwcam_iso_start(sc)); +} + +static void +fwcam_hw_stop_stream(device_t dev) +{ + struct fwcam_softc *sc = device_get_softc(dev); + + fwcam_iso_stop(sc); + + FWCAM_LOCK(sc); + if (sc->state != FWCAM_STATE_DETACHING) + sc->state = FWCAM_STATE_PROBED; + FWCAM_UNLOCK(sc); +} static int fwcam_probe(device_t dev) @@ -1021,6 +1236,7 @@ fwcam_attach(device_t dev) struct fwcam_softc *sc; struct fw_unit *unit; uint32_t cmd_base; + int err; unit = fw_get_unit(dev); if (unit == NULL || unit->fwdev == NULL) @@ -1044,13 +1260,15 @@ fwcam_attach(device_t dev) *** 130 LINES SKIPPED ***home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a78b274.3adef.62ed7ead>
