Skip to content

feat(usb): enumerate devices on xHCI root hub ports - #739

Open
nokosaaan wants to merge 2 commits into
tier4:mainfrom
nokosaaan:usb-xhci/3-enumerate
Open

nokosaaan wants to merge 2 commits into
tier4:mainfrom
nokosaaan:usb-xhci/3-enumerate

Conversation

@nokosaaan

Copy link
Copy Markdown
Contributor

Description

Enumerate devices attached to the root hub ports of the xHCI controller brought up in #737. For each connected device, the driver now issues Enable Slot and Address Device, then reads its Device Descriptor and logs the VID/PID and class. No class driver is attached yet; that comes in the PL2303 / CDC-ACM follow-ups.

What this PR does:

  • Port power and discovery: sets PORTSC.PP on unpowered ports (xHCI §4.19.1.1), waits 500 ms for VBUS to settle, and snapshots the ports with CCS = 1 before touching any of them.
  • Per port:
    1. Port reset: set PR and wait for PRC, with the RW1CS change bits handled per spec.
    2. Enable Slot, validating the returned slot ID.
    3. Address Device (BSR = 0), with an Input Context for the Slot and EP0. The EP0 max packet size is chosen from the port speed.
    4. GET_DESCRIPTOR(Device) over an EP0 control transfer (Setup / Data / Status TRBs).
  • Control transfers: a generic HcCore::control() builds the TRBs from a typed SetupPacket and tolerates a Short Packet on the data stage.
  • Robustness: a failure on one port is logged, stale events are drained, and enumeration continues with the next port.

Changed files: pcie/usb/xhci/mod.rs, regs.rs (port register and TRB definitions), ring.rs (Transfer Ring alias and slot_id()).

Related links

  • Follows feat(usb): xHCI host controller bring-up (polled) #737 (xHCI controller bring-up).
  • Next in the series: PL2303 console mirror, then CDC-ACM.
  • xHCI Specification Rev 1.2, §4.3 (USB Device Initialization), §4.19 (Root Hub), §6.2.2 (Slot Context), §6.2.3 (Endpoint Context).

How was this PR tested?

  • scripts/ci.sh passes on my machine.
  • make qemu-x86_64 (controller only, as added in feat(usb): xHCI host controller bring-up (polled) #737): the No-Op succeeds, then no device found at boot, and the kernel boots to the shell.
  • QEMU with devices attached, using make qemu-x86_64's arguments plus -device usb-kbd,bus=xhci.0 -device usb-tablet,bus=xhci.0 -netdev user,id=un0 -device usb-net,netdev=un0,bus=xhci.0. All three devices are enumerated:
    xHCI: 0000:00:02.0 xHCI: device detected on port 5
    xHCI: 0000:00:02.0 xHCI: slot 1 assigned for port 5
    xHCI: 0000:00:02.0 xHCI: slot 1 addressed
    xHCI: 0000:00:02.0 xHCI: slot 1 — USB 2.00 VID=0x0627 PID=0x0001 class=0x00/0x00/0x00 bcdDevice=0x0000
    ...
    xHCI: 0000:00:02.0 xHCI: slot 3 — USB 2.00 VID=0x0525 PID=0xa4a2 class=0x02/0x00/0x00 bcdDevice=0x0000
    All CPUs are ready.
    
  • Without -device qemu-xhci, there is no xHCI output and boot is unchanged.

Notes for reviewers

  • DMA lifetime. Once a slot is addressed, the controller holds pointers to its Device Context and EP0 ring. Freeing them would let the controller DMA into reused memory.
    • UsbDevice is therefore always kept in ManuallyDrop, which leaks about 12 KiB per device at boot, once.
    • The transient Input Context is freed only if its command did not time out.
    • Control transfers use a persistent per-device data buffer, so a late completion after a timeout cannot write into freed memory.
  • Untrusted input. Descriptor fields from the device are read through fixed-size array patterns; nothing is indexed with device-provided lengths.
  • Bounded waits. Port reset, each command and each transfer have explicit polling budgets, and a timeout is returned as an error.
  • Scope.
    • Root hub ports only; devices behind a USB hub are not enumerated.
    • Enumeration happens once at boot; hot-plug is not handled.
  • QEMU arguments. I can also add -device usb-kbd,bus=xhci.0 to QEMU_X86_ARGS, so that make qemu-x86_64 exercises enumeration by default. Let me know if you prefer that.

nokosaaan and others added 2 commits October 20, 2026 17:50
Power the root hub ports, reset every connected port, then Enable Slot,
Address Device and read the Device Descriptor of each device over EP0
control transfers, logging its VID/PID and class.

Device Contexts and EP0 rings stay allocated for the controller's
lifetime because the controller keeps pointers to them; the control
transfer data buffer is per-device and persistent so a timed-out
transfer can never DMA into freed memory.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: nokosaaan <[email protected]>
@nokosaaan nokosaaan assigned nokosaaan and ytakano and unassigned ytakano and nokosaaan Oct 20, 2026
@ytakano

ytakano commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

@nokosaaan

Could you make the timeout recovery safe before continuing to the next port?

drain_events() only removes events that are already available. A timed-out command can still complete afterward, while command() accepts the next Command Completion event without checking its command TRB pointer.

For example, if Address Device for port A times out and completes while port B is waiting for Enable Slot, B could accept A’s completion and use the wrong slot ID. The subsequent Address Device call could then consume B’s Enable Slot completion and release its Input Context before the controller has finished using it. wait_transfer() has a similar issue because it does not match the completion to the expected slot, endpoint, or TRB.

Please match completions to their submitted operations and keep DMA buffers alive until those operations are known to be complete. Alternatively, stop enumeration after a timeout and safely quiesce the controller before releasing the associated resources or proceeding.

Could you also add a regression test with two connected ports and a completion delayed beyond the polling timeout, verifying that the late event cannot complete the next operation or cause premature buffer release?

This is a concern from code inspection, not a runtime-reproduced failure.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants