Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F160296091
D57685.id180108.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
40 KB
Referenced Files
None
Subscribers
None
D57685.id180108.diff
View Options
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,1131 @@
+/*
+ * 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/sbuf.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(0, "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(0, "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;
+
+ device_printf(sc->fd.dev, "IIDC camera capabilities:\n");
+ device_printf(sc->fd.dev, " formats: 0x%08x\n", sc->formats);
+
+ for (i = 0; i < 8; i++) {
+ if (sc->formats & (1 << (31 - i))) {
+ err = fwcam_read_quadlet(sc, IIDC_V_MODE_INQ(i), &val);
+ if (err == 0)
+ device_printf(sc->fd.dev,
+ " %s: modes=0x%08x\n",
+ fwcam_format_name(i), val);
+ }
+ }
+
+ device_printf(sc->fd.dev, " basic_func: 0x%08x%s%s%s\n",
+ sc->basic_func,
+ (sc->basic_func & IIDC_CAM_POWER_CTRL) ? " [power]" : "",
+ (sc->basic_func & IIDC_ONE_SHOT_INQ) ? " [one_shot]" : "",
+ (sc->basic_func & IIDC_MULTI_SHOT_INQ) ? " [multi_shot]" : "");
+
+ {
+ struct sbuf sb;
+ char buf[128];
+
+ sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN);
+ sbuf_printf(&sb, "0x%08x", sc->features_hi);
+ if (sc->features_hi & IIDC_HAS_BRIGHTNESS)
+ sbuf_cat(&sb, " brightness");
+ if (sc->features_hi & IIDC_HAS_AUTO_EXPOSURE)
+ sbuf_cat(&sb, " auto_exp");
+ if (sc->features_hi & IIDC_HAS_SHARPNESS)
+ sbuf_cat(&sb, " sharpness");
+ if (sc->features_hi & IIDC_HAS_WHITE_BALANCE)
+ sbuf_cat(&sb, " white_bal");
+ if (sc->features_hi & IIDC_HAS_GAIN)
+ sbuf_cat(&sb, " gain");
+ if (sc->features_hi & IIDC_HAS_SHUTTER)
+ sbuf_cat(&sb, " shutter");
+ if (sc->features_hi & IIDC_HAS_GAMMA)
+ sbuf_cat(&sb, " gamma");
+ if (sc->features_hi & IIDC_HAS_FOCUS)
+ sbuf_cat(&sb, " focus");
+ sbuf_finish(&sb);
+ device_printf(sc->fd.dev, " features_hi: %s\n",
+ sbuf_data(&sb));
+ sbuf_delete(&sb);
+ }
+
+ 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);
+ }
+
+ device_printf(sc->fd.dev, "powering on camera\n");
+ 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) {
+ device_printf(sc->fd.dev,
+ "camera powered on after %dms\n",
+ (retries + 1) * 100);
+ 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;
+
+ FWCAM_DEBUG(1, "probe_task: powering on + reading capabilities\n");
+
+ 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;
+
+ device_printf(sc->fd.dev,
+ "current: format=%d mode=%d rate=%d\n",
+ sc->cur_format, sc->cur_mode, sc->cur_framerate);
+
+ 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) {
+ device_printf(sc->fd.dev,
+ "reconnected, restarting stream\n");
+ fwcam_iso_start(sc);
+ }
+ device_printf(sc->fd.dev, "probe complete\n");
+ }
+}
+
+/*
+ * 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);
+
+ device_printf(sc->fd.dev,
+ "streaming: ch=%d frame_size=%u\n",
+ sc->iso_channel, sc->frame_size);
+ 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;
+
+ device_printf(sc->fd.dev, "streaming stopped (dropped %d frames)\n",
+ sc->frame_dropped);
+}
+
+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_DEBUG(1, "post_explore\n");
+
+ 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);
+
+ device_printf(sc->fd.dev,
+ "IIDC camera found: EUI=%08x%08x "
+ "node=%d speed=S%d cmd_base=0x%08x\n",
+ fwdev->eui.hi, fwdev->eui.lo,
+ fwdev->dst, 100 << fwdev->speed,
+ sc->cmd_lo);
+
+ 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)
+{
+
+ FWCAM_DEBUG(1, "bus reset\n");
+}
+
+static void
+fwcam_identify(driver_t *driver, device_t parent)
+{
+
+ FWCAM_DEBUG(1, "identify\n");
+
+ 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
Details
Attached
Mime Type
text/plain
Expires
Wed, Jun 24, 12:50 AM (9 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34266072
Default Alt Text
D57685.id180108.diff (40 KB)
Attached To
Mode
D57685: fwcam: add IIDC 1394 FireWire camera driver
Attached
Detach File
Event Timeline
Log In to Comment