diff --git a/share/man/man4/fwcam.4 b/share/man/man4/fwcam.4 index c375d1eab9b1..1c66c96d622f 100644 --- a/share/man/man4/fwcam.4 +++ b/share/man/man4/fwcam.4 @@ -1,116 +1,129 @@ .\" .\" Copyright (c) 2026 Abdelkader Boudih .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" -.Dd June 19, 2026 +.Dd July 12, 2026 .Dt FWCAM 4 .Os .Sh NAME .Nm fwcam .Nd IIDC FireWire 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). +Camera) v1.04/v1.20/v1.30 cameras attached via IEEE 1394 (FireWire). +.Pp +The driver attaches to unit directories in the device CSR configuration ROM +with +.Va unit_spec_id +0x00a02d (1394 Trade Association) and +.Va unit_sw_version +matching IIDC v1.04, v1.20, or v1.30. +The IIDC command base address is located within the unit directory +or a nested logical unit directory. +.Pp +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 . .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: .Bl -bullet -compact .It Apple iSight (FireWire cylinder), EUI-64 000a270004135e5d, Format_0 modes 1\(en3 .El .Sh IOCTLS .Bl -tag -width "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 "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 "/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/dev/firewire/fwcam.c b/sys/dev/firewire/fwcam.c index 234dd41eedf1..b2381c4be3f9 100644 --- a/sys/dev/firewire/fwcam.c +++ b/sys/dev/firewire/fwcam.c @@ -1,1103 +1,1099 @@ /* * Copyright (c) 2026 Abdelkader Boudih * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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", }; +/* + * Search a CSR directory for the IIDC command base register (key 0x40). + * The iSight places cmd_base inside a logical_unit_directory nested + * within the IIDC unit directory, so we recurse into sub-directories + * up to max_depth levels. + */ static uint32_t -fwcam_find_iidc(struct fw_device *fwdev) +fwcam_search_dir_cmd_base(uint32_t *csrrom, uint32_t dir_qoff, + uint32_t rom_quads, int max_depth) { - struct crom_context cc; + struct csrdirectory *dir; struct csrreg *reg; - uint32_t cmd_base; + uint32_t dir_len, sub_qoff, val; + int i; - 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)) + if (dir_qoff >= rom_quads || max_depth <= 0) + return (0); + dir = (struct csrdirectory *)&csrrom[dir_qoff]; + dir_len = dir->crc_len; + if (dir_qoff + 1 + dir_len > rom_quads) 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; + for (i = 0; i < (int)dir_len; i++) { + if (dir_qoff + 1 + i >= rom_quads) + break; + reg = &dir->entry[i]; + if (reg->key == IIDC_CROM_CMD_BASE && reg->val != 0) + return (reg->val); + if ((reg->key & CSRTYPE_MASK) == CSRTYPE_D) { + sub_qoff = dir_qoff + 1 + i + reg->val; + val = fwcam_search_dir_cmd_base(csrrom, sub_qoff, + rom_quads, max_depth - 1); + if (val != 0) + return (val); + } + } + return (0); +} + +/* + * Extract IIDC command base register from the unit directory. + * Searches the unit directory and any sub-directories (the iSight + * places cmd_base inside a logical_unit_directory). + */ +static uint32_t +fwcam_find_iidc_cmd_base(struct fw_unit *unit) +{ - return (cmd_base); + return (fwcam_search_dir_cmd_base(unit->fwdev->csrrom, + unit->dir_offset / 4, CSRROMSIZE / 4, 2)); } 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 int fwcam_read_capabilities(struct fwcam_softc *sc) { int err, f, m; 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); } /* Read mode and rate inquiry registers for each supported format */ for (f = 0; f < 8; f++) { if (!(sc->formats & (1 << (31 - f)))) { sc->modes[f] = 0; continue; } err = fwcam_read_quadlet(sc, IIDC_V_MODE_INQ(f), &sc->modes[f]); if (err) { sc->modes[f] = 0; continue; } for (m = 0; m < 8; m++) { if (!(sc->modes[f] & (1 << (31 - m)))) { sc->rates[f][m] = 0; continue; } err = fwcam_read_quadlet(sc, IIDC_V_RATE_INQ(f, m), &sc->rates[f][m]); if (err) sc->rates[f][m] = 0; } } 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; + wakeup(sc); FWCAM_UNLOCK(sc); if (sc->dma_ch >= 0 && sc->state != FWCAM_STATE_DETACHING) fwcam_iso_start(sc); + } else { + FWCAM_LOCK(sc); + if (sc->state == FWCAM_STATE_PROBING) + sc->state = FWCAM_STATE_IDLE; + wakeup(sc); + FWCAM_UNLOCK(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; /* IIDC spec s3.1: set video mode registers before ISO enable */ err = fwcam_write_quadlet(sc, IIDC_CUR_V_FORMAT, (uint32_t)sc->cur_format << IIDC_CUR_V_SHIFT); if (err) { device_printf(sc->fd.dev, "failed to set CUR_V_FORMAT: %d\n", err); goto fail; } err = fwcam_write_quadlet(sc, IIDC_CUR_V_MODE, (uint32_t)sc->cur_mode << IIDC_CUR_V_SHIFT); if (err) { device_printf(sc->fd.dev, "failed to set CUR_V_MODE: %d\n", err); goto fail; } err = fwcam_write_quadlet(sc, IIDC_CUR_V_FRM_RATE, (uint32_t)sc->cur_framerate << IIDC_CUR_V_SHIFT); if (err) { device_printf(sc->fd.dev, "failed to set CUR_V_FRM_RATE: %d\n", err); goto fail; } /* Check Vmode_Error_Status - camera rejects ISO_EN on error */ err = fwcam_read_quadlet(sc, IIDC_VMODE_ERR_STATUS, &val); if (err == 0 && (val & IIDC_VMODE_ERROR)) { device_printf(sc->fd.dev, "Vmode_Error_Status set: format=%d mode=%d rate=%d " "speed=%d\n", sc->cur_format, sc->cur_mode, sc->cur_framerate, sc->iso_speed); err = EINVAL; goto fail; } 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 == EIO) { /* * Cameras with a lens cover might power down the sensor * when the cover is closed and reject streaming requests. * Try to re-power and retry once before giving up. */ err = fwcam_power_on(sc); if (err == 0) err = fwcam_write_quadlet(sc, IIDC_ISO_EN, IIDC_ISO_EN_ON); if (err) { device_printf(sc->fd.dev, "ISO enable refused (lens cover closed?)\n"); goto fail; } } else 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; 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 (EAGAIN); /* not yet probed */ + 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, [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 (!(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); 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 << 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 (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); - sc->iso_speed = min(fwdev->speed, FWSPD_S400); - - 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) { + struct fw_unit *unit; + + unit = fw_get_unit(dev); + if (unit == NULL) + return (ENXIO); + + if (unit->spec_id != CSRVAL_1394TA) + return (ENXIO); + if (unit->sw_version != CSR_PROTCAM130 && + unit->sw_version != CSR_PROTCAM120 && + unit->sw_version != CSR_PROTCAM104) + return (ENXIO); device_set_desc(dev, "IIDC Digital Camera over FireWire"); - return (0); + return (BUS_PROBE_DEFAULT); } static int fwcam_attach(device_t dev) { struct fwcam_softc *sc; + struct fw_unit *unit; + uint32_t cmd_base; + + unit = fw_get_unit(dev); + if (unit == NULL || unit->fwdev == NULL) + return (ENXIO); + + cmd_base = fwcam_find_iidc_cmd_base(unit); + if (cmd_base == 0) { + device_printf(dev, "no IIDC command base in unit directory\n"); + return (ENXIO); + } sc = device_get_softc(dev); sc->fd.dev = dev; - sc->fd.fc = device_get_ivars(dev); + sc->fd.fc = fw_get_comm(dev); mtx_init(&sc->mtx, "fwcam", NULL, MTX_DEF); - sc->fwdev = NULL; + sc->fwdev = unit->fwdev; + sc->cmd_hi = 0xffff; + sc->cmd_lo = 0xf0000000 | (cmd_base << 2); + sc->iso_speed = min(unit->fwdev->speed, FWSPD_S400); 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/dev/firewire/fwcam.h b/sys/dev/firewire/fwcam.h index 79b0dd3b5ec6..19139dd74d3d 100644 --- a/sys/dev/firewire/fwcam.h +++ b/sys/dev/firewire/fwcam.h @@ -1,336 +1,338 @@ /* * Copyright (c) 2026 Abdelkader Boudih * * 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 #define IIDC_VMODE_ERR_STATUS 0x628 /* Cur_V_Format / Cur_V_Mode / Cur_V_Frm_Rate register fields */ #define IIDC_CUR_V_SHIFT 29 /* bits [0..2] */ /* Vmode_Error_Status register */ #define IIDC_VMODE_ERROR (1 << 31) /* bit [0]: mode error */ /* 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 +#define FWCAM_STATE_PROBING 1 +#define FWCAM_STATE_PROBED 2 +#define FWCAM_STATE_STREAMING 3 +#define FWCAM_STATE_DETACHING 4 /* IIDC format/mode/rate/feature/state name tables for userland and driver */ #define FWCAM_FMT_NMAX 8 #define FWCAM_RATE_NMAX 8 static const char * const fwcam_fmt_names[FWCAM_FMT_NMAX] = { "VGA (Format_0)", "Super VGA 1 (Format_1)", "Super VGA 2 (Format_2)", "Reserved", "Reserved", "Reserved", "Still Image (Format_6)", "Partial Image (Format_7)", }; static const char * const fwcam_rate_names[FWCAM_RATE_NMAX] = { "1.875 fps", "3.75 fps", "7.5 fps", "15 fps", "30 fps", "60 fps", "120 fps", "240 fps", }; static const char * const fwcam_feat_names[FWCAM_FEAT_MAX] = { [FWCAM_FEAT_BRIGHTNESS] = "brightness", [FWCAM_FEAT_AUTO_EXPOSURE] = "auto_exposure", [FWCAM_FEAT_SHARPNESS] = "sharpness", [FWCAM_FEAT_WHITE_BALANCE] = "white_balance", [FWCAM_FEAT_HUE] = "hue", [FWCAM_FEAT_SATURATION] = "saturation", [FWCAM_FEAT_GAMMA] = "gamma", [FWCAM_FEAT_SHUTTER] = "shutter", [FWCAM_FEAT_GAIN] = "gain", [FWCAM_FEAT_IRIS] = "iris", [FWCAM_FEAT_FOCUS] = "focus", [FWCAM_FEAT_TEMPERATURE] = "temperature", [FWCAM_FEAT_TRIGGER] = "trigger", [FWCAM_FEAT_ZOOM] = "zoom", [FWCAM_FEAT_PAN] = "pan", [FWCAM_FEAT_TILT] = "tilt", }; static const char * const fwcam_state_names[] = { [FWCAM_STATE_IDLE] = "idle", + [FWCAM_STATE_PROBING] = "probing", [FWCAM_STATE_PROBED] = "probed", [FWCAM_STATE_STREAMING] = "streaming", [FWCAM_STATE_DETACHING] = "detaching", }; /* Format_0 (VGA non-compressed) mode geometry */ struct fwcam_fmt0_mode { int w, h; const char *pixfmt; }; #define FWCAM_FMT0_NMODES 7 static const struct fwcam_fmt0_mode fwcam_fmt0_modes[FWCAM_FMT0_NMODES] = { { 160, 120, "YUV444" }, /* mode 0 */ { 320, 240, "YUV422" }, /* mode 1 */ { 640, 480, "YUV411" }, /* mode 2 */ { 640, 480, "YUV422" }, /* mode 3 */ { 640, 480, "RGB8" }, /* mode 4 */ { 640, 480, "Mono8" }, /* mode 5 */ { 640, 480, "Mono16" }, /* mode 6 */ }; /* * 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 */ uint32_t modes[8]; /* V_MODE_INQ per format */ uint32_t rates[8][8]; /* V_RATE_INQ per format/mode */ /* 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_ */