mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-30 04:33:00 +02:00
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:
@@ -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'"
|
||||
@@ -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):
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user