6.1 KiB
USB hubs (M22)
A hub is USB bus infrastructure, not an application peripheral, so hub
topology is handled inside the usb-xhci-bus driver — the process that owns
the controller's device slots and contexts. A device behind a hub is not reached
by any hub-specific software path: it is reached by the controller,
programmed with a route string in its slot context. Route strings and slot
contexts are xHCI hardware concepts that only exist inside the controller driver,
so that is where hub handling belongs. Class drivers (HID, storage) stay separate
and unaware — the hub is transparent to them; a keyboard behind a hub reaches the
same usb-hid-keyboard driver as one on a root port.
This is a deliberate scoping choice, not a microkernel compromise: the USB bus
driver handles USB bus topology. The alternative — a separate usb-hub
class-driver process plus a cross-process enumeration protocol — would only
shuttle the bus's own topology state (slot ids, route strings, TT linkage) out to
another process and back, since the hub driver cannot build a slot context
itself.
The compound-hub reality
A USB 3.0 hub is physically two hubs sharing each connector: a SuperSpeed hub and a USB 2.0 companion hub, enumerated as separate devices on separate root ports. A full- or low-speed device plugged into a USB 3.0 hub attaches to the USB 2.0 companion, not the SuperSpeed hub. So supporting full-speed devices (keyboards, mice) behind a hub means driving the USB 2.0 companion and handling transaction translators — there is no SuperSpeed-only shortcut that reaches a full-speed keyboard.
Slot-context fields for a downstream device
buildAddressInputContext fills the Slot Context. Today it hardcodes route 0 and
the root-hub port. A downstream device additionally needs:
- Route String (Slot Context dword 0, bits 19:0) — 5 tiers × 4 bits, each
tier the downstream hub-port number. Composed as
route = (parent_route << 4) | hub_port, capped at the xHCI 5-tier max. - Root Hub Port Number (dword 1, bits 23:16) — the root port the whole hub chain hangs off, inherited from the parent hub (not the hub's own port number).
- Speed (dword 0, bits 23:20) — read from the hub's downstream port status after reset, not assumed.
- Parent Hub Slot ID (dword 2, bits 7:0) + Parent Port Number (dword 2, bits 13:8) — the transaction translator: set when a full/low-speed device sits behind a high-speed hub, so the controller routes split transactions through that hub's TT. For a multi-TT hub, MTT (Slot Context dword 0 bit 25) is set and the TT port is the device's own hub port.
Detection: the status-change interrupt endpoint
A hub has one interrupt IN endpoint that returns a port-status-change bitmap (bit N set = port N changed). The bus arms an interrupt transfer on it (reusing the controller's existing interrupt-endpoint machinery, but serviced in-process — no class-driver subscription IPC), and on each report:
- For each changed port,
GET_STATUS(hub class request) reads the port's connect/enable/reset state and speed, andCLEAR_FEATURE(C_PORT_*)acknowledges the change. - On a connect:
SET_FEATURE(PORT_RESET), wait for reset-complete via a later status-change report, read the enabled speed, thensetupDevicewith the composed route string / root port / TT fields,enumerate, and register the interfaces — exactly the existing path, recursing if the new device is itself a hub. - On a disconnect: tear down the downstream device (report each interface
ChildRemoved, Disable Slot) — the B3 teardown path, keyed by the device's route rather than a root port.
Hub setup (once, when the hub enumerates)
When the bus scan (or a hot-plug bring-up) finds a device of class 9:
- Read the hub descriptor (class GET_DESCRIPTOR, type 0x2A for a USB 3.0 hub / 0x29 for USB 2.0) → downstream port count, characteristics.
- For a USB 3.0 hub,
SET_FEATURE(BH_PORT_RESET)semantics and the depth (SET_HUB_DEPTH) so the hub knows its tier for route-string forwarding. SET_FEATURE(PORT_POWER)each downstream port.- Configure the hub's slot as a hub: Hub bit (Slot Context dword 0 bit 26), Number of Ports (dword 1, bits 31:24), TT Think Time and MTT for a USB 2.0 multi-TT hub — via an Evaluate/Configure Endpoint on the hub's slot.
- Arm the status-change interrupt endpoint.
Testing
QEMU's usb-hub is a USB 2.0 single-TT hub. A static boot topology
(-device usb-hub,bus=xhci.0,id=h -device usb-kbd,bus=h.0) presents the
downstream device connected from the start, so the bus reads it on the first
status-change report — exercising the full path (hub setup, TT slot context,
downstream enumerate, class-driver bind) without needing a hot-plug event. A new
usb-hub QEMU case asserts the hub enumerates, the downstream keyboard
enumerates behind it, and usb-hid-keyboard binds.
Real-hardware validation (the user's SuperSpeed Genesys hub + full-speed keyboard/mouse on its USB 2.0 companion) is flagged separately — the compound USB 3.0 hub path is not modelled by QEMU's USB 2.0 hub.
Milestones (all complete)
- B4a ✓ — hub recognition + setup: detect class 9 in the scan, read the hub descriptor, configure the slot as a hub, power downstream ports, log the topology.
- B4b ✓ — downstream enumeration: the in-process status-change subscription,
port reset, Address Device with route string + root port + TT fields,
enumerate + register. A full-speed keyboard behind a USB2 hub binds
usb-hid-keyboardin QEMU. - B4c ✓ — disconnect teardown (recursive: a hub takes its subtree with it)
and hub-behind-hub recursion (route strings compose across tiers). QEMU's hub
does raise downstream status changes, so both connect and disconnect are
harness-tested (
usb-hub,usb-hub-nested,usb-hub-unplug).
Real-hardware validation of the user's SuperSpeed Genesys hub with full-speed devices on its USB 2.0 companion remains pending — QEMU's USB 2.0 hub does not model the compound USB 3.0 hub.