Skip site navigation (1)Skip section navigation (2)
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>