udev: PCI bridges as parents, USB interfaces, the controller's own address, and the attributes lsusb reads

PCI devices now nest under the bridge they sit behind (from each bridge's
secondary..subordinate window), so a controller behind a root port is where
Linux would put it. USB interfaces (1-5:1.0) are directories with class,
number, a driver link and a uevent with INTERFACE= and a MODALIAS, announced
after the drivers have been offered the device and removed before it. The xHCI
driver tells the USB core its PCI address when it comes up, replacing the guess
that bus N was the Nth controller. Devices and root hubs carry version,
bMaxPacketSize0, bNumInterfaces, bmAttributes and the rest; lsusb -t from
Alpine's usbutils now draws the tree.

Checked on q35 with an xHCI behind a pcie-root-port and a USB keyboard on each
controller. Plain lsusb still fails: libusb wants usbfs, and refuses a kernel
that calls itself 0.1.0.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
This commit is contained in:
Nexoniarz 2026-10-26 17:54:13 +01:00
commit d650ff433b
6 changed files with 461 additions and 42 deletions

View file

@ -57,6 +57,10 @@ struct usb_info {
uint16 vid, pid, bcd;
uint8 cls, subcls, proto; // the device descriptor's
uint8 nconf;
uint16 bcdusb; // the version the device speaks
uint8 maxp0; // bMaxPacketSize0
uint8 nif, confval, attrs; // the configuration descriptor's
uint16 maxpower; // in mA
char path[24]; // Linux's name: "1-3", "2-1.4"
char driver[16]; // the first interface's, or empty
};
@ -66,5 +70,18 @@ int usb_nbuses(void);
// Tell /sys and udev a USB device appeared ("add") or is about to go ("remove").
void sysfs_usb_uevent(const struct usb_info *u, const char *action);
void sysfs_usb_root_uevent(int bus, const char *action);
// A USB controller is up; number its bus and announce its root hub.
void usb_bus_add(void *hc);
// A USB controller is up, at this PCI address; number its bus and announce its
// root hub.
void usb_bus_add(void *hc, int pci_bus, int pci_slot, int pci_func);
// The PCI address of the controller behind bus number `bus`; 0, or -1 if none.
int usb_bus_pci(int bus, int *pci_bus, int *pci_slot, int *pci_func);
// One interface of a configured USB device, in the order the configuration
// descriptor lists them (alternate setting 0 only).
struct usb_if_info {
uint8 num, cls, subcls, proto, nendp;
char driver[16]; // what is bound to it, or empty
};
int usb_if_nth(int bus, int devnum, int n, struct usb_if_info *out);
void sysfs_usb_if_uevent(const struct usb_info *u, const struct usb_if_info *i,
const char *action);