usb: hub-downstream disconnect teardown + hub-behind-hub recursion (B4c)

Completes hub support (docs/usb-hub.md). On the bus tick, a downstream
hub-port change now dispatches: a connect enumerates the new device
(B4b), a disconnect tears the old one down — recursively, since a hub
that leaves takes its whole subtree with it (children first), reporting
each interface ChildRemoved and Disable-Slotting the device. Route
strings compose across tiers, so a device two hubs deep enumerates with
a two-tier route.

QEMU's hub — unlike root-port hot-plug — DOES raise downstream
status-change events, so both paths are harness-tested: usb-hub (a
keyboard behind a hub binds usb-hid-keyboard), usb-hub-nested (a
keyboard two hubs deep), usb-hub-unplug (device_del behind the hub tears
it down). Full suite 91/91.

Real-hardware validation of the user's SuperSpeed Genesys hub with
full-speed devices on its USB2 companion is flagged for the user (QEMU's
USB2 hub does not model the compound USB3 hub).
This commit is contained in:
Daniel Samson
2026-07-21 22:16:42 +01:00
parent 9da1a9899c
commit e52ae242fc
4 changed files with 115 additions and 27 deletions
+28
View File
@@ -651,6 +651,34 @@ CASES = [
# cannot exercise the path. The hot-plug code — port-change queue, teardown
# via Disable Slot, ChildRemoved reporting — is validated on real hardware,
# flagged for the user. The qmp_sequence harness support it added remains.)
# Hub-behind-hub (B4c): route strings compose across tiers — a keyboard two
# hubs deep enumerates and binds. Static nested topology on a 2nd controller.
{"name": "usb-hub-nested",
"build_case": "usb-hid",
"smp": 4,
"timeout": 150,
"qemu_extra": ["-device", "qemu-xhci,id=xhci2",
"-device", "usb-hub,bus=xhci2.0,port=1",
"-device", "usb-hub,bus=xhci2.0,port=1.1",
"-device", "usb-kbd,bus=xhci2.0,port=1.1.1"],
"expect": r"(?s)(?=.*hub slot \d+ port \d+ device vendor 0x0409)"
r"(?=.*usb-hid-keyboard: ok \(device 3)",
"fail": r"DANOS-TEST-RESULT: FAIL"},
# Hub-downstream disconnect (B4c): device_del the keyboard behind the hub;
# the hub's status-change endpoint reports it, the device is torn down
# (ChildRemoved + Disable Slot). QEMU's hub DOES raise downstream changes
# (unlike root-port hot-plug).
{"name": "usb-hub-unplug",
"build_case": "usb-hid",
"smp": 4,
"timeout": 150,
"qemu_extra": ["-device", "qemu-xhci,id=xhci2",
"-device", "usb-hub,bus=xhci2.0,port=1",
"-device", "usb-kbd,bus=xhci2.0,port=1.1,id=dkbd"],
"qmp_after": {"delay": 8, "command": "device_del", "arguments": {"id": "dkbd"}},
"expect": r"(?s)(?=.*usb-hid-keyboard: ok \(device 3)"
r"(?=.*slot \d+ disconnected)",
"fail": r"DANOS-TEST-RESULT: FAIL"},
# The kernel VFS root (M-F): the mount table serves the initrd at /system —
# path resolution, node status/read (an ELF magic), and directory listing,
# asserted kernel-side.