# 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:

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**
(`-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-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.
