Files
Asuro/doc/planning/XHCI.md
T
t3hn3rd 26e2fd947e feature: add mkdocs-material documentation site with CI deployment
Generate comprehensive documentation for all 173 source units and
integrate mkdocs-material into the build toolchain. Documentation
covers kernel core, x86 architecture, drivers, applications, and
all subsystems with a tabbed navigation structure.

- Add mkdocs.yml with material theme, mermaid support, and tabbed nav
- Add doc/index.md landing page
- Generate doc/src/ markdown for every Pascal unit in src/
- Reorganize planning docs into doc/planning/
- Add compat, lvglh, and toolchain doc sections
- Update Dockerfile to install python3 and mkdocs-material (pinned <2)
- Replace pasdoc-based compile_docs.sh with mkdocs build --strict
- Add compile_docs.sh to the main build pipeline in compile.sh
- Add deploy-docs step in .drone.yml with host volume mount to nginx
- Add site/.gitkeep for build output directory
2026-03-08 21:06:14 +00:00

467 lines
25 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.
# xHCI Implementation Plan
> Living document — tracks design decisions, architecture, progress, and open questions for the xHCI (USB 3.0) host controller driver.
---
## 0. Progress Tracker
| Stage | Status | Notes |
|-------|--------|-------|
| Stage 1: Constants & Types | **Done** | Register offsets, TRB types, packed records |
| Stage 2: MMIO & Allocation Helpers | **Done** | 32/64-bit MMIO, ring/context allocation |
| Stage 3: Ring Management | **Done** | Enqueue/dequeue, cycle bit, command ring |
| Stage 4: Init/Reset/Start/Stop | **Done** | BIOS handoff, DCBAA, scratchpad, event ring |
| Stage 5: Port Status & Reset | **Done** | PORTSC, Enable Slot, Address Device |
| Stage 6: Submit & Poll | **Done** | TRB submission, event ring processing |
| Stage 7: Load + UnitTests | **Done** | PCI scan, HC registration, unit tests |
| Stage 8: kernel.pas Integration | **Done** | uses clause, UnitTest call, build+test |
**Estimated total:** ~1800-2000 lines (EHCI was 1717 lines; xHCI is more complex but has less legacy baggage)
---
## 1. Architecture Overview
xHCI (eXtensible Host Controller Interface) is fundamentally different from UHCI/OHCI/EHCI.
Instead of frame lists, QHs, and TDs, xHCI uses a **ring-based** command/transfer/event model.
### Key Differences from EHCI
| Concept | EHCI | xHCI |
|---|---|---|
| I/O model | MMIO (BAR0 32-bit) | MMIO (BAR0, 64-bit capable) |
| PCI prog_if | `$20` | `$30` |
| Transfer descriptors | QH + qTD chains | TRB (Transfer Request Block) rings |
| Scheduling | Frame list + async list | Command Ring + per-EP Transfer Rings |
| Completions | Poll qTD ACTIVE bits | Event Ring with dequeue pointer |
| Device management | Software SET_ADDRESS | Controller manages slots (Enable Slot, Address Device commands) |
| Speed support | High-speed only (companions for LS/FS) | All speeds (LS/FS/HS/SS) natively |
| Notifications | Interrupts + polling | Doorbell Registers |
### Register Layout (MMIO)
```
BAR0 + 0x00 Capability Registers (read-only)
CAPLENGTH (8-bit) → offset to Operational regs
HCIVERSION (16-bit) → spec version
HCSPARAMS1 (32-bit) → MaxSlots, MaxIntrs, MaxPorts
HCSPARAMS2 (32-bit) → scratchpad info
HCSPARAMS3 (32-bit) → latency
HCCPARAMS1 (32-bit) → addressing capability, XECP pointer
DBOFF (32-bit) → Doorbell Array offset from BAR0
RTSOFF (32-bit) → Runtime Register Space offset from BAR0
BAR0 + CAPLENGTH Operational Registers
USBCMD ($00) → Run/Stop, HCRST, etc.
USBSTS ($04) → HCHalted, HSE, EINT, PCD
DNCTRL ($14) → Device Notification Control
CRCR ($18) → Command Ring Control (64-bit)
DCBAAP ($30) → Device Context Base Address Array Pointer (64-bit)
CONFIG ($38) → Max Device Slots Enabled
PORTSC[n] ($400 + 16*n) → Port Status & Control
BAR0 + RTSOFF Runtime Registers
Interrupter[n]:
IMAN ($20 + 32*n + $00) → Interrupt Management
IMOD ($20 + 32*n + $04) → Interrupt Moderation
ERSTSZ ($20 + 32*n + $08) → Event Ring Segment Table Size
ERSTBA ($20 + 32*n + $10) → Event Ring Segment Table Base Address (64-bit)
ERDP ($20 + 32*n + $18) → Event Ring Dequeue Pointer (64-bit)
BAR0 + DBOFF Doorbell Registers
DB[0] → Host Controller (Command Ring)
DB[n] → Device Slot n (Transfer Ring for target endpoint)
```
### Data Structures (Hardware)
**TRB (Transfer Request Block)** — 16 bytes, 16-byte aligned
```
Offset Size Field
$00 8 Parameter (address or immediate data)
$04 4 Status (transfer length, completion code, etc.)
$08 4 Control (TRB Type, Cycle bit, flags)
```
**Device Context** — 32 or 64 bytes per slot/endpoint context entry
- Slot Context: route string, speed, hub info, etc.
- Endpoint Context[0..30]: EP type, max packet, TR dequeue pointer, etc.
**Input Context** — Same layout, used to program device/endpoint changes
**DCBAA** — Device Context Base Address Array: array of 64-bit pointers, one per slot + slot 0 (scratchpad)
**Event Ring Segment Table Entry** — 16 bytes
```
Offset Size Field
$00 8 Ring Segment Base Address
$08 4 Ring Segment Size (number of TRBs)
$0C 4 Reserved
```
### Ring Model
- **Command Ring**: Kernel enqueues command TRBs, rings doorbell 0. Controller processes and posts Command Completion Event on Event Ring.
- **Transfer Ring**: Per-endpoint. Kernel enqueues transfer TRBs, rings the device's doorbell. Controller processes and posts Transfer Event on Event Ring.
- **Event Ring**: Controller writes completion TRBs. Software reads and advances dequeue pointer.
- **Cycle Bit**: Producer toggles cycle bit at wrap; consumer checks it to detect new entries without needing a separate write pointer.
### Integration with usbcore Abstraction
The existing `TUSBHCDriver` function pointers map to xHCI as follows:
| Callback | xHCI Implementation |
|---|---|
| `fnReset` | Halt + HCRST, wait, allocate DCBAA/rings/scratchpad |
| `fnStart` | Set MaxSlots, DCBAAP, CRCR, Interrupter 0, set RS=1 |
| `fnStop` | Clear RS, wait for HCHalted |
| `fnPortStatus` | Read PORTSC[port], return bit 0 = CCS |
| `fnPortReset` | Set PORTSC.PR, wait for PRC, Enable Slot + Address Device |
| `fnSubmit` | Build TRBs on transfer ring, ring doorbell |
| `fnPoll` | Read Event Ring, process completions, advance ERDP |
### SET_ADDRESS Interception (Critical)
xHCI manages USB addresses internally via the **Address Device** command (issued during `fnPortReset`). The usbcore `enumerate_device` flow calls `usb_set_address()` which submits a SET_ADDRESS control transfer. Our `fnSubmit` **must intercept** `SET_ADDRESS` requests:
1. Detect: `transfer^.PipeType = ptControl` AND `transfer^.Setup.bRequest = USB_REQ_SET_ADDRESS`
2. Action: Set `transfer^.Status := tsSuccess`, return `true` — do NOT send to hardware
3. Reason: xHCI already assigned the address during Enable Slot / Address Device in `fnPortReset`
The usbcore then sets `dev^.Address := addr` and increments `hc^.NextAddress`. For xHCI, the actual USB address is managed by the controller (stored in the Device Context), and the address used by software is the **Slot ID** (1-based). We set `dev^.Address` to the slot ID during `fnPortReset` so subsequent transfers target the correct slot.
**Approach:** In `xhci_port_reset`, after Address Device succeeds, store the slot ID in a slot→port mapping table in `TXHCI_PrivData`, and set the device's address in the HC's `NextAddress`. When usbcore calls `usb_set_address(dev, addr)`, our submit intercepts it and returns success. The device's `Address` field effectively becomes the slot ID.
### Slot ↔ Port Mapping
xHCI uses slot IDs (1–MaxSlots) to identify devices. We need a mapping:
```
TXHCI_PrivData:
SlotPort : array[1..256] of uint8 { slot → root port number }
SlotSpeed : array[1..256] of uint8 { slot → USB_SPEED_* }
SlotRing : array[1..256] of Pointer { slot → EP0 Transfer Ring }
SlotCtx : array[1..256] of Pointer { slot → Output Device Context }
```
These arrays are dynamically allocated (MaxSlots * element_size) to avoid kernel bloat. MaxSlots is capped at 256 (xHCI spec max) but typically 32–64 in practice.
---
## 2. Implementation Stages
### Stage 1: Constants & Types (~200 lines)
- All register offsets (capability, operational, runtime, doorbell)
- Bit definitions for USBCMD, USBSTS, PORTSC, HCSPARAMS1/2/3, HCCPARAMS1
- PORTSC speed bits (Port Speed field bits 13:10)
- TRB type codes: Normal ($01), Setup Stage ($02), Data Stage ($03), Status Stage ($04), Link ($06), Enable Slot ($09), Disable Slot ($0A), Address Device ($0B), Configure Endpoint ($0C), Evaluate Context ($0D), Reset Endpoint ($0E), No Op Command ($17), Transfer Event ($20), Command Completion Event ($21), Port Status Change Event ($22)
- TRB flag bits: Cycle ($01), ENT ($02), ISP ($04), NS ($08), CH ($10), IOC ($20), IDT ($40)
- TRB completion codes: Success ($01), Data Buffer Error ($02), Babble ($03), USB Transaction Error ($04), TRB Error ($05), Stall ($06), Short Packet ($0D), etc.
- Packed records:
- `TXHCI_TRB` — 16 bytes (Param: uint32[2], Status: uint32, Control: uint32)
- `TXHCI_SlotCtx` — 32 bytes (route, speed, hub info, port, max exit latency, etc.)
- `TXHCI_EPCtx` — 32 bytes (EP type, max packet, interval, TR dequeue ptr, avg TRB len, etc.)
- `TXHCI_InputCtrlCtx` — 32 bytes (Drop/Add context flags)
- `TXHCI_ERSTE` — 16 bytes (Ring Segment Base Address, Ring Segment Size, Reserved)
- Private data record: `TXHCI_PrivData` (see below)
- Ring descriptor: `TXHCI_Ring` (Base pointer, Size, Enqueue index, Cycle bit)
### Stage 2: MMIO & Allocation Helpers (~100 lines)
- `xhci_readl(base, reg)` / `xhci_writel(base, reg, val)` — 32-bit MMIO (same pattern as EHCI)
- `xhci_readq(base, reg)` / `xhci_writeq(base, reg, val)` — 64-bit MMIO (two 32-bit reads/writes, low then high, for i386)
- `xhci_alloc_ring(size)` — allocate a TRB ring (page-aligned, zeroed), place Link TRB at last entry pointing back to start
- `xhci_free_ring(ring)` — free ring via `kfree_aligned`
- `xhci_alloc_ctx(ctxSize)` — allocate device/input context (64-byte aligned, zeroed)
- `xhci_free_ctx(ctx)` — free context via `kfree_aligned`
- `xhci_context_size(hcc)` — returns 32 or 64 based on HCCPARAMS1.AC64 bit
### Stage 3: Ring Management (~150 lines)
- `xhci_ring_enqueue(ring, trb)` — copy TRB to ring at enqueue index, set cycle bit, advance enqueue. If next slot is Link TRB, toggle cycle and wrap to index 0.
- `xhci_ring_doorbell(priv, slotID, target)` — write to doorbell register: `DB[slotID] = target`
- `xhci_event_pending(priv)` — check if Event Ring has a pending event (cycle bit matches software CCS)
- `xhci_event_dequeue(priv, trb)` — read next event TRB into `trb`, advance dequeue, handle segment wrap
- `xhci_event_advance_erdp(priv)` — write current dequeue pointer to ERDP register
- `xhci_send_command(priv, trb)` — enqueue command TRB, ring doorbell 0
- `xhci_wait_command(priv, completionTrb, timeout)` — spin-poll Event Ring for Command Completion Event, return completion code
- `xhci_make_trb(param0, param1, status, control)` — helper to build a TRB from components
### Stage 4: Init/Reset/Start/Stop (~250 lines)
- `xhci_bios_handoff(priv, pciDev)` — Walk XECP chain (HCCPARAMS1 bits 31:16) to find USBLEGSUP capability (ID=1). Set OS Owned Semaphore (bit 24), wait for BIOS Owned (bit 16) to clear. Same pattern as EHCI but via XECP pointer, not EECP.
- `xhci_reset(hc)`:
1. Clear USBCMD.RS, wait for USBSTS.HCH=1
2. Set USBCMD.HCRST, wait for self-clear AND USBSTS.CNR=0 (Controller Not Ready)
3. Allocate DCBAA: `(MaxSlots + 1) * 8` bytes, 64-byte aligned
4. Allocate Command Ring: 256 TRBs (4KB page), Link TRB at entry 255
5. Allocate Event Ring: 256 TRBs (4KB page)
6. Allocate Event Ring Segment Table: 1 entry (16 bytes), 64-byte aligned
7. Allocate scratchpad buffers if HCSPARAMS2 indicates any needed
8. Allocate slot arrays (SlotPort, SlotSpeed, SlotRing, SlotCtx) sized to MaxSlots
- `xhci_start(hc)`:
1. Write CONFIG register: MaxSlots Enabled = MaxSlots
2. Write DCBAAP (64-bit) = physical address of DCBAA
3. Write CRCR (64-bit) = physical address of Command Ring | initial cycle bit
4. Program Interrupter 0: ERSTSZ=1, ERSTBA=physical ERST, ERDP=physical Event Ring base
5. Set USBCMD.RS=1, USBCMD.INTE=1, USBCMD.HSEE=1
6. Wait for USBSTS.HCH=0
- `xhci_stop(hc)`:
1. Clear USBCMD.RS
2. Wait for USBSTS.HCH=1
- `xhci_alloc_scratchpad(priv)`:
1. Read Max Scratchpad Bufs from HCSPARAMS2 (bits 25:21 high, bits 4:0 from HCSPARAMS2 high word)
2. If > 0: allocate scratchpad buffer array (N * 8 bytes, 64-byte aligned), allocate N pages, fill array with physical addresses, store array pointer at DCBAA[0]
### Stage 5: Port Status & Reset (~200 lines)
- `xhci_port_status(hc, port)`:
- Read PORTSC[port] at OpBase + $400 + (port * $10)
- Return raw PORTSC value (bit 0 = CCS for usbcore compatibility)
- `xhci_port_speed(portsc)`:
- Extract Port Speed field (bits 13:10)
- Map: 1=Full, 2=Low, 3=High, 4=Super → USB_SPEED_*
- `xhci_port_reset(hc, port)`:
1. Read PORTSC, check CCS (Current Connect Status)
2. Determine if USB2 or USB3 port (xHCI assigns port numbers: USB2 ports first, then USB3)
3. For USB2: set PORTSC.PR (Port Reset, bit 4), wait for PRC (bit 21)
4. For USB3: set PORTSC.WPR (Warm Port Reset, bit 31) if needed, wait for PRC
5. Clear change bits (CSC, PRC, etc.) by writing 1s
6. Read speed from PORTSC
7. **Enable Slot:** Send Enable Slot command TRB, wait for completion → get Slot ID
8. **Build Input Context:** Allocate Input Context, set Input Control Context (Add flags for Slot + EP0), fill Slot Context (speed, route, root hub port, context entries=1), fill EP0 Context (EP type=Control Bidirectional, MaxPacketSize based on speed, TR Dequeue Pointer = EP0 ring, CErr=3, avg TRB length=8)
9. **Address Device:** Send Address Device command TRB (Input Context pointer, Slot ID), wait for completion
10. Store slot→port mapping, store EP0 ring pointer, store output device context pointer (from DCBAA[SlotID])
11. Set `hc^.NextAddress` to slot ID so usbcore assigns this as the device address
12. Return true on success
### Stage 6: Submit & Poll (~250 lines)
- `xhci_find_slot(priv, devAddr)` — look up slot ID from device address (for xHCI, dev address IS the slot ID)
- `xhci_submit_control(hc, transfer)`:
1. **Intercept SET_ADDRESS:** If `transfer^.Setup.bRequest = USB_REQ_SET_ADDRESS`, set `transfer^.Status := tsSuccess`, return true
2. Find slot ID from `transfer^.Device^.Address`
3. Get EP0 Transfer Ring for this slot
4. Build **Setup Stage TRB**: IDT=1 (Immediate Data), TRT (Transfer Type) based on direction, 8 bytes of setup data in Parameter fields
5. Build **Data Stage TRB(s)** if BufferLen > 0: physical buffer address, transfer length, direction bit, chain if multiple
6. Build **Status Stage TRB**: direction opposite to data (or IN if no data), IOC=1
7. Ring doorbell for slot (DB[SlotID], target = 1 for EP0)
8. Store transfer reference for poll completion
- `xhci_submit_bulk_intr(hc, transfer)`:
1. Find slot ID
2. Determine EP ring from endpoint address → DCI (Device Context Index = epNum * 2 + direction)
3. Build **Normal TRB(s)**: buffer physical address, transfer length, IOC on last TRB
4. Ring doorbell (DB[SlotID], target = DCI)
- `xhci_submit(hc, transfer)` — dispatch: control → `xhci_submit_control`, bulk/interrupt → `xhci_submit_bulk_intr`
- `xhci_poll(hc)`:
1. While event pending on Event Ring:
a. Read event TRB
b. Check TRB type:
- **Transfer Event ($20):** Extract Slot ID, Endpoint ID, completion code, transfer length. Find matching pending transfer. Update `transfer^.Status` and `transfer^.ActualLen`. Free EP ring resources if needed.
- **Command Completion Event ($21):** Handled by `xhci_wait_command` (set a flag/store result in priv data for the blocking waiter)
- **Port Status Change Event ($22):** Log the event, could trigger hot-plug enumeration (future)
c. Advance dequeue pointer
2. Write ERDP to acknowledge processed events
### Stage 7: Load + UnitTests (~250 lines)
- `load`:
1. `PCI.getDeviceInfo($0C, $03, $30, count)` — find xHCI controllers
2. For each controller:
a. Read BAR0 (handle 64-bit BAR: BAR0 low + BAR1 high, mask low 4 bits; for i386 we only use the low 32 bits)
b. Map MMIO: `block := mmioBase SHR 22; force_alloc_block(block, 0); map_page(block, block)`
c. Enable PCI bus mastering: `PCI.setBusMaster(bus, slot, func, true)`
d. Read capability registers: CAPLENGTH, HCIVERSION, HCSPARAMS1/2/3, HCCPARAMS1, DBOFF, RTSOFF
e. Compute OpBase = mmioBase + CAPLENGTH, RTBase = mmioBase + RTSOFF, DBBase = mmioBase + DBOFF
f. Allocate `TXHCI_PrivData`, populate fields
g. BIOS handoff
h. Build `TUSBHCDriver` record (same pattern as EHCI.load)
i. Reset → Start → `usbcore.register_hc` → `usbcore.scan_ports`
- `UnitTest`:
- Structure size checks: `TXHCI_TRB=16`, `TXHCI_SlotCtx=32`, `TXHCI_EPCtx=32`, `TXHCI_InputCtrlCtx=32`, `TXHCI_ERSTE=16`
- TRB construction helpers: verify type encoding, cycle bit, parameter placement
- Ring allocation: verify page alignment, Link TRB at end
- Ring enqueue/dequeue: verify cycle bit toggling on wrap
- Context size helper: AC64=0 → 32, AC64=1 → 64
- Port speed mapping: PORTSC speed field → USB_SPEED_*
- Constant sanity checks (register offsets, TRB types, bit masks)
- DCI calculation: epNum, direction → DCI
- Estimated: ~80-100 assertions
### Stage 8: kernel.pas Integration + Build
- kernel.pas already has `XHCI` in the uses chain via `USB.pas` → `XHCI` (USB.pas imports XHCI)
- But kernel.pas does NOT currently import XHCI directly or call `XHCI.UnitTest`
- Changes needed:
1. Add `XHCI` to kernel.pas uses clause (after EHCI)
2. Add `XHCI.UnitTest;` call after `EHCI.UnitTest;`
3. Build: `docker-compose run builder | Tee-Object -FilePath .\build.log`
4. Check: `Select-String -Path .\build.log -Pattern "Error|Fatal|Success|No errors|Failed" -CaseSensitive`
5. Test: Enable USB 3.0 (xHCI) controller in VirtualBox VM settings, boot, check serial log
---
## 3. Concrete Record Definitions
### TXHCI_TRB (16 bytes, 16-byte aligned)
```pascal
PXHCI_TRB = ^TXHCI_TRB;
TXHCI_TRB = packed record
Param0 : uint32; { Parameter low dword (address low or immediate data) }
Param1 : uint32; { Parameter high dword (address high or immediate data) }
Status : uint32; { Status / Transfer Length / Completion Code }
Control : uint32; { Cycle bit, TRB Type (bits 15:10), flags }
end;
```
### TXHCI_Ring (software descriptor for a TRB ring)
```pascal
PXHCI_Ring = ^TXHCI_Ring;
TXHCI_Ring = record
Base : PXHCI_TRB; { Virtual base address of ring buffer }
Size : uint32; { Number of TRB entries (including Link TRB) }
Enqueue : uint32; { Current enqueue index }
Dequeue : uint32; { Current dequeue index (Event Ring only) }
CycleBit : uint8; { Current producer cycle state (0 or 1) }
end;
```
### TXHCI_SlotCtx (32 bytes)
```pascal
PXHCI_SlotCtx = ^TXHCI_SlotCtx;
TXHCI_SlotCtx = packed record
RouteAndSpeed : uint32; { Route String (19:0), Speed (23:20), MTT (25), Hub (26), Context Entries (31:27) }
LatencyAndPort : uint32; { Max Exit Latency (15:0), Root Hub Port Number (23:16), Num Ports (31:24) }
ParentInfo : uint32; { Parent Hub Slot ID (7:0), Parent Port (15:8), TTT (17:16), Interrupter Target (31:22) }
DevStateAddr : uint32; { Device Address (7:0), Slot State (31:27) }
Reserved : array[0..3] of uint32; { Pad to 32 bytes }
end;
```
### TXHCI_EPCtx (32 bytes)
```pascal
PXHCI_EPCtx = ^TXHCI_EPCtx;
TXHCI_EPCtx = packed record
EPInfo1 : uint32; { Interval (23:16), LSA (15), MaxPStreams (14:10), Mult (9:8), EP State (2:0) }
EPInfo2 : uint32; { Max Packet Size (31:16), Max Burst Size (15:8), HID (7), EP Type (5:3), CErr (2:1) }
TRDeqLo : uint32; { TR Dequeue Pointer Low (physical) | DCS (bit 0) }
TRDeqHi : uint32; { TR Dequeue Pointer High }
EPInfo3 : uint32; { Average TRB Length (15:0), Max ESIT Payload (31:16) }
Reserved : array[0..2] of uint32; { Pad to 32 bytes }
end;
```
### TXHCI_InputCtrlCtx (32 bytes)
```pascal
PXHCI_InputCtrlCtx = ^TXHCI_InputCtrlCtx;
TXHCI_InputCtrlCtx = packed record
DropFlags : uint32; { Drop Context flags (bits 31:2, bit 1=slot context) }
AddFlags : uint32; { Add Context flags (bit 0=slot, bit 1=EP0, etc.) }
Reserved : array[0..5] of uint32; { Pad to 32 bytes }
end;
```
### TXHCI_ERSTE (16 bytes, 64-byte aligned)
```pascal
PXHCI_ERSTE = ^TXHCI_ERSTE;
TXHCI_ERSTE = packed record
BaseAddrLo : uint32; { Ring Segment Base Address Low (physical) }
BaseAddrHi : uint32; { Ring Segment Base Address High }
SegSize : uint32; { Ring Segment Size (number of TRBs) }
Reserved : uint32;
end;
```
### TXHCI_PrivData
```pascal
PXHCI_PrivData = ^TXHCI_PrivData;
TXHCI_PrivData = record
MMIOBase : uint32; { BAR0 MMIO base (virtual = physical) }
OpBase : uint32; { Operational registers = MMIOBase + CAPLENGTH }
RTBase : uint32; { Runtime registers = MMIOBase + RTSOFF }
DBBase : uint32; { Doorbell array = MMIOBase + DBOFF }
MaxSlots : uint8; { From HCSPARAMS1 bits 7:0 }
MaxIntrs : uint16; { From HCSPARAMS1 bits 18:8 }
MaxPorts : uint8; { From HCSPARAMS1 bits 31:24 }
CtxSize : uint8; { 32 or 64 bytes per context entry }
HCSPARAMS1 : uint32; { Cached }
HCSPARAMS2 : uint32; { Cached }
HCCPARAMS1 : uint32; { Cached }
DCBAA : Pointer; { Device Context Base Address Array (physical-accessible) }
CmdRing : TXHCI_Ring; { Command Ring descriptor }
EvtRing : TXHCI_Ring; { Event Ring descriptor (Interrupter 0) }
ERST : PXHCI_ERSTE; { Event Ring Segment Table }
ScratchBufs : Pointer; { Scratchpad buffer array (or nil) }
NumScratch : uint16; { Number of scratchpad buffers }
{ Per-slot data (dynamically allocated arrays, indexed 1..MaxSlots) }
SlotPort : Pointer; { ^array of uint8: slot → root port }
SlotSpeed : Pointer; { ^array of uint8: slot → USB_SPEED_* }
SlotRings : Pointer; { ^array of PXHCI_Ring: slot → EP0 Transfer Ring }
SlotOutCtx : Pointer; { ^array of Pointer: slot → Output Device Context }
{ Pending command completion (for blocking xhci_wait_command) }
CmdComplete : boolean;
CmdResult : uint32; { Completion TRB for last command }
CmdResultCode : uint8; { Completion code }
PCIBus : uint8;
PCISlot : uint8;
PCIFunc : uint8;
end;
```
---
## 4. Size Constraints
All hardware structures must be packed records with exact sizes:
- TRB: 16 bytes, 16-byte aligned
- Slot/Endpoint Context: 32 bytes each (standard, 64 bytes if AC64=1 in HCCPARAMS1)
- Input Control Context: 32 bytes (or 64 if AC64=1)
- Device Context: Slot + 31 EP Contexts = 32 * 32 = 1024 bytes (or 2048 with 64-byte contexts)
- Input Context: Input Control + Slot + 31 EP = 33 * 32 = 1056 bytes (or 33 * 64 = 2112)
- DCBAA: (MaxSlots + 1) * 8 bytes, 64-byte aligned
- Event Ring Segment Table: 16 bytes per entry, 64-byte aligned
- TRB Ring: N * 16 bytes (+ Link TRB), page aligned preferred
- Scratchpad Buffer Array: MaxScratchpad * 8 bytes, 64-byte aligned
---
## 5. Known Constraints (Asuro-specific)
- `mod` operator is broken on bare-metal i386 (promotes to int64). Use branch arithmetic instead.
- PCI config access: `PCI.requestConfig(bus, slot, func, row)` + `inl($CFC)` for reads; `PCI.writeConfig(bus, slot, func, row, val)` for writes.
- DMA addresses: use `vtop()` from `vmemorymanager` to convert virtual→physical.
- MMIO mapping: `block := mmioBase SHR 22; force_alloc_block(block, 0); map_page(block, block)`. May need multiple blocks if registers span a 4MB boundary.
- No classes, no runtime library. Standard Pascal only.
- Use `kalloc()`/`kfree()` for dynamic allocation. `kalloc_aligned()` for aligned allocations.
- Large static arrays cause kernel bloat — prefer dynamic allocation.
- Ring 0 execution — an exception = system hang. Defensive programming required.
- 64-bit register access on i386: xHCI has many 64-bit registers (CRCR, DCBAAP, ERSTBA, ERDP). Must write as two 32-bit writes (low dword first, then high dword = 0 since we're 32-bit).
---
## 6. Lessons Learned from EHCI Implementation
These patterns from the EHCI driver (1717 lines) carry directly into xHCI:
1. **BIOS handoff** — essential for VirtualBox; without it, BIOS may still own the controller
2. **MMIO block mapping** — use `force_alloc_block` / `map_page` with `mmioBase SHR 22`
3. **PCI bus mastering** — must call `PCI.setBusMaster(bus, slot, func, true)` for DMA
4. **vtop() for all hardware-visible pointers** — every pointer written to HW registers or data structures must be physical
5. **HC driver record on stack, copied via register_hc** — `usbcore.register_hc` copies the record into the LL; the returned `hcEntry` pointer is what gets passed to `scan_ports`
6. **Forward declarations** for HC callbacks before implementation
7. **kalloc_aligned + memset(0)** for all hardware structures
8. **Unit test pattern** — local Assert/PrintSummary procedures, test helpers and constants without touching hardware
9. **Speed detection during port reset** — determine speed from port status register, needed for device context setup
---
## 7. VirtualBox Testing Notes
- Enable USB 3.0 (xHCI) controller in VM settings → Settings → USB → USB 3.0
- VirtualBox emulates Intel 7 Series/C210 Series xHCI (VID=8086, DID=1E31) or similar
- xHCI controller appears at PCI class $0C, subclass $03, prog_if $30
- VirtualBox does NOT automatically attach USB devices — must add USB filters or manually attach
- VBox emulated USB tablet (default pointing device) goes through OHCI/EHCI, not xHCI
- For testing: attach a USB device via VBox USB filter, or use the built-in emulated devices
- Serial log: `Get-Content "C:\Temp\asuro.log"` — filter for `XHCI` tag