Page MenuHomeFreeBSD

D57685.id180112.diff
No OneTemporary

D57685.id180112.diff

diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -177,6 +177,7 @@
ffs.4 \
filemon.4 \
firewire.4 \
+ fwcam.4 \
${_ftgpio.4} \
${_ftwd.4} \
full.4 \
diff --git a/share/man/man4/fwcam.4 b/share/man/man4/fwcam.4
new file mode 100644
--- /dev/null
+++ b/share/man/man4/fwcam.4
@@ -0,0 +1,116 @@
+.\"
+.\" Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd June 19, 2026
+.Dt FWCAM 4
+.Os
+.Sh NAME
+.Nm fwcam
+.Nd IIDC 1394-based digital camera driver
+.Sh SYNOPSIS
+.Cd "device firewire"
+.Cd "device fwcam"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for IIDC (Instrumentation and Industrial Digital
+Camera) v1.30 cameras attached via IEEE 1394 (FireWire).
+.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 .
+.Pp
+The driver supports Format_0 (VGA non-compressed) video with the
+following modes:
+.Bl -tag -width "Mode 3" -compact
+.It Mode 0
+160x120 YUV444 (57,600 bytes per frame)
+.It Mode 1
+320x240 YUV422 (153,600 bytes per frame)
+.It Mode 2
+640x480 YUV411 (460,800 bytes per frame)
+.It Mode 3
+640x480 YUV422 (614,400 bytes per frame)
+.It Mode 4
+640x480 RGB8 (921,600 bytes per frame)
+.It Mode 5
+640x480 Mono8 (307,200 bytes per frame)
+.El
+.Pp
+Camera features
+.Pq brightness, gain, shutter, focus, etc.\&
+are queried and set via
+.Dv FWCAM_GFEAT
+and
+.Dv FWCAM_SFEAT
+ioctls.
+.Sh HARDWARE
+The
+.Nm
+driver supports any camera compliant with the IIDC 1394-based Digital Camera
+Specification v1.30.
+Tested hardware:
+.Pp
+.Bl -tag -width "Apple iSight (FireWire cylinder)" -compact
+.It Apple iSight (FireWire cylinder)
+EUI-64 000a270004135e5d, Format_0 modes 1-3
+.El
+.Sh IOCTLS
+.Bl -tag -width ".Dv FWCAM_GINFO"
+.It Dv FWCAM_GMODE Pq Vt "struct fwcam_mode"
+Get the current video format, mode, and frame rate.
+The
+.Va frame_size
+field is filled with the computed frame size in bytes.
+.It Dv FWCAM_SMODE Pq Vt "struct fwcam_mode"
+Set the video format, mode, and frame rate.
+Streaming is restarted if active.
+.It Dv FWCAM_GFEAT Pq Vt "struct fwcam_feature"
+Get feature capabilities and current value.
+The caller sets
+.Va id
+to one of the
+.Dv FWCAM_FEAT_*
+constants; the driver fills
+.Va flags ,
+.Va min ,
+.Va max ,
+and
+.Va value .
+.It Dv FWCAM_SFEAT Pq Vt "struct fwcam_feature"
+Set a feature value.
+.It Dv FWCAM_GINFO Pq Vt "struct fwcam_info"
+Return camera capabilities, current settings, and stream statistics
+including dropped frame count and active ISO channel.
+.El
+.Sh SYSCTLS
+.Bl -tag -width ".Va hw.firewire.fwcam.iso_channel"
+.It Va hw.firewire.fwcam.iso_channel
+Isochronous receive channel (default 0).
+Can be set as a tunable in
+.Xr loader.conf 5 .
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /dev/fwcamX" -compact
+.It Pa /dev/fwcamX
+Camera character device.
+Read to obtain raw video frames.
+.El
+.Sh SEE ALSO
+.Xr firewire 4 ,
+.Xr fwohci 4
+.Pp
+IIDC 1394-based Digital Camera Specification v1.30,
+1394 Trade Association Document 1999023.
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 16.0 .
+.Sh AUTHORS
+.An Abdelkader Boudih Aq Mt freebsd@seuros.com
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -2613,6 +2613,7 @@
device sbp_targ # SBP-2 Target mode (Requires scbus and targ)
device fwe # Ethernet over FireWire (non-standard!)
device fwip # IP over FireWire (RFC2734 and RFC3146)
+device fwcam # IIDC digital cameras over FireWire
#####################################################################
# dcons support (Dumb Console Device)
diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1729,6 +1729,7 @@
dev/firewire/firewire.c optional firewire
dev/firewire/fwcrom.c optional firewire
dev/firewire/fwdev.c optional firewire
+dev/firewire/fwcam.c optional fwcam
dev/firewire/fwdma.c optional firewire
dev/firewire/fwmem.c optional firewire
dev/firewire/fwohci.c optional firewire
diff --git a/sys/dev/firewire/fwcam.h b/sys/dev/firewire/fwcam.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/firewire/fwcam.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _DEV_FIREWIRE_FWCAM_H_
+#define _DEV_FIREWIRE_FWCAM_H_
+
+/*
+ * IIDC 1394-based Digital Camera Specification v1.30
+ * Register offsets relative to command_regs_base
+ */
+
+/* Section 1.1 - Camera initialize register */
+#define IIDC_INITIALIZE 0x000
+
+/* Section 1.2 - Inquiry registers for video format/mode/frame rate */
+#define IIDC_V_FORMAT_INQ 0x100
+#define IIDC_V_MODE_INQ(f) (0x180 + (f) * 4)
+#define IIDC_V_RATE_INQ(f, m) (0x200 + (f) * 0x20 + (m) * 4)
+
+/* Section 1.3 - Inquiry register for basic function */
+#define IIDC_BASIC_FUNC_INQ 0x400
+
+/* Section 1.4 - Inquiry registers for feature presence */
+#define IIDC_FEATURE_HI_INQ 0x404
+#define IIDC_FEATURE_LO_INQ 0x408
+
+/* Section 1.5 - Inquiry registers for feature elements (per feature) */
+#define IIDC_BRIGHTNESS_INQ 0x500
+#define IIDC_AUTO_EXPOSURE_INQ 0x504
+#define IIDC_SHARPNESS_INQ 0x508
+#define IIDC_WHITE_BAL_INQ 0x50C
+#define IIDC_HUE_INQ 0x510
+#define IIDC_SATURATION_INQ 0x514
+#define IIDC_GAMMA_INQ 0x518
+#define IIDC_SHUTTER_INQ 0x51C
+#define IIDC_GAIN_INQ 0x520
+#define IIDC_IRIS_INQ 0x524
+#define IIDC_FOCUS_INQ 0x528
+#define IIDC_TEMPERATURE_INQ 0x52C
+#define IIDC_TRIGGER_INQ 0x530
+
+/* Section 1.6 - Status and control registers for camera */
+#define IIDC_CUR_V_FRM_RATE 0x600
+#define IIDC_CUR_V_MODE 0x604
+#define IIDC_CUR_V_FORMAT 0x608
+#define IIDC_ISO_CHANNEL 0x60C
+#define IIDC_CAMERA_POWER 0x610
+#define IIDC_ISO_EN 0x614
+#define IIDC_MEMORY_SAVE 0x618
+#define IIDC_ONE_SHOT 0x61C
+#define IIDC_MEM_SAVE_CH 0x620
+#define IIDC_CUR_MEM_CH 0x624
+
+/* Section 1.7 - Status and control register for features */
+#define IIDC_BRIGHTNESS 0x800
+#define IIDC_AUTO_EXPOSURE 0x804
+#define IIDC_SHARPNESS 0x808
+#define IIDC_WHITE_BALANCE 0x80C
+#define IIDC_HUE 0x810
+#define IIDC_SATURATION 0x814
+#define IIDC_GAMMA 0x818
+#define IIDC_SHUTTER 0x81C
+#define IIDC_GAIN 0x820
+#define IIDC_IRIS 0x824
+#define IIDC_FOCUS 0x828
+#define IIDC_TEMPERATURE 0x82C
+#define IIDC_TRIGGER_MODE 0x830
+#define IIDC_ZOOM 0x880
+#define IIDC_PAN 0x884
+#define IIDC_TILT 0x888
+
+/* Video format indices (CUR_V_FORMAT register) */
+#define IIDC_FMT_VGA 0 /* Format_0: VGA non-compressed */
+#define IIDC_FMT_SVGA1 1 /* Format_1: Super VGA (1) */
+#define IIDC_FMT_SVGA2 2 /* Format_2: Super VGA (2) */
+#define IIDC_FMT_STILL 6 /* Format_6: Still image */
+#define IIDC_FMT_PARTIAL 7 /* Format_7: Partial/scalable */
+
+/* V_FORMAT_INQ bits */
+#define IIDC_FORMAT_VGA (1 << 31) /* Format_0: VGA */
+#define IIDC_FORMAT_SVGA1 (1 << 30) /* Format_1: Super VGA (1) */
+#define IIDC_FORMAT_SVGA2 (1 << 29) /* Format_2: Super VGA (2) */
+#define IIDC_FORMAT_STILL (1 << 25) /* Format_6: Still image */
+#define IIDC_FORMAT_PARTIAL (1 << 24) /* Format_7: Partial image */
+
+/* ISO_CHANNEL register fields */
+#define IIDC_ISO_CH_MASK 0xf0000000 /* bits [0..3] */
+#define IIDC_ISO_CH_SHIFT 28
+#define IIDC_ISO_SPEED_MASK 0x03000000 /* bits [6..7] */
+#define IIDC_ISO_SPEED_SHIFT 24
+
+/* CAMERA_POWER register */
+#define IIDC_POWER_ON (1 << 31) /* bit [0] */
+
+/* ISO_EN register */
+#define IIDC_ISO_EN_ON (1 << 31) /* bit [0] */
+
+/* BASIC_FUNC_INQ bits */
+#define IIDC_ADV_FEATURE_INQ (1 << 31) /* bit [0] */
+#define IIDC_CAM_POWER_CTRL (1 << 15) /* bit [16] */
+#define IIDC_ONE_SHOT_INQ (1 << 12) /* bit [19] */
+#define IIDC_MULTI_SHOT_INQ (1 << 11) /* bit [20] */
+
+/* FEATURE_HI_INQ bits (feature presence, section 1.4) */
+#define IIDC_HAS_BRIGHTNESS (1 << 31)
+#define IIDC_HAS_AUTO_EXPOSURE (1 << 30)
+#define IIDC_HAS_SHARPNESS (1 << 29)
+#define IIDC_HAS_WHITE_BALANCE (1 << 28)
+#define IIDC_HAS_HUE (1 << 27)
+#define IIDC_HAS_SATURATION (1 << 26)
+#define IIDC_HAS_GAMMA (1 << 25)
+#define IIDC_HAS_SHUTTER (1 << 24)
+#define IIDC_HAS_GAIN (1 << 23)
+#define IIDC_HAS_IRIS (1 << 22)
+#define IIDC_HAS_FOCUS (1 << 21)
+#define IIDC_HAS_TEMPERATURE (1 << 20)
+#define IIDC_HAS_TRIGGER (1 << 19)
+
+/* Config ROM: IIDC unit-dependent directory command_regs_base key */
+#define IIDC_CROM_CMD_BASE (CSRTYPE_C | 0x00) /* 0x40 */
+
+/*
+ * Frame size limits for Format_0 (VGA non-compressed):
+ * Mode_1: 320x240 YUV422 = 153,600 bytes
+ * Mode_2: 640x480 YUV411 = 460,800 bytes
+ * Mode_3: 640x480 YUV422 = 614,400 bytes
+ */
+/*
+ * ioctl interface
+ */
+struct fwcam_mode {
+ uint8_t format; /* IIDC video format (0-7) */
+ uint8_t mode; /* IIDC video mode (0-7) */
+ uint8_t framerate; /* IIDC frame rate (0-7) */
+ uint8_t _pad;
+ uint32_t frame_size; /* computed frame size in bytes (read-only) */
+};
+
+struct fwcam_feature {
+ uint32_t id; /* FWCAM_FEAT_* */
+ uint32_t flags; /* FWCAM_FEATF_* (from INQ, read-only) */
+ uint32_t min; /* minimum value (from INQ, read-only) */
+ uint32_t max; /* maximum value (from INQ, read-only) */
+ uint32_t value; /* current value / value to set */
+ uint32_t value2; /* second value (white balance V) */
+};
+
+/* Feature IDs (index into IIDC feature register space) */
+#define FWCAM_FEAT_BRIGHTNESS 0
+#define FWCAM_FEAT_AUTO_EXPOSURE 1
+#define FWCAM_FEAT_SHARPNESS 2
+#define FWCAM_FEAT_WHITE_BALANCE 3
+#define FWCAM_FEAT_HUE 4
+#define FWCAM_FEAT_SATURATION 5
+#define FWCAM_FEAT_GAMMA 6
+#define FWCAM_FEAT_SHUTTER 7
+#define FWCAM_FEAT_GAIN 8
+#define FWCAM_FEAT_IRIS 9
+#define FWCAM_FEAT_FOCUS 10
+#define FWCAM_FEAT_TEMPERATURE 11
+#define FWCAM_FEAT_TRIGGER 12
+#define FWCAM_FEAT_ZOOM 13
+#define FWCAM_FEAT_PAN 14
+#define FWCAM_FEAT_TILT 15
+#define FWCAM_FEAT_MAX 16
+
+/* Feature flags (from INQ register bits) */
+#define FWCAM_FEATF_PRESENT (1 << 0) /* feature is present */
+#define FWCAM_FEATF_ONOFF (1 << 1) /* supports on/off */
+#define FWCAM_FEATF_AUTO (1 << 2) /* supports auto mode */
+#define FWCAM_FEATF_MANUAL (1 << 3) /* supports manual mode */
+
+struct fwcam_info {
+ uint32_t formats; /* V_FORMAT_INQ bitmask */
+ uint32_t basic_func; /* BASIC_FUNC_INQ */
+ uint32_t features_hi; /* FEATURE_HI_INQ */
+ uint32_t features_lo; /* FEATURE_LO_INQ */
+ uint8_t cur_format;
+ uint8_t cur_mode;
+ uint8_t cur_framerate;
+ uint8_t state; /* FWCAM_STATE_* */
+ uint32_t frame_size;
+ uint32_t frame_dropped;
+ uint8_t iso_channel; /* active ISO receive channel */
+ uint8_t _pad[3];
+};
+
+#define FWCAM_GMODE _IOR('C', 1, struct fwcam_mode)
+#define FWCAM_SMODE _IOWR('C', 2, struct fwcam_mode)
+#define FWCAM_GFEAT _IOWR('C', 3, struct fwcam_feature)
+#define FWCAM_SFEAT _IOW('C', 4, struct fwcam_feature)
+#define FWCAM_GINFO _IOR('C', 5, struct fwcam_info)
+
+/* fwcam state values (visible to userland via fwcam_info.state) */
+#define FWCAM_STATE_IDLE 0
+#define FWCAM_STATE_PROBED 1
+#define FWCAM_STATE_STREAMING 2
+#define FWCAM_STATE_DETACHING 3
+
+/*
+ * Internal constants
+ */
+#define FWCAM_MAX_FRAME_SIZE (640 * 480 * 3) /* 921,600 (RGB24) */
+#define FWCAM_ISO_NCHUNK 256 /* receive DMA chunks */
+#define FWCAM_ISO_PKTSIZE 2048 /* max iso packet size (MCLBYTES) */
+
+#ifdef _KERNEL
+struct fwcam_softc {
+ struct firewire_dev_comm fd; /* must be first */
+ struct mtx mtx;
+ struct cdev *cdev;
+
+ /* Remote camera node */
+ struct fw_device *fwdev;
+
+ /* IIDC command register addressing */
+ uint16_t cmd_hi; /* always 0xffff */
+ uint32_t cmd_lo; /* 0xf0000000 | (base << 2) */
+
+ /* Capabilities from INQ registers */
+ uint32_t formats; /* V_FORMAT_INQ */
+ uint32_t basic_func; /* BASIC_FUNC_INQ */
+ uint32_t features_hi; /* FEATURE_HI_INQ */
+ uint32_t features_lo; /* FEATURE_LO_INQ */
+
+ /* Current settings */
+ uint8_t cur_format;
+ uint8_t cur_mode;
+ uint8_t cur_framerate;
+ uint8_t iso_channel;
+ uint8_t iso_speed;
+
+ /* Deferred probe task */
+ struct task probe_task;
+
+ /* Isochronous receive */
+ int dma_ch; /* IR DMA channel, -1 if none */
+ int iso_active; /* iso_input running */
+
+ /* Frame assembly (double buffer) */
+ uint8_t *frame_buf; /* frame being assembled */
+ uint8_t *read_buf; /* completed frame for read() */
+ uint32_t frame_size; /* expected frame size (bytes) */
+ uint32_t frame_offset; /* write position in frame_buf */
+ int frame_ready; /* read_buf has valid frame */
+ int read_in_progress; /* uiomove active on read_buf */
+ int frame_dropped; /* dropped frame count */
+ int open_count; /* cdev open count */
+ struct selinfo rsel; /* poll/select/kqueue */
+
+ /* State: one of FWCAM_STATE_* */
+ int state;
+};
+
+#define FWCAM_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define FWCAM_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+
+#endif /* _KERNEL */
+
+#endif /* _DEV_FIREWIRE_FWCAM_H_ */
diff --git a/sys/dev/firewire/fwcam.c b/sys/dev/firewire/fwcam.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/firewire/fwcam.c
@@ -0,0 +1,1050 @@
+/*
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * fwcam(4) - IIDC 1394-based Digital Camera driver
+ *
+ * Implements the IIDC v1.30 specification (TA Document 1999023) for
+ * FireWire digital cameras.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#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 <dev/firewire/firewire.h>
+#include <dev/firewire/firewirereg.h>
+#include <dev/firewire/iec13213.h>
+#include <dev/firewire/fwcam.h>
+#include <dev/firewire/fw_helpers.h>
+
+static MALLOC_DEFINE(M_FWCAM, "fwcam", "IIDC FireWire Camera");
+
+static int debug = 0;
+static int iso_channel = 0;
+SYSCTL_DECL(_hw_firewire);
+static SYSCTL_NODE(_hw_firewire, OID_AUTO, fwcam, CTLFLAG_RD | CTLFLAG_MPSAFE,
+ 0, "IIDC Camera");
+SYSCTL_INT(_hw_firewire_fwcam, OID_AUTO, debug, CTLFLAG_RWTUN, &debug, 0,
+ "fwcam debug level");
+SYSCTL_INT(_hw_firewire_fwcam, OID_AUTO, iso_channel, CTLFLAG_RWTUN,
+ &iso_channel, 0, "ISO channel for isochronous receive (default 0)");
+
+#define FWCAM_DEBUG(lev, fmt, ...) \
+ do { \
+ if (debug >= (lev)) \
+ printf("fwcam: " fmt, ## __VA_ARGS__); \
+ } while (0)
+
+static void fwcam_identify(driver_t *, device_t);
+static int fwcam_probe(device_t);
+static int fwcam_attach(device_t);
+static int fwcam_detach(device_t);
+static void fwcam_post_busreset(void *);
+static void fwcam_probe_task(void *, int);
+static void fwcam_post_explore(void *);
+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 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 uint32_t
+fwcam_find_iidc(struct fw_device *fwdev)
+{
+ struct crom_context cc;
+ struct csrreg *reg;
+ uint32_t cmd_base;
+
+ if (!crom_has_specver(fwdev->csrrom, CSRVAL_1394TA, CSR_PROTCAM130) &&
+ !crom_has_specver(fwdev->csrrom, CSRVAL_1394TA, CSR_PROTCAM120) &&
+ !crom_has_specver(fwdev->csrrom, CSRVAL_1394TA, CSR_PROTCAM104))
+ return (0);
+
+ cmd_base = 0;
+ crom_init_context(&cc, fwdev->csrrom);
+ reg = crom_search_key(&cc, IIDC_CROM_CMD_BASE);
+ if (reg != NULL && reg->val != 0)
+ cmd_base = reg->val;
+
+ return (cmd_base);
+}
+
+static int
+fwcam_read_quadlet(struct fwcam_softc *sc, uint32_t offset, uint32_t *val)
+{
+ uint16_t dst;
+ uint8_t spd;
+ int err;
+
+ FWCAM_LOCK(sc);
+ if (sc->fwdev == NULL) {
+ FWCAM_UNLOCK(sc);
+ return (ENXIO);
+ }
+ dst = FWLOCALBUS | sc->fwdev->dst;
+ spd = min(sc->fwdev->speed, FWSPD_S400);
+ FWCAM_UNLOCK(sc);
+
+ err = fw_read_quadlet(sc->fd.fc, M_FWCAM, dst, spd,
+ sc->cmd_hi, sc->cmd_lo + offset, val);
+ if (err)
+ FWCAM_DEBUG(1, "read_quadlet: offset=0x%x err=%d\n",
+ offset, err);
+ return (err);
+}
+
+static int
+fwcam_write_quadlet(struct fwcam_softc *sc, uint32_t offset, uint32_t val)
+{
+ uint16_t dst;
+ uint8_t spd;
+ int err;
+
+ FWCAM_LOCK(sc);
+ if (sc->fwdev == NULL) {
+ FWCAM_UNLOCK(sc);
+ return (ENXIO);
+ }
+ dst = FWLOCALBUS | sc->fwdev->dst;
+ spd = min(sc->fwdev->speed, FWSPD_S400);
+ FWCAM_UNLOCK(sc);
+
+ err = fw_write_quadlet(sc->fd.fc, M_FWCAM, dst, spd,
+ sc->cmd_hi, sc->cmd_lo + offset, val);
+ if (err)
+ FWCAM_DEBUG(1, "write_quadlet: offset=0x%x err=%d\n",
+ offset, err);
+ return (err);
+}
+
+static const char *
+fwcam_format_name(int format)
+{
+ static const char *names[] = {
+ "VGA (Format_0)",
+ "Super VGA 1 (Format_1)",
+ "Super VGA 2 (Format_2)",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Still Image (Format_6)",
+ "Partial Image (Format_7)",
+ };
+
+ if (format >= 0 && format <= 7)
+ return (names[format]);
+ return ("Unknown");
+}
+
+static int
+fwcam_read_capabilities(struct fwcam_softc *sc)
+{
+ uint32_t val;
+ int err, i;
+
+ err = fwcam_read_quadlet(sc, IIDC_V_FORMAT_INQ, &sc->formats);
+ if (err) {
+ device_printf(sc->fd.dev,
+ "failed to read V_FORMAT_INQ: %d\n", err);
+ return (err);
+ }
+
+ err = fwcam_read_quadlet(sc, IIDC_BASIC_FUNC_INQ, &sc->basic_func);
+ if (err) {
+ device_printf(sc->fd.dev,
+ "failed to read BASIC_FUNC_INQ: %d\n", err);
+ return (err);
+ }
+
+ err = fwcam_read_quadlet(sc, IIDC_FEATURE_HI_INQ, &sc->features_hi);
+ if (err)
+ sc->features_hi = 0;
+
+ err = fwcam_read_quadlet(sc, IIDC_FEATURE_LO_INQ, &sc->features_lo);
+ if (err)
+ sc->features_lo = 0;
+
+ return (0);
+}
+
+static int
+fwcam_power_on(struct fwcam_softc *sc)
+{
+ uint32_t val;
+ int err, retries;
+
+ err = fwcam_read_quadlet(sc, IIDC_BASIC_FUNC_INQ, &val);
+ if (err) {
+ device_printf(sc->fd.dev,
+ "cannot read BASIC_FUNC_INQ: %d\n", err);
+ return (err);
+ }
+
+ if ((val & IIDC_CAM_POWER_CTRL) == 0) {
+ FWCAM_DEBUG(1, "no power control, assuming powered\n");
+ return (0);
+ }
+
+ err = fwcam_read_quadlet(sc, IIDC_CAMERA_POWER, &val);
+ if (err == 0 && (val & IIDC_POWER_ON)) {
+ FWCAM_DEBUG(1, "camera already powered on\n");
+ return (0);
+ }
+
+ err = fwcam_write_quadlet(sc, IIDC_CAMERA_POWER, IIDC_POWER_ON);
+ if (err) {
+ device_printf(sc->fd.dev,
+ "failed to write CAMERA_POWER: %d\n", err);
+ return (err);
+ }
+
+ for (retries = 0; retries < 50; retries++) {
+ pause("fwcampw", hz / 10);
+
+ if (sc->state == FWCAM_STATE_DETACHING)
+ return (ENXIO);
+
+ err = fwcam_read_quadlet(sc, IIDC_CAMERA_POWER, &val);
+ if (err)
+ continue; /* read may fail while powering up */
+
+ if (val & IIDC_POWER_ON)
+ return (0);
+ }
+
+ device_printf(sc->fd.dev, "camera power-on timeout (5s)\n");
+ return (ETIMEDOUT);
+}
+
+static void
+fwcam_probe_task(void *arg, int pending __unused)
+{
+ struct fwcam_softc *sc = (struct fwcam_softc *)arg;
+ int err;
+
+ if (sc->state == FWCAM_STATE_DETACHING || sc->fwdev == NULL)
+ return;
+
+ err = fwcam_power_on(sc);
+ if (err) {
+ device_printf(sc->fd.dev,
+ "power-on failed (%d), trying to read anyway\n", err);
+ }
+
+ if (sc->state == FWCAM_STATE_DETACHING)
+ return;
+
+ if (fwcam_read_capabilities(sc) == 0) {
+ uint32_t val;
+
+ if (fwcam_read_quadlet(sc, IIDC_CUR_V_FORMAT, &val) == 0)
+ sc->cur_format = (val >> 29) & 0x7;
+ if (fwcam_read_quadlet(sc, IIDC_CUR_V_MODE, &val) == 0)
+ sc->cur_mode = (val >> 29) & 0x7;
+ if (fwcam_read_quadlet(sc, IIDC_CUR_V_FRM_RATE, &val) == 0)
+ sc->cur_framerate = (val >> 29) & 0x7;
+
+ FWCAM_LOCK(sc);
+ if (sc->state == FWCAM_STATE_DETACHING) {
+ FWCAM_UNLOCK(sc);
+ return;
+ }
+ sc->state = FWCAM_STATE_PROBED;
+ FWCAM_UNLOCK(sc);
+
+ if (sc->open_count > 0 &&
+ sc->state != FWCAM_STATE_DETACHING)
+ fwcam_iso_start(sc);
+ }
+}
+
+/*
+ * Compute expected frame size for current format/mode.
+ * Format_0 (VGA) modes:
+ * Mode 0: 160x120 YUV444 = 160*120*3 = 57600
+ * Mode 1: 320x240 YUV422 = 320*240*2 = 153600
+ * Mode 2: 640x480 YUV411 = 640*480*3/2 = 460800
+ * Mode 3: 640x480 YUV422 = 640*480*2 = 614400
+ * Mode 4: 640x480 RGB8 = 640*480*3 = 921600
+ * Mode 5: 640x480 Mono8 = 640*480 = 307200
+ */
+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]);
+
+ /* Default to largest VGA mode */
+ return (FWCAM_MAX_FRAME_SIZE);
+}
+
+static int
+fwcam_iso_start(struct fwcam_softc *sc)
+{
+ struct firewire_comm *fc = sc->fd.fc;
+ struct fw_xferq *xferq;
+ uint32_t val;
+ int dma_ch, err;
+
+ mtx_assert(&sc->mtx, MA_NOTOWNED);
+
+ FWCAM_LOCK(sc);
+ if (sc->dma_ch >= 0 || sc->state == FWCAM_STATE_STREAMING) {
+ FWCAM_UNLOCK(sc);
+ return (0); /* already running or starting */
+ }
+ if (sc->state == FWCAM_STATE_DETACHING) {
+ FWCAM_UNLOCK(sc);
+ return (ENXIO);
+ }
+ FWCAM_UNLOCK(sc);
+
+ dma_ch = fw_open_isodma(fc, 0);
+ if (dma_ch < 0) {
+ device_printf(sc->fd.dev, "no IR DMA channel available\n");
+ return (EBUSY);
+ }
+
+ FWCAM_LOCK(sc);
+ if (sc->dma_ch >= 0) {
+ FWCAM_UNLOCK(sc);
+ fc->ir[dma_ch]->flag &= ~FWXFERQ_OPEN;
+ return (0);
+ }
+ FWCAM_UNLOCK(sc);
+
+ xferq = fc->ir[dma_ch];
+ xferq->flag |= FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_STREAM;
+
+ sc->iso_channel = (uint8_t)(iso_channel & FWXFERQ_CHTAGMASK);
+ xferq->flag &= ~FWXFERQ_CHTAGMASK;
+ xferq->flag |= sc->iso_channel & FWXFERQ_CHTAGMASK;
+
+ xferq->sc = (caddr_t)sc;
+ xferq->hand = fwcam_iso_input;
+ xferq->bnchunk = FWCAM_ISO_NCHUNK;
+ xferq->bnpacket = 1;
+ xferq->psize = FWCAM_ISO_PKTSIZE;
+ xferq->queued = 0;
+ xferq->buf = NULL;
+
+ xferq->bulkxfer = malloc(sizeof(struct fw_bulkxfer) * xferq->bnchunk,
+ M_FWCAM, M_WAITOK | M_ZERO);
+
+ fw_iso_init_chunks(xferq);
+
+ 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;
+
+ val = ((uint32_t)sc->iso_channel << IIDC_ISO_CH_SHIFT) |
+ ((uint32_t)sc->iso_speed << IIDC_ISO_SPEED_SHIFT);
+ err = fwcam_write_quadlet(sc, IIDC_ISO_CHANNEL, val);
+ if (err) {
+ device_printf(sc->fd.dev,
+ "failed to set ISO_CHANNEL: %d\n", err);
+ goto fail;
+ }
+
+ err = fwcam_write_quadlet(sc, IIDC_ISO_EN, IIDC_ISO_EN_ON);
+ if (err) {
+ device_printf(sc->fd.dev,
+ "failed to enable ISO: %d\n", err);
+ goto fail;
+ }
+
+ err = fc->irx_enable(fc, dma_ch);
+ if (err) {
+ device_printf(sc->fd.dev,
+ "failed to start IR DMA: %d\n", err);
+ fwcam_write_quadlet(sc, IIDC_ISO_EN, 0);
+ goto fail;
+ }
+
+ FWCAM_LOCK(sc);
+ if (sc->state == FWCAM_STATE_DETACHING) {
+ FWCAM_UNLOCK(sc);
+ fc->irx_disable(fc, dma_ch);
+ fwcam_write_quadlet(sc, IIDC_ISO_EN, 0);
+ err = ENXIO;
+ goto fail;
+ }
+ sc->dma_ch = dma_ch;
+ sc->state = FWCAM_STATE_STREAMING;
+ FWCAM_UNLOCK(sc);
+
+ return (0);
+
+fail:
+ fw_iso_free_chunks(xferq, M_FWCAM);
+ xferq->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_OPEN | FWXFERQ_STREAM |
+ FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK);
+ 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);
+}
+
+static void
+fwcam_iso_stop(struct fwcam_softc *sc)
+{
+ struct firewire_comm *fc = sc->fd.fc;
+ struct fw_xferq *xferq;
+ int dma_ch;
+
+ FWCAM_LOCK(sc);
+ dma_ch = sc->dma_ch;
+ if (dma_ch < 0) {
+ FWCAM_UNLOCK(sc);
+ return;
+ }
+ sc->dma_ch = -1; /* claim ownership of teardown */
+ FWCAM_UNLOCK(sc);
+
+ xferq = fc->ir[dma_ch];
+
+ if (xferq->flag & FWXFERQ_RUNNING)
+ fc->irx_disable(fc, dma_ch);
+
+ if (sc->fwdev != NULL)
+ fwcam_write_quadlet(sc, IIDC_ISO_EN, 0);
+
+ 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 |
+ FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK);
+ xferq->hand = NULL;
+
+ 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_iso_input(struct fw_xferq *xferq)
+{
+ struct fwcam_softc *sc = (struct fwcam_softc *)xferq->sc;
+ struct fw_bulkxfer *sxfer;
+ struct fw_pkt *fp;
+ struct mbuf *m;
+ uint8_t *payload;
+ uint32_t plen;
+ uint8_t *tmp;
+ int dma_ch;
+
+ FWCAM_LOCK(sc);
+ dma_ch = sc->dma_ch;
+ if (dma_ch < 0 || sc->frame_buf == NULL) {
+ FWCAM_UNLOCK(sc);
+ return;
+ }
+ sc->iso_active = 1;
+ FWCAM_UNLOCK(sc);
+
+ while ((sxfer = STAILQ_FIRST(&xferq->stvalid)) != NULL) {
+ STAILQ_REMOVE_HEAD(&xferq->stvalid, link);
+
+ m = fw_iso_dequeue(xferq, sxfer, sc->fd.fc);
+ if (m == NULL)
+ continue;
+
+ fp = mtod(m, struct fw_pkt *);
+ plen = fp->mode.stream.len;
+ if (plen == 0) {
+ m_freem(m);
+ continue; /* empty packet (padding) */
+ }
+
+ 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++;
+ }
+ }
+ sc->frame_offset = 0;
+ }
+
+ if (m->m_len < 4) {
+ m_freem(m);
+ continue;
+ }
+ payload = mtod(m, uint8_t *) + 4;
+ if (plen > (uint32_t)(m->m_len - 4))
+ plen = m->m_len - 4;
+
+ if (sc->frame_offset + plen <= sc->frame_size) {
+ memcpy(sc->frame_buf + sc->frame_offset, payload, plen);
+ sc->frame_offset += plen;
+ } else {
+ sc->frame_dropped++;
+ sc->frame_offset = 0;
+ }
+
+ m_freem(m);
+ }
+
+ fw_iso_rearm_done(xferq, sc->fd.fc, &sc->mtx, &sc->iso_active,
+ &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 = 0;
+
+ FWCAM_LOCK(sc);
+ if (sc->state == FWCAM_STATE_DETACHING) {
+ FWCAM_UNLOCK(sc);
+ return (ENXIO);
+ }
+ if (sc->state != FWCAM_STATE_PROBED &&
+ sc->state != FWCAM_STATE_STREAMING) {
+ FWCAM_UNLOCK(sc);
+ return (EAGAIN); /* not yet probed */
+ }
+
+ sc->open_count++;
+ if (sc->open_count == 1 && sc->state == FWCAM_STATE_PROBED) {
+ FWCAM_UNLOCK(sc);
+ err = fwcam_iso_start(sc);
+ if (err) {
+ FWCAM_LOCK(sc);
+ sc->open_count--;
+ FWCAM_UNLOCK(sc);
+ }
+ } else {
+ FWCAM_UNLOCK(sc);
+ }
+ return (err);
+}
+
+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);
+ 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,
+ [FWCAM_FEAT_SHARPNESS] = IIDC_SHARPNESS_INQ,
+ [FWCAM_FEAT_WHITE_BALANCE] = IIDC_WHITE_BAL_INQ,
+ [FWCAM_FEAT_HUE] = IIDC_HUE_INQ,
+ [FWCAM_FEAT_SATURATION] = IIDC_SATURATION_INQ,
+ [FWCAM_FEAT_GAMMA] = IIDC_GAMMA_INQ,
+ [FWCAM_FEAT_SHUTTER] = IIDC_SHUTTER_INQ,
+ [FWCAM_FEAT_GAIN] = IIDC_GAIN_INQ,
+ [FWCAM_FEAT_IRIS] = IIDC_IRIS_INQ,
+ [FWCAM_FEAT_FOCUS] = IIDC_FOCUS_INQ,
+ [FWCAM_FEAT_TEMPERATURE] = IIDC_TEMPERATURE_INQ,
+ [FWCAM_FEAT_TRIGGER] = IIDC_TRIGGER_INQ,
+};
+
+static const uint32_t fwcam_feat_ctrl[] = {
+ [FWCAM_FEAT_BRIGHTNESS] = IIDC_BRIGHTNESS,
+ [FWCAM_FEAT_AUTO_EXPOSURE] = IIDC_AUTO_EXPOSURE,
+ [FWCAM_FEAT_SHARPNESS] = IIDC_SHARPNESS,
+ [FWCAM_FEAT_WHITE_BALANCE] = IIDC_WHITE_BALANCE,
+ [FWCAM_FEAT_HUE] = IIDC_HUE,
+ [FWCAM_FEAT_SATURATION] = IIDC_SATURATION,
+ [FWCAM_FEAT_GAMMA] = IIDC_GAMMA,
+ [FWCAM_FEAT_SHUTTER] = IIDC_SHUTTER,
+ [FWCAM_FEAT_GAIN] = IIDC_GAIN,
+ [FWCAM_FEAT_IRIS] = IIDC_IRIS,
+ [FWCAM_FEAT_FOCUS] = IIDC_FOCUS,
+ [FWCAM_FEAT_TEMPERATURE] = IIDC_TEMPERATURE,
+ [FWCAM_FEAT_TRIGGER] = IIDC_TRIGGER_MODE,
+ [FWCAM_FEAT_ZOOM] = IIDC_ZOOM,
+ [FWCAM_FEAT_PAN] = IIDC_PAN,
+ [FWCAM_FEAT_TILT] = IIDC_TILT,
+};
+
+static int
+fwcam_get_feature(struct fwcam_softc *sc, struct fwcam_feature *feat)
+{
+ uint32_t inq, ctrl;
+ int err;
+
+ if (feat->id >= FWCAM_FEAT_MAX)
+ return (EINVAL);
+
+ feat->flags = 0;
+ feat->min = 0;
+ feat->max = 0;
+ if (feat->id < nitems(fwcam_feat_inq) &&
+ fwcam_feat_inq[feat->id] != 0) {
+ err = fwcam_read_quadlet(sc, fwcam_feat_inq[feat->id], &inq);
+ if (err)
+ return (err);
+
+ if (inq & (1 << 31))
+ feat->flags |= FWCAM_FEATF_PRESENT;
+ if (inq & (1 << 28))
+ feat->flags |= FWCAM_FEATF_ONOFF;
+ if (inq & (1 << 27))
+ feat->flags |= FWCAM_FEATF_AUTO;
+ if (inq & (1 << 26))
+ feat->flags |= FWCAM_FEATF_MANUAL;
+ feat->min = (inq >> 12) & 0xfff;
+ feat->max = inq & 0xfff;
+ }
+
+ if (feat->id >= nitems(fwcam_feat_ctrl) ||
+ fwcam_feat_ctrl[feat->id] == 0)
+ return (EINVAL);
+
+ err = fwcam_read_quadlet(sc, fwcam_feat_ctrl[feat->id], &ctrl);
+ if (err)
+ return (err);
+
+ feat->value = ctrl & 0xfff;
+ /* White balance has U/B in bits [20:31] and V/R in bits [8:19] */
+ if (feat->id == FWCAM_FEAT_WHITE_BALANCE)
+ feat->value2 = (ctrl >> 12) & 0xfff;
+ else
+ feat->value2 = 0;
+
+ return (0);
+}
+
+static int
+fwcam_set_feature(struct fwcam_softc *sc, struct fwcam_feature *feat)
+{
+ uint32_t ctrl, val;
+ int err;
+
+ if (feat->id >= FWCAM_FEAT_MAX)
+ return (EINVAL);
+ if (feat->id >= nitems(fwcam_feat_ctrl) ||
+ fwcam_feat_ctrl[feat->id] == 0)
+ return (EINVAL);
+
+ err = fwcam_read_quadlet(sc, fwcam_feat_ctrl[feat->id], &ctrl);
+ if (err)
+ return (err);
+
+ if (feat->id == FWCAM_FEAT_WHITE_BALANCE) {
+ /* White balance: value=V/R (low 12), value2=U/B (high 12) */
+ val = (ctrl & 0xff000000) |
+ ((feat->value2 & 0xfff) << 12) |
+ (feat->value & 0xfff);
+ } else {
+ /* Preserve upper bits, set new value in lower 12 */
+ val = (ctrl & 0xfffff000) | (feat->value & 0xfff);
+ }
+
+ return (fwcam_write_quadlet(sc, fwcam_feat_ctrl[feat->id], val));
+}
+
+static int
+fwcam_cdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
+ int fflag, struct thread *td)
+{
+ 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)
+ 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);
+
+ case FWCAM_SMODE:
+ {
+ int was_streaming = 0;
+
+ mode = (struct fwcam_mode *)data;
+ if (mode->format > 7 || mode->mode > 7 || mode->framerate > 7)
+ return (EINVAL);
+
+ if (mode->format != IIDC_FMT_VGA)
+ return (EINVAL);
+ if (!(sc->formats & (1 << (31 - mode->format))))
+ return (EINVAL);
+
+ FWCAM_LOCK(sc);
+ if (sc->state == FWCAM_STATE_DETACHING) {
+ FWCAM_UNLOCK(sc);
+ return (ENXIO);
+ }
+ 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;
+ }
+ FWCAM_UNLOCK(sc);
+
+ err = fwcam_write_quadlet(sc, IIDC_CUR_V_FORMAT,
+ (uint32_t)mode->format << 29);
+ if (err == 0)
+ err = fwcam_write_quadlet(sc, IIDC_CUR_V_MODE,
+ (uint32_t)mode->mode << 29);
+ if (err == 0)
+ err = fwcam_write_quadlet(sc, IIDC_CUR_V_FRM_RATE,
+ (uint32_t)mode->framerate << 29);
+
+ 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 (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);
+
+ default:
+ return (ENOTTY);
+ }
+}
+
+static void
+fwcam_post_explore(void *arg)
+{
+ struct fwcam_softc *sc = (struct fwcam_softc *)arg;
+ struct fw_device *fwdev;
+ uint32_t cmd_base;
+ int was_streaming, err;
+
+ FWCAM_LOCK(sc);
+
+ if (sc->state == FWCAM_STATE_DETACHING) {
+ FWCAM_UNLOCK(sc);
+ return;
+ }
+
+ if (sc->fwdev != NULL) {
+ STAILQ_FOREACH(fwdev, &sc->fd.fc->devices, link) {
+ if (fwdev == sc->fwdev &&
+ fwdev->status == FWDEVATTACHED)
+ break;
+ }
+ if (fwdev == NULL) {
+ was_streaming = (sc->state == FWCAM_STATE_STREAMING);
+ device_printf(sc->fd.dev, "camera disconnected%s\n",
+ was_streaming ? " (was streaming)" : "");
+ sc->fwdev = NULL;
+ sc->state = FWCAM_STATE_IDLE;
+ wakeup(sc); /* unblock readers */
+ selwakeup(&sc->rsel);
+ FWCAM_UNLOCK(sc);
+
+ if (was_streaming)
+ fwcam_iso_stop(sc);
+ FWCAM_LOCK(sc);
+ }
+ }
+
+ if (sc->fwdev == NULL) {
+ STAILQ_FOREACH(fwdev, &sc->fd.fc->devices, link) {
+ if (fwdev->status != FWDEVATTACHED)
+ continue;
+
+ cmd_base = fwcam_find_iidc(fwdev);
+ if (cmd_base == 0)
+ continue;
+
+ sc->fwdev = fwdev;
+ sc->cmd_hi = 0xffff;
+ sc->cmd_lo = 0xf0000000 | (cmd_base << 2);
+
+ FWCAM_UNLOCK(sc);
+ err = taskqueue_enqueue(taskqueue_thread,
+ &sc->probe_task);
+ if (err)
+ device_printf(sc->fd.dev,
+ "taskqueue_enqueue failed: %d\n", err);
+ return;
+ }
+ }
+
+ FWCAM_UNLOCK(sc);
+}
+
+static void
+fwcam_post_busreset(void *arg __unused)
+{
+
+}
+
+static void
+fwcam_identify(driver_t *driver, device_t parent)
+{
+
+ if (device_find_child(parent, "fwcam", DEVICE_UNIT_ANY) == NULL)
+ BUS_ADD_CHILD(parent, 0, "fwcam", DEVICE_UNIT_ANY);
+}
+
+static int
+fwcam_probe(device_t dev)
+{
+
+ device_set_desc(dev, "IIDC Digital Camera over FireWire");
+ return (0);
+}
+
+static int
+fwcam_attach(device_t dev)
+{
+ struct fwcam_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->fd.dev = dev;
+ sc->fd.fc = device_get_ivars(dev);
+ mtx_init(&sc->mtx, "fwcam", NULL, MTX_DEF);
+
+ sc->fwdev = NULL;
+ sc->state = FWCAM_STATE_IDLE;
+ sc->dma_ch = -1;
+ sc->iso_active = 0;
+ sc->open_count = 0;
+ knlist_init_mtx(&sc->rsel.si_note, &sc->mtx);
+ TASK_INIT(&sc->probe_task, 0, fwcam_probe_task, sc);
+
+ sc->cdev = make_dev(&fwcam_cdevsw, device_get_unit(dev),
+ UID_ROOT, GID_OPERATOR, 0660, "fwcam%d", device_get_unit(dev));
+ sc->cdev->si_drv1 = sc;
+
+ sc->fd.post_busreset = fwcam_post_busreset;
+ sc->fd.post_explore = fwcam_post_explore;
+
+ fwcam_post_explore(sc);
+
+ return (0);
+}
+
+static int
+fwcam_detach(device_t dev)
+{
+ struct fwcam_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ FWCAM_LOCK(sc);
+ sc->state = FWCAM_STATE_DETACHING;
+ wakeup(sc); /* wake any sleeping readers */
+ FWCAM_UNLOCK(sc);
+
+ taskqueue_drain(taskqueue_thread, &sc->probe_task);
+ fwcam_iso_stop(sc);
+
+ if (sc->cdev != NULL)
+ destroy_dev(sc->cdev);
+
+ seldrain(&sc->rsel);
+ knlist_destroy(&sc->rsel.si_note);
+
+ FWCAM_LOCK(sc);
+ sc->fwdev = NULL;
+ FWCAM_UNLOCK(sc);
+
+ mtx_destroy(&sc->mtx);
+
+ return (0);
+}
+
+static device_method_t fwcam_methods[] = {
+ DEVMETHOD(device_identify, fwcam_identify),
+ DEVMETHOD(device_probe, fwcam_probe),
+ DEVMETHOD(device_attach, fwcam_attach),
+ DEVMETHOD(device_detach, fwcam_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t fwcam_driver = {
+ "fwcam",
+ fwcam_methods,
+ sizeof(struct fwcam_softc),
+};
+
+DRIVER_MODULE(fwcam, firewire, fwcam_driver, 0, 0);
+MODULE_VERSION(fwcam, 1);
+MODULE_DEPEND(fwcam, firewire, 1, 1, 1);
diff --git a/sys/modules/firewire/fwcam/Makefile b/sys/modules/firewire/fwcam/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/firewire/fwcam/Makefile
@@ -0,0 +1,11 @@
+# Makefile for the IIDC 1394-based Digital Camera driver
+
+.PATH: ${SRCTOP}/sys/dev/firewire
+
+KMOD = fwcam
+SRCS = bus_if.h device_if.h \
+ fwcam.c fwcam.h fw_helpers.h \
+ firewire.h firewirereg.h \
+ iec13213.h
+
+.include <bsd.kmod.mk>

File Metadata

Mime Type
text/plain
Expires
Wed, Jun 24, 3:26 AM (14 h, 33 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34271767
Default Alt Text
D57685.id180112.diff (38 KB)

Event Timeline