The following reply was made to PR port-arm/60021; it has been noted by GNATS.
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
To: Nick Hudson <nick.hudson%gmx.co.uk@localhost>
Cc: mac%culver.net@localhost, gnats-bugs%NetBSD.org@localhost, netbsd-bugs%NetBSD.org@localhost
Subject: Re: port-arm/60021: USB-only boot: uhub0 attaches but uhub1 never
appears, no hotplug events; SD-boot sees hub+umass fine
Date: Tue, 4 Aug 2026 13:34:02 +0000
> Date: Sat, 21 Feb 2026 10:39:41 +0000
> From: Nick Hudson <nick.hudson%gmx.co.uk@localhost>
>=20
> This is almost certainly that autoconf doesn't wait (long enough) for
> sub-ordintate hubs
>=20
> https://nxr.netbsd.org/xref/src/sys/dev/usb/uhub.c#880
>=20
> 880 mutex_enter(&sc->sc_lock);
> 881 sc->sc_explorepending =3D false;
> 882 for (int i =3D 0; i < sc->sc_statuslen; i++) {
> 883 if (sc->sc_statuspend[i] !=3D 0) {
> 884 memcpy(sc->sc_status, sc->sc_statuspend,
> 885 sc->sc_statuslen);
> 886 memset(sc->sc_statuspend, 0, sc->sc_statuslen);
> 887 usb_needs_explore(sc->sc_hub);
> 888 break;
> 889 }
> 890 }
> 891 mutex_exit(&sc->sc_lock);
> 892 if (sc->sc_first_explore) {
> 893 config_pending_decr(sc->sc_dev);
> 894 sc->sc_first_explore =3D false;
> 895 }
I don't understand, doesn't it wait for subordinate hubs? Maybe the
USB hub just doesn't report the device ready at first?
Here's a fuller picture of the logic -- note that uhub_explore will
_synchronously_ attach autoconf drivers for the devices it finds on
the hub, and then uhub_attach will _also_ config_pending_incr:
529 usbd_status
530 uhub_explore(struct usbd_device *dev)
531 {
...
598 for (port =3D 1; port <=3D hd->bNbrPorts; port++) {
599 up =3D &dev->ud_hub->uh_ports[port - 1];
...
848 /* Get device info and set its address. */
849 err =3D usbd_new_device(sc->sc_dev, dev->ud_bus,
850 dev->ud_depth + 1, speed, port, up);
...
858 if (err) {
...
872 } else {
...
878 if (up->up_dev->ud_hub)
879 up->up_dev->ud_hub->uh_explore(up->up_dev);
880 }
881 }
882 mutex_enter(&sc->sc_lock);
883 sc->sc_explorepending =3D false;
884 for (int i =3D 0; i < sc->sc_statuslen; i++) {
885 if (sc->sc_statuspend[i] !=3D 0) {
886 memcpy(sc->sc_status, sc->sc_statuspend,
887 sc->sc_statuslen);
888 memset(sc->sc_statuspend, 0, sc->sc_statuslen);
889 usb_needs_explore(sc->sc_hub);
890 break;
891 }
892 }
893 mutex_exit(&sc->sc_lock);
894 if (sc->sc_first_explore) {
895 config_pending_decr(sc->sc_dev);
896 sc->sc_first_explore =3D false;
897 }
https://nxr.netbsd.org/xref/src/sys/dev/usb/uhub.c?r=3D1.163#529
usbd_new_device synchronously attaches autoconf drivers:
1394 usbd_status
1395 usbd_new_device(device_t parent, struct usbd_bus *bus, int depth, i=
nt speed,
1396 int port, struct usbd_port *up)
1397 {
...
1606 if (port =3D=3D 0) { /* root hub */
1607 KASSERT(addr =3D=3D 1);
1608 usbd_attach_roothub(parent, dev);
1609 return USBD_NORMAL_COMPLETION;
1610 }
1611=20
1612 err =3D usbd_probe_and_attach(parent, dev, port, addr);
1613 if (err) {
1614 usbd_remove_device(dev, up);
1615 return err;
1616 }
1617=20
1618 return USBD_NORMAL_COMPLETION;
1619 }
https://nxr.netbsd.org/xref/src/sys/dev/usb/usb_subr.c?r=3D1.281#1388
1047 usbd_status
1048 usbd_attach_roothub(device_t parent, struct usbd_device *dev)
1049 {
...
1065 dv =3D config_found(parent, &uaa, NULL,
1066 CFARGS(.iattr =3D "usbroothubif"));
https://nxr.netbsd.org/xref/src/sys/dev/usb/usb_subr.c?r=3D1.281#1047
1270 usbd_status
1271 usbd_probe_and_attach(device_t parent, struct usbd_device *dev,
1272 int port, int addr)
1273 {
...
1283 err =3D usbd_attachwholedevice(parent, dev, port, 0);
1284 if (dev->ud_nifaces_claimed || err)
1285 return err;
...
1290 for (confi =3D 0; confi < dd->bNumConfigurations; confi++) {
...
1305 err =3D usbd_attachinterfaces(parent, dev, port, NULL);
...
1313 if (dev->ud_nifaces_claimed || err)
1314 return err;
1315 }
...
1324 err =3D usbd_attachwholedevice(parent, dev, port, 1);
https://nxr.netbsd.org/xref/src/sys/dev/usb/usb_subr.c?r=3D1.281#1270
1129 static usbd_status
1130 usbd_attachwholedevice(device_t parent, struct usbd_device *dev, in=
t port,
1131 int usegeneric)
1132 {
...
1161 dv =3D config_found(parent, &uaa, usbd_print,
1162 CFARGS(.submatch =3D config_stdsubmatch,
1163 .iattr =3D "usbdevif",
1164 .locators =3D dlocs));
...
https://nxr.netbsd.org/xref/src/sys/dev/usb/usb_subr.c?r=3D1.281#1129
1177 static usbd_status
1178 usbd_attachinterfaces(device_t parent, struct usbd_device *dev,
1179 int port, const int *locators)
1180 {
...
1242 dv =3D config_found(parent, &uiaa, usbd_ifprint,
1243 CFARGS(.submatch =3D config_stdsubmatch,
1244 .iattr =3D "usbifif",
1245 .locators =3D ilocs));
uhub is only at one of these (usbdevif or usbifif), but the point is
the attach happens synchronously for subordinate hubs, before
uhub_explore gets to config_pending_decr.
And in uhub_attach, pretty much the first thing it does is
config_pending_incr -- and it also sets the hub of the USB device to
be the one it just created, so uhub_attach's call to uh_explore should
take care of this:
299 static void
300 uhub_attach(device_t parent, device_t self, void *aux)
301 {
...
318 config_pending_incr(self);
...
371 hub =3D kmem_alloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_=
port),
372 KM_SLEEP);
373 dev->ud_hub =3D hub;
374 dev->ud_hub->uh_hubsoftc =3D sc;
375 hub->uh_explore =3D uhub_explore;
376 hub->uh_hubdesc =3D hubdesc;
https://nxr.netbsd.org/xref/src/sys/dev/usb/uhub.c?r=3D1.163#529
As noted above, uhub_explore even explores the subordinate hubs
synchronously too by calling uh_explore!
So, does the USB device not appear to be connected at the time of the
initial probe, perhaps?
Building with USB_DEBUG and enabling usb_debug=3D1 so you can drop into
ddb and `show kernhist usbhist' might help.