The branch main has been updated by adrian: URL: https://cgit.FreeBSD.org/src/commit/?id=58bd89647617dab2906db16ae30f7a935c2f8acb
commit 58bd89647617dab2906db16ae30f7a935c2f8acb Author: Abdelkader Boudih <[email protected]> AuthorDate: 2026-07-17 04:03:04 +0000 Commit: Adrian Chadd <[email protected]> CommitDate: 2026-07-17 04:07:46 +0000 fwcam: match unit directories, defer probe to first open Migrated fwcam to use per-unit-directory child device Differential Revision: https://reviews.freebsd.org/D58202 Reviewed by: adrian --- share/man/man4/fwcam.4 | 17 +++- sys/dev/firewire/fwcam.c | 200 +++++++++++++++++++++++------------------------ sys/dev/firewire/fwcam.h | 8 +- 3 files changed, 118 insertions(+), 107 deletions(-) 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 @@ -3,7 +3,7 @@ .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" -.Dd June 19, 2026 +.Dd July 12, 2026 .Dt FWCAM 4 .Os .Sh NAME @@ -16,7 +16,20 @@ 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. 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 @@ -52,13 +52,10 @@ SYSCTL_INT(_hw_firewire_fwcam, OID_AUTO, iso_channel, CTLFLAG_RWTUN, 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 *); @@ -80,25 +77,56 @@ static struct cdevsw fwcam_cdevsw = { .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 @@ -286,11 +314,18 @@ fwcam_probe_task(void *arg, int pending __unused) 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); } } @@ -616,16 +651,33 @@ 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++; @@ -937,103 +989,53 @@ fwcam_cdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, } } -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; @@ -1045,11 +1047,6 @@ fwcam_attach(device_t 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); } @@ -1084,7 +1081,6 @@ fwcam_detach(device_t dev) } 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), 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 @@ -203,9 +203,10 @@ 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 @@ -248,6 +249,7 @@ static const char * const fwcam_feat_names[FWCAM_FEAT_MAX] = { 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",
