danos/docs/device-driver-development/usb-hub.md

115 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 from the device record: a
root-port device carries route 0 and its own root-hub port. A downstream device
additionally carries:
- **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:
1. For each changed port, `GET_STATUS` (hub class request) reads the port's
connect/enable/reset state and speed, and `CLEAR_FEATURE(C_PORT_*)`
acknowledges the change.
2. On a **connect**: `SET_FEATURE(PORT_RESET)`, wait for reset-complete via a
later status-change report, read the enabled speed, then `setupDevice` with
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.
3. 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:
1. Read the **hub descriptor** (class GET_DESCRIPTOR, type 0x2A for a USB 3.0
hub / 0x29 for USB 2.0) → downstream port count, characteristics.
2. 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.
3. `SET_FEATURE(PORT_POWER)` each downstream port.
4. 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.
5. Arm the status-change interrupt endpoint.
## Testing
QEMU's `usb-hub` is a USB 2.0 single-TT hub. A **static boot topology** on a
dedicated second controller (`-device qemu-xhci,id=xhci2 -device
usb-hub,bus=xhci2.0,port=1 -device usb-kbd,bus=xhci2.0,port=1.1` — isolated from
the boot controller's auto-assigned devices, whose ports the hub would collide
with) 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-keyboard` in 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.