Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 17 Jul 2026 04:09:27 +0000
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Cc:        Abdelkader Boudih <freebsd@seuros.com>
Subject:   git: 21143476a576 - main - firewire: per-unit-directory child device support
Message-ID:  <6a59aaf7.26044.206dbf79@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by adrian:

URL: https://cgit.FreeBSD.org/src/commit/?id=21143476a5767b49ca722e6f28b19770703b537e

commit 21143476a5767b49ca722e6f28b19770703b537e
Author:     Abdelkader Boudih <freebsd@seuros.com>
AuthorDate: 2026-07-17 04:02:36 +0000
Commit:     Adrian Chadd <adrian@FreeBSD.org>
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;


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a59aaf7.26044.206dbf79>