diff --git a/board/common/rootfs/etc/udev/rules.d/70-usb-authorize.rules b/board/common/rootfs/etc/udev/rules.d/70-usb-authorize.rules new file mode 100644 index 00000000..65d959e3 --- /dev/null +++ b/board/common/rootfs/etc/udev/rules.d/70-usb-authorize.rules @@ -0,0 +1,19 @@ +# Authorize USB devices on buses that Infix has unlocked (authorized_default=1). +# +# With usbcore.authorized_default=2 only DT-marked hard-wired ports are +# auto-authorized by the kernel. When confd unlocks a bus it sets +# authorized_default=1 on the root hub and uses nftw to authorize already- +# present devices. However, devices behind an intermediate hub may appear +# asynchronously — after nftw has already finished — because hub port probing +# is slow. This rule catches those out-of-band additions: if a device arrives +# unauthorized (authorized=0) on a bus where confd has set authorized_default=1 +# on any ancestor root hub, authorize it immediately. +# +# Note: ATTRS{} matches the attribute on the device itself or any parent, +# so authorized_default=1 on the root hub (usbN) is found even for devices +# connected through one or more intermediate hubs. Locked buses keep +# authorized_default=0, so this rule correctly leaves them alone. +ACTION=="add", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", \ + ATTR{authorized}=="0", \ + ATTRS{authorized_default}=="1", \ + RUN+="/bin/sh -c 'echo 1 > /sys%p/authorized'" diff --git a/board/common/rootfs/usr/libexec/infix/init.d/00-probe b/board/common/rootfs/usr/libexec/infix/init.d/00-probe index d7067127..7d047731 100755 --- a/board/common/rootfs/usr/libexec/infix/init.d/00-probe +++ b/board/common/rootfs/usr/libexec/infix/init.d/00-probe @@ -433,7 +433,13 @@ def probe_qemusystem(out): def generic_usb_ports(out): - """Generic USB port discovery - works for all systems""" + """Generic USB port discovery - works for all systems. + + Each root hub gets its own uniquely-named entry so it can be independently + enabled or disabled. Boards that need explicit per-port control (e.g. to + exclude internal buses) should annotate their DT with usb-ports / + usb-port-names in the chosen/infix node instead. + """ ports = [] usb_base = "/sys/bus/usb/devices" @@ -441,26 +447,22 @@ def generic_usb_ports(out): return for entry in sorted(os.listdir(usb_base)): - # Only look at root hubs (usbN) if not entry.startswith("usb"): continue - device_path = os.path.join(usb_base, entry) - # Check if this is a root hub (has authorized files) - if os.path.exists(os.path.join(device_path, "authorized")): - # Extract number from usbN - num = entry.replace("usb", "") - ports.append({ - "num": num, - "path": device_path - }) + if not os.path.exists(os.path.join(device_path, "authorized")): + continue + num = entry.replace("usb", "") + ports.append({"num": num, "path": device_path}) - # Name ports: "USB" for single port, "USB1", "USB2", etc. for multiple - if ports: - if len(ports) == 1: - out["usb-ports"] = [{"name": "USB", "path": ports[0]["path"]}] - else: - out["usb-ports"] = [{"name": f"USB{p['num']}", "path": p["path"]} for p in ports] + if not ports: + return + + # Single bus → "USB"; multiple → "USB1", "USB2", ... (by bus number) + if len(ports) == 1: + out["usb-ports"] = [{"name": "USB", "path": ports[0]["path"]}] + else: + out["usb-ports"] = [{"name": f"USB{p['num']}", "path": p["path"]} for p in ports] def probe_dmisystem(out): diff --git a/src/confd/src/hardware.c b/src/confd/src/hardware.c index 65114493..5e2b3a96 100644 --- a/src/confd/src/hardware.c +++ b/src/confd/src/hardware.c @@ -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); }