git: f5dc2263ab1b - main - fwcam: convert to video(4) framework

Adrian Chadd <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a78b274.3adef.62ed7ead__37002.1900440165$1786294938$gmane$org@gitrepo.freebsd.org>
The branch main has been updated by adrian:

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

commit f5dc2263ab1be8a35a7e27e82103f9ccd41ae584
Author:     Abdelkader Boudih <[email protected]>
AuthorDate: 2026-08-09 16:57:05 +0000
Commit:     Adrian Chadd <[email protected]>
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 ***
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.