The branch main has been updated by adrian: URL: https://cgit.FreeBSD.org/src/commit/?id=21143476a5767b49ca722e6f28b19770703b537e
commit 21143476a5767b49ca722e6f28b19770703b537e Author: Abdelkader Boudih <[email protected]> AuthorDate: 2026-07-17 04:02:36 +0000 Commit: Adrian Chadd <[email protected]> CommitDate: 2026-07-17 04:07:36 +0000 firewire: per-unit-directory child device support Added structure to allow multiple device to attach to the same driver. Also removed the deprecation warning from the man page. Differential Revision: https://reviews.freebsd.org/D58201 Reviewed by: adrian --- share/man/man4/firewire.4 | 26 ++++- sys/dev/firewire/firewire.c | 229 ++++++++++++++++++++++++++++++++++++++--- sys/dev/firewire/firewirereg.h | 53 ++++++++++ sys/dev/firewire/if_fwe.c | 8 +- sys/dev/firewire/if_fwip.c | 8 +- sys/dev/firewire/sbp.c | 5 +- sys/dev/firewire/sbp_targ.c | 8 +- 7 files changed, 310 insertions(+), 27 deletions(-) diff --git a/share/man/man4/firewire.4 b/share/man/man4/firewire.4 index 99aadb308fc3..a7253443b5cf 100644 --- a/share/man/man4/firewire.4 +++ b/share/man/man4/firewire.4 @@ -29,7 +29,7 @@ .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd January 23, 2025 +.Dd July 12, 2026 .Dt FIREWIRE 4 .Os .Sh NAME @@ -72,6 +72,27 @@ The bus attaches to the controller. Additional drivers can be attached to the bus. .Pp +The +.Nm +bus parses each device's CSR configuration ROM to discover unit +directories. +A newbus child is created for each unit directory, carrying the +.Va unit_spec_id +and +.Va unit_sw_version +fields as ivars. +Device-specific drivers such as +.Xr fwcam 4 , +.Xr fwisound 4 , +and +.Xr fwdv 4 +match against these fields in their probe routines. +Protocol drivers such as +.Xr sbp 4 +and +.Xr fwe 4 +continue to use the legacy identify-based attachment model. +.Pp Up to 63 devices, including the host itself, can be attached to a .Nm @@ -99,8 +120,11 @@ for details on how to setup debugging with firewire. .El .Sh SEE ALSO .Xr dcons 4 , +.Xr fwcam 4 , +.Xr fwdv 4 , .Xr fwe 4 , .Xr fwip 4 , +.Xr fwisound 4 , .Xr fwohci 4 , .Xr pci 4 , .Xr sbp 4 , diff --git a/sys/dev/firewire/firewire.c b/sys/dev/firewire/firewire.c index dfb906ba34c1..c0a7c53cdd6c 100644 --- a/sys/dev/firewire/firewire.c +++ b/sys/dev/firewire/firewire.c @@ -86,12 +86,16 @@ static int firewire_detach(device_t); static int firewire_resume(device_t); static void firewire_xfer_timeout(void *, int); static device_t firewire_add_child(device_t, u_int, const char *, int); +static void firewire_child_deleted(device_t, device_t); static void fw_try_bmr(void *); static void fw_try_bmr_callback(struct fw_xfer *); static void fw_asystart(struct fw_xfer *); static int fw_get_tlabel(struct firewire_comm *, struct fw_xfer *); static void fw_bus_probe(void *); static void fw_attach_dev(struct firewire_comm *); +static void fw_parse_units(struct fw_device *); +static void fw_create_unit_children(struct firewire_comm *, struct fw_device *); +static int fw_remove_unit_children(struct firewire_comm *, struct fw_device *); static void fw_bus_probe_thread(void *); #ifdef FW_VMACCESS static void fw_vmaccess (struct fw_xfer *); @@ -111,6 +115,7 @@ static device_method_t firewire_methods[] = { /* Bus interface */ DEVMETHOD(bus_add_child, firewire_add_child), + DEVMETHOD(bus_child_deleted, firewire_child_deleted), DEVMETHOD_END }; @@ -473,17 +478,39 @@ firewire_add_child(device_t dev, u_int order, const char *name, int unit) { device_t child; struct firewire_softc *sc; + struct fw_child_ivars *iv; sc = device_get_softc(dev); child = device_add_child(dev, name, unit); if (child) { - device_set_ivars(child, sc->fc); + iv = malloc(sizeof(*iv), M_FW, M_NOWAIT | M_ZERO); + if (iv == NULL) { + device_delete_child(dev, child); + return (NULL); + } + iv->type = FW_CHILD_BUS; + iv->u.fc = sc->fc; + device_set_ivars(child, iv); device_probe_and_attach(child); } return child; } +static void +firewire_child_deleted(device_t dev, device_t child) +{ + struct fw_child_ivars *iv; + + iv = device_get_ivars(child); + if (iv != NULL) { + if (iv->type == FW_CHILD_UNIT && iv->u.unit != NULL) + iv->u.unit->dev = NULL; + free(iv, M_FW); + device_set_ivars(child, NULL); + } +} + static int firewire_resume(device_t dev) { @@ -523,6 +550,13 @@ firewire_detach(device_t dev) if ((err = fwdev_destroydev(sc)) != 0) return err; + /* Remove unit children before bus_generic_detach */ + for (fwdev = STAILQ_FIRST(&fc->devices); fwdev != NULL; + fwdev = fwdev_next) { + fwdev_next = STAILQ_NEXT(fwdev, link); + fw_remove_unit_children(fc, fwdev); + } + if ((err = bus_generic_detach(dev)) != 0) return err; @@ -721,13 +755,22 @@ fw_busreset(struct firewire_comm *fc, uint32_t new_status) fw_reset_crom(fc); + /* + * Skip unit children; only bus-level children have post_busreset. + */ if (device_get_children(fc->bdev, &devlistp, &devcnt) == 0) { - for (i = 0; i < devcnt; i++) - if (device_get_state(devlistp[i]) >= DS_ATTACHED) { - fdc = device_get_softc(devlistp[i]); - if (fdc->post_busreset != NULL) - fdc->post_busreset(fdc); - } + for (i = 0; i < devcnt; i++) { + struct fw_child_ivars *iv; + + if (device_get_state(devlistp[i]) < DS_ATTACHED) + continue; + iv = fw_get_ivars(devlistp[i]); + if (iv == NULL || iv->type != FW_CHILD_BUS) + continue; + fdc = device_get_softc(devlistp[i]); + if (fdc->post_busreset != NULL) + fdc->post_busreset(fdc); + } free(devlistp, M_TEMP); } @@ -1585,6 +1628,7 @@ fw_explore_node(struct fw_device *dfwdev) fwdev->dst = dfwdev->dst; fwdev->maxrec = dfwdev->maxrec; fwdev->status = dfwdev->status; + STAILQ_INIT(&fwdev->units); /* * Pre-1394a-2000 didn't have link_spd in @@ -1638,10 +1682,17 @@ fw_explore_node(struct fw_device *dfwdev) fwdev->status = FWDEVINIT; /* unchanged ? */ if (bcmp(&csr[0], &fwdev->csrrom[0], sizeof(uint32_t) * 5) == 0) { - if (firewire_debug) - device_printf(fc->dev, - "node%d: crom unchanged\n", node); - return (0); + if (!STAILQ_EMPTY(&fwdev->units)) { + if (firewire_debug) + device_printf(fc->dev, + "node%d: crom unchanged\n", node); + fwdev->rom_changed = 0; + return (0); + } + /* Units lost after detach/reattach; re-parse. */ + fwdev->rom_changed = 0; + } else { + fwdev->rom_changed = 1; } } @@ -1764,6 +1815,136 @@ fw_bus_probe_thread(void *arg) kproc_exit(0); } +static void +fw_parse_units(struct fw_device *fwdev) +{ + struct csrhdr *hdr; + struct csrdirectory *rootdir, *udir; + struct csrreg *ureg; + struct fw_unit *unit; + uint32_t spec_id, sw_version; + uint32_t rom_quads, root_off, root_len; + uint32_t reg_off, udir_qoff, udir_len; + int ri, ui, uindex; + + rom_quads = CSRROMSIZE / 4; + hdr = (struct csrhdr *)fwdev->csrrom; + if (hdr->info_len <= 1) + return; + + root_off = 1 + hdr->info_len; + if (root_off >= rom_quads) + return; + rootdir = (struct csrdirectory *)&fwdev->csrrom[root_off]; + root_len = rootdir->crc_len; + if (root_off + 1 + root_len > rom_quads) + return; + + uindex = 0; + for (ri = 0; ri < (int)root_len; ri++) { + reg_off = root_off + 1 + ri; + if (reg_off >= rom_quads) + break; + if (rootdir->entry[ri].key != CROM_UDIR) + continue; + + udir_qoff = reg_off + rootdir->entry[ri].val; + if (udir_qoff >= rom_quads) + continue; + udir = (struct csrdirectory *)&fwdev->csrrom[udir_qoff]; + udir_len = udir->crc_len; + if (udir_qoff + 1 + udir_len > rom_quads) + continue; + + spec_id = 0; + sw_version = 0; + for (ui = 0; ui < (int)udir_len; ui++) { + if (udir_qoff + 1 + ui >= rom_quads) + break; + ureg = &udir->entry[ui]; + if (ureg->key == CSRKEY_SPEC) + spec_id = ureg->val; + else if (ureg->key == CSRKEY_VER) + sw_version = ureg->val; + } + if (spec_id == 0 && sw_version == 0) + continue; + + unit = malloc(sizeof(*unit), M_FW, M_NOWAIT | M_ZERO); + if (unit == NULL) + continue; + unit->fwdev = fwdev; + unit->spec_id = spec_id; + unit->sw_version = sw_version; + unit->dir_offset = udir_qoff * 4; + STAILQ_INSERT_TAIL(&fwdev->units, unit, link); + uindex++; + } + if (firewire_debug && uindex > 0) + device_printf(fwdev->fc->bdev, + "node %d: %d unit director%s found\n", + fwdev->dst, uindex, uindex == 1 ? "y" : "ies"); +} + +static void +fw_create_unit_children(struct firewire_comm *fc, struct fw_device *fwdev) +{ + struct fw_unit *unit; + struct fw_child_ivars *iv; + + bus_topo_lock(); + STAILQ_FOREACH(unit, &fwdev->units, link) { + if (unit->dev != NULL) + continue; + unit->dev = device_add_child(fc->bdev, NULL, DEVICE_UNIT_ANY); + if (unit->dev == NULL) { + device_printf(fc->bdev, + "failed to add unit child for node %d\n", + fwdev->dst); + continue; + } + iv = malloc(sizeof(*iv), M_FW, M_NOWAIT | M_ZERO); + if (iv == NULL) { + device_delete_child(fc->bdev, unit->dev); + unit->dev = NULL; + continue; + } + iv->type = FW_CHILD_UNIT; + iv->u.unit = unit; + device_set_ivars(unit->dev, iv); + if (firewire_debug) + device_printf(fc->bdev, + "node %d: unit spec=0x%06x ver=0x%06x\n", + fwdev->dst, unit->spec_id, unit->sw_version); + device_probe_and_attach(unit->dev); + } + bus_topo_unlock(); +} + +static int +fw_remove_unit_children(struct firewire_comm *fc, struct fw_device *fwdev) +{ + struct fw_unit *unit, *next; + int err, ret; + + ret = 0; + bus_topo_lock(); + STAILQ_FOREACH_SAFE(unit, &fwdev->units, link, next) { + if (unit->dev != NULL) { + err = device_delete_child(fc->bdev, unit->dev); + if (err != 0) { + ret = err; + continue; + } + unit->dev = NULL; + } + STAILQ_REMOVE(&fwdev->units, unit, fw_unit, link); + free(unit, M_FW); + } + bus_topo_unlock(); + return (ret); +} + /* * To attach sub-devices layer onto IEEE1394 bus. */ @@ -1780,6 +1961,15 @@ fw_attach_dev(struct firewire_comm *fc) next = STAILQ_NEXT(fwdev, link); if (fwdev->status == FWDEVINIT) { fwdev->status = FWDEVATTACHED; + if (fwdev->rom_changed && + !STAILQ_EMPTY(&fwdev->units)) { + if (fw_remove_unit_children(fc, fwdev) == 0) + fwdev->rom_changed = 0; + } + if (STAILQ_EMPTY(&fwdev->units)) { + fw_parse_units(fwdev); + } + fw_create_unit_children(fc, fwdev); } else if (fwdev->status == FWDEVINVAL) { fwdev->rcnt++; if (firewire_debug) @@ -1791,6 +1981,8 @@ fw_attach_dev(struct firewire_comm *fc) * Remove devices which have not been seen * for a while. */ + if (fw_remove_unit_children(fc, fwdev) != 0) + continue; STAILQ_REMOVE(&fc->devices, fwdev, fw_device, link); free(fwdev, M_FW); @@ -1801,11 +1993,16 @@ fw_attach_dev(struct firewire_comm *fc) err = device_get_children(fc->bdev, &devlistp, &devcnt); if (err == 0) { for (i = 0; i < devcnt; i++) { - if (device_get_state(devlistp[i]) >= DS_ATTACHED) { - fdc = device_get_softc(devlistp[i]); - if (fdc->post_explore != NULL) - fdc->post_explore(fdc); - } + struct fw_child_ivars *iv; + + if (device_get_state(devlistp[i]) < DS_ATTACHED) + continue; + iv = fw_get_ivars(devlistp[i]); + if (iv == NULL || iv->type != FW_CHILD_BUS) + continue; + fdc = device_get_softc(devlistp[i]); + if (fdc->post_explore != NULL) + fdc->post_explore(fdc); } free(devlistp, M_TEMP); } diff --git a/sys/dev/firewire/firewirereg.h b/sys/dev/firewire/firewirereg.h index 97a53606c001..a9f4f23e6a91 100644 --- a/sys/dev/firewire/firewirereg.h +++ b/sys/dev/firewire/firewirereg.h @@ -58,14 +58,38 @@ struct fw_device { int rommax; /* offset from 0xffff f000 0000 */ uint32_t csrrom[CSRROMSIZE / 4]; int rcnt; + int rom_changed; struct firewire_comm *fc; uint32_t status; #define FWDEVINIT 1 #define FWDEVATTACHED 2 #define FWDEVINVAL 3 + STAILQ_HEAD(, fw_unit) units; STAILQ_ENTRY(fw_device) link; }; +struct fw_unit { + device_t dev; + struct fw_device *fwdev; + uint32_t spec_id; + uint32_t sw_version; + uint32_t dir_offset; + STAILQ_ENTRY(fw_unit) link; +}; + +enum fw_child_type { + FW_CHILD_BUS, + FW_CHILD_UNIT, +}; + +struct fw_child_ivars { + enum fw_child_type type; + union { + struct firewire_comm *fc; + struct fw_unit *unit; + } u; +}; + struct firewire_softc { struct cdev *dev; struct firewire_comm *fc; @@ -294,6 +318,35 @@ extern int firewire_debug; extern devclass_t firewire_devclass; extern int firewire_phydma_enable; +static __inline struct fw_child_ivars * +fw_get_ivars(device_t dev) +{ + + return ((struct fw_child_ivars *)device_get_ivars(dev)); +} + +static __inline struct firewire_comm * +fw_get_comm(device_t dev) +{ + struct fw_child_ivars *iv; + + iv = fw_get_ivars(dev); + if (iv->type == FW_CHILD_BUS) + return (iv->u.fc); + return (iv->u.unit->fwdev->fc); +} + +static __inline struct fw_unit * +fw_get_unit(device_t dev) +{ + struct fw_child_ivars *iv; + + iv = fw_get_ivars(dev); + if (iv->type == FW_CHILD_UNIT) + return (iv->u.unit); + return (NULL); +} + #define FWPRI (PWAIT | PCATCH) #define CALLOUT_INIT(x) callout_init(x, 1 /* mpsafe */) diff --git a/sys/dev/firewire/if_fwe.c b/sys/dev/firewire/if_fwe.c index c8bfc439e166..ed288cab02c7 100644 --- a/sys/dev/firewire/if_fwe.c +++ b/sys/dev/firewire/if_fwe.c @@ -123,10 +123,12 @@ fwe_probe(device_t dev) { device_t pa; + if (fw_get_unit(dev) != NULL) + return (ENXIO); + pa = device_get_parent(dev); - if (device_get_unit(dev) != device_get_unit(pa)) { + if (device_get_unit(dev) != device_get_unit(pa)) return (ENXIO); - } device_set_desc(dev, "Ethernet over FireWire"); return (0); @@ -150,7 +152,7 @@ fwe_attach(device_t dev) fwe->stream_ch = stream_ch; fwe->dma_ch = -1; - fwe->fd.fc = device_get_ivars(dev); + fwe->fd.fc = fw_get_comm(dev); if (tx_speed < 0) tx_speed = fwe->fd.fc->speed; diff --git a/sys/dev/firewire/if_fwip.c b/sys/dev/firewire/if_fwip.c index bdbe76e2caf4..c3c6c595c775 100644 --- a/sys/dev/firewire/if_fwip.c +++ b/sys/dev/firewire/if_fwip.c @@ -134,10 +134,12 @@ fwip_probe(device_t dev) { device_t pa; + if (fw_get_unit(dev) != NULL) + return (ENXIO); + pa = device_get_parent(dev); - if (device_get_unit(dev) != device_get_unit(pa)) { + if (device_get_unit(dev) != device_get_unit(pa)) return (ENXIO); - } device_set_desc(dev, "IP over FireWire"); return (0); @@ -159,7 +161,7 @@ fwip_attach(device_t dev) /* XXX */ fwip->dma_ch = -1; - fwip->fd.fc = device_get_ivars(dev); + fwip->fd.fc = fw_get_comm(dev); if (tx_speed < 0) tx_speed = fwip->fd.fc->speed; diff --git a/sys/dev/firewire/sbp.c b/sys/dev/firewire/sbp.c index 9e76000f5fb6..d8c5f2dd68b7 100644 --- a/sys/dev/firewire/sbp.c +++ b/sys/dev/firewire/sbp.c @@ -318,6 +318,9 @@ SBP_DEBUG(0) printf("sbp_probe\n"); END_DEBUG + if (fw_get_unit(dev) != NULL) + return (ENXIO); + device_set_desc(dev, "SBP-2/SCSI over FireWire"); #if 0 @@ -1917,7 +1920,7 @@ END_DEBUG sbp_cold++; sbp = device_get_softc(dev); sbp->fd.dev = dev; - sbp->fd.fc = fc = device_get_ivars(dev); + sbp->fd.fc = fc = fw_get_comm(dev); mtx_init(&sbp->mtx, "sbp", NULL, MTX_DEF); if (max_speed < 0) diff --git a/sys/dev/firewire/sbp_targ.c b/sys/dev/firewire/sbp_targ.c index 96a86c25b684..ba40d2807ae8 100644 --- a/sys/dev/firewire/sbp_targ.c +++ b/sys/dev/firewire/sbp_targ.c @@ -249,10 +249,12 @@ sbp_targ_probe(device_t dev) { device_t pa; + if (fw_get_unit(dev) != NULL) + return (ENXIO); + pa = device_get_parent(dev); - if (device_get_unit(dev) != device_get_unit(pa)) { + if (device_get_unit(dev) != device_get_unit(pa)) return (ENXIO); - } device_set_desc(dev, "SBP-2/SCSI over FireWire target mode"); return (0); @@ -1949,7 +1951,7 @@ sbp_targ_attach(device_t dev) bzero((void *)sc, sizeof(struct sbp_targ_softc)); mtx_init(&sc->mtx, "sbp_targ", NULL, MTX_DEF); - sc->fd.fc = fc = device_get_ivars(dev); + sc->fd.fc = fc = fw_get_comm(dev); sc->fd.dev = dev; sc->fd.post_explore = (void *) sbp_targ_post_explore; sc->fd.post_busreset = (void *) sbp_targ_post_busreset;
