board/common: fix authorization cascade for internal USB hubs

Two related problems prevented devices behind an intermediate hub (e.g.
the VIA Labs hub on the RPi 400's VL805 xHCI) from being authorized
when confd unlocks a USB bus:

1. Cascade ordering: usb_authorize() ran nftw() with FTW_DEPTH, which
   visits children before the root-hub entry.  The hub was authorized
   while authorized_default was still 2, so when the kernel probed the
   hub's children it immediately denied them.  Fix: write
   authorized_default=1 on the bus before entering nftw.

2. Slow/async probe: hub port enumeration is asynchronous — devices
   behind a hub may appear in sysfs after nftw has already finished.
   Fix: add a udev rule that catches usb_device add events on buses
   where authorized_default=1 is already set on any ancestor root hub
   and authorizes them immediately.

The ATTRS{} matcher in udev walks the full sysfs parent chain, so
authorized_default=1 on the root hub is visible even for devices several
hubs deep.  Buses still locked at authorized_default=0 are left alone.

Also expand the docstring in generic_usb_ports() to document the
design: each root hub gets its own uniquely-named entry (USB, or USB1/
USB2/... when multiple) so buses can be individually controlled, and
boards needing finer control should use DT usb-ports annotations.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2026-03-10 12:11:08 +01:00
parent 726a3132e3
commit 799fd93cac
3 changed files with 49 additions and 17 deletions
+11
View File
@@ -78,6 +78,17 @@ static bool usb_authorize(struct json_t *root, const char *name, int enabled)
}
}
} else {
/*
* Set authorized_default=1 on the bus BEFORE nftw
* authorizes individual devices. With FTW_DEPTH,
* nftw visits children before the root-hub entry, so
* without this a hub would be authorized first while
* authorized_default is still 2. Setting it here
* ensures devices appearing behind an intermediate
* hub are auto-authorized by the kernel as they probe.
*/
if (fexist(apath))
writedf(1, "w", "%s", apath);
if (realpath(path, apath))
nftw(apath, dir_cb, 20, FTW_DEPTH | FTW_PHYS);
}