xhci_poll_events()'s Port Status Change branch now decodes the Port ID from the event TRB (XHCI_PSC_EVT_PORT_ID, new in xhci.h), reads that port's PORTSC.CCS via a new xhci_port_regs() helper, and logs connect vs. disconnect. Acknowledges by writing back only PP (preserved) and CSC (the bit being cleared) -- PED/PR/other _C bits written 0 so nothing is accidentally disabled, reset, or silently cleared, matching the RW1C discipline already used for ERDP.EHB in 2d. Verified with the real target scenario via QMP hotplug on all three architectures: boot with the xHCI controller present but no USB device attached (confirmed zero port activity at ok>), then live attach/detach/re-attach of a virtual USB thumb drive (disk/usb-thumbdrive-test.img via usb-storage on xhci0.0). Full connect->disconnect->connect cycle confirmed clean (no port wedge) on amd64; single connect confirmed on aarch64 and riscv64. Still open: correlating Command Completion Events back to their issuing command, driving Enable Slot/Address Device from this connect path (currently only a boot-time smoke test), and the callback surface into Section U's higher-level code. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HZ8kNoTuP63pbQtro4qvrm
226 lines
11 KiB
C
226 lines
11 KiB
C
/*
|
|
* xhci.h — xHCI (Extensible Host Controller Interface, USB 3.x) register
|
|
* layout and shared constants for StarKernel's USB host controller driver.
|
|
*
|
|
* Register layout from the xHCI 1.2 specification. Four MMIO regions, each
|
|
* reached via an offset from PCI BAR0:
|
|
* Capability Registers — at BAR0 + 0, fixed layout, CAPLENGTH gives the
|
|
* offset to Operational Registers
|
|
* Operational Registers — at BAR0 + CAPLENGTH
|
|
* Runtime Registers — at BAR0 + RTSOFF (read from Capability Registers)
|
|
* Doorbell Array — at BAR0 + DBOFF (read from Capability Registers)
|
|
*
|
|
* Struct fields are `volatile`, naturally aligned, NOT __attribute__((packed))
|
|
* — matching virtio_blk.c's precedent and its documented riscv64 lesson:
|
|
* packed structs force byte-wise loads/stores on strict-alignment targets,
|
|
* and QEMU's MMIO handlers for exact-width registers can misbehave under
|
|
* byte-wise access. The xHCI spec's register layout is naturally aligned at
|
|
* every offset, so this translates directly without padding tricks.
|
|
*
|
|
* QEMU's `qemu-xhci` device identifies as PCI vendor 0x1B36 (Red Hat, Inc.),
|
|
* device 0x000D — confirmed live via QMP `query-pci` against a real running
|
|
* instance (not assumed from memory), 2026-08-22.
|
|
*/
|
|
|
|
#ifndef STARKERNEL_XHCI_H
|
|
#define STARKERNEL_XHCI_H
|
|
|
|
#include <stdint.h>
|
|
|
|
/* -------------------------------------------------------------------------
|
|
* PCI identification
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
#define XHCI_PCI_VENDOR_ID 0x1B36u /* Red Hat, Inc. (QEMU qemu-xhci) */
|
|
#define XHCI_PCI_DEVICE_ID 0x000Du
|
|
|
|
/* -------------------------------------------------------------------------
|
|
* Capability Registers (BAR0 + 0)
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
typedef struct {
|
|
volatile uint8_t cap_length; /* offset to Operational Registers */
|
|
volatile uint8_t reserved0;
|
|
volatile uint16_t hci_version; /* BCD xHCI spec version */
|
|
volatile uint32_t hcs_params1; /* MaxSlots[7:0], MaxIntrs[18:8], MaxPorts[31:24] */
|
|
volatile uint32_t hcs_params2; /* IST, ERST Max, scratchpad buffer counts */
|
|
volatile uint32_t hcs_params3; /* U1/U2 device exit latencies */
|
|
volatile uint32_t hcc_params1; /* AC64, BNC, CSZ, xECP pointer[31:16], etc. */
|
|
volatile uint32_t db_off; /* Doorbell Array offset (low 2 bits reserved) */
|
|
volatile uint32_t rts_off; /* Runtime Register Space offset (low 5 bits reserved) */
|
|
volatile uint32_t hcc_params2;
|
|
} xhci_cap_regs_t;
|
|
|
|
#define XHCI_HCSPARAMS1_MAX_SLOTS(v) ((uint32_t)(v) & 0xFFu)
|
|
#define XHCI_HCSPARAMS1_MAX_INTRS(v) (((uint32_t)(v) >> 8) & 0x7FFu)
|
|
#define XHCI_HCSPARAMS1_MAX_PORTS(v) (((uint32_t)(v) >> 24) & 0xFFu)
|
|
|
|
/* HCSPARAMS2: Max Scratchpad Buffers is a 10-bit field split across two
|
|
* non-adjacent locations (xHCI 1.2 spec table 5-13) — Hi bits[25:21],
|
|
* Lo bits[31:27]. Zero means the controller needs no scratchpad buffers
|
|
* (common for simple emulated controllers, but verify live, not assumed). */
|
|
#define XHCI_HCSPARAMS2_MAX_SCRATCHPAD_BUFS(v) \
|
|
((((uint32_t)(v) >> 21) & 0x1Fu) << 5 | (((uint32_t)(v) >> 27) & 0x1Fu))
|
|
|
|
/* -------------------------------------------------------------------------
|
|
* Operational Registers (BAR0 + cap_length)
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
typedef struct {
|
|
volatile uint32_t usb_cmd; /* Run/Stop, HC Reset, Interrupter Enable, ... */
|
|
volatile uint32_t usb_sts; /* HCHalted, HSE, EINT, PCD, CNR, HCE */
|
|
volatile uint32_t page_size; /* bit N set => 2^(N+12)-byte pages supported */
|
|
volatile uint32_t reserved0[2];
|
|
volatile uint32_t dn_ctrl; /* Device Notification Control */
|
|
volatile uint64_t crcr; /* Command Ring Control Register */
|
|
volatile uint32_t reserved1[4];
|
|
volatile uint64_t dcbaap; /* Device Context Base Address Array Pointer */
|
|
volatile uint32_t config; /* MaxSlotsEn[7:0] */
|
|
/* Port Register Sets follow at a fixed offset (0x400 from Operational
|
|
* base), not contiguous with the fields above — accessed via
|
|
* xhci_port_regs() below, not as a struct member. */
|
|
} xhci_op_regs_t;
|
|
|
|
/* USBCMD bits */
|
|
#define XHCI_USBCMD_RUN (1u << 0) /* Run/Stop: 1 = run */
|
|
#define XHCI_USBCMD_HCRST (1u << 1) /* HC Reset */
|
|
#define XHCI_USBCMD_INTE (1u << 2) /* Interrupter Enable */
|
|
#define XHCI_USBCMD_HSEE (1u << 3) /* Host System Error Enable */
|
|
|
|
/* USBSTS bits */
|
|
#define XHCI_USBSTS_HCH (1u << 0) /* HC Halted */
|
|
#define XHCI_USBSTS_HSE (1u << 2) /* Host System Error */
|
|
#define XHCI_USBSTS_EINT (1u << 3) /* Event Interrupt */
|
|
#define XHCI_USBSTS_PCD (1u << 4) /* Port Change Detect */
|
|
#define XHCI_USBSTS_CNR (1u << 11) /* Controller Not Ready */
|
|
#define XHCI_USBSTS_HCE (1u << 12) /* Host Controller Error */
|
|
|
|
/* CRCR bits (low bits of the 64-bit register; pointer occupies bits[63:6]) */
|
|
#define XHCI_CRCR_RCS (1ull << 0) /* Ring Cycle State */
|
|
#define XHCI_CRCR_CS (1ull << 1) /* Command Stop */
|
|
#define XHCI_CRCR_CA (1ull << 2) /* Command Abort */
|
|
#define XHCI_CRCR_CRR (1ull << 3) /* Command Ring Running (read-only) */
|
|
#define XHCI_CRCR_PTR_MASK (~0x3Full) /* pointer must be 64-byte aligned */
|
|
|
|
/* CONFIG */
|
|
#define XHCI_CONFIG_MAX_SLOTS_EN(n) ((uint32_t)(n) & 0xFFu)
|
|
|
|
/* Port Register Set — array at Operational base + 0x400, 0x10 bytes each,
|
|
* indexed 0..(MaxPorts-1) for ports numbered 1..MaxPorts. */
|
|
typedef struct {
|
|
volatile uint32_t portsc; /* Port Status and Control */
|
|
volatile uint32_t portpmsc; /* Port Power Management Status and Control */
|
|
volatile uint32_t portli; /* Port Link Info */
|
|
volatile uint32_t porthlpmc; /* Port Hardware LPM Control */
|
|
} xhci_port_regs_t;
|
|
|
|
#define XHCI_PORT_REGS_OFFSET 0x400u
|
|
|
|
/* PORTSC bits (subset needed for hotplug + reset) */
|
|
#define XHCI_PORTSC_CCS (1u << 0) /* Current Connect Status */
|
|
#define XHCI_PORTSC_PED (1u << 1) /* Port Enabled/Disabled */
|
|
#define XHCI_PORTSC_PR (1u << 4) /* Port Reset */
|
|
#define XHCI_PORTSC_PLS_MASK (0xFu << 5) /* Port Link State */
|
|
#define XHCI_PORTSC_PP (1u << 9) /* Port Power */
|
|
#define XHCI_PORTSC_SPEED_MASK (0xFu << 10)
|
|
#define XHCI_PORTSC_CSC (1u << 17) /* Connect Status Change */
|
|
#define XHCI_PORTSC_PEC (1u << 18) /* Port Enabled/Disabled Change */
|
|
#define XHCI_PORTSC_PRC (1u << 21) /* Port Reset Change */
|
|
/* Writing 1 to a _C (change) bit clears it (RW1CS) — writing 0 has no effect.
|
|
* PORTSC also has RW1CS bits interleaved with RW bits; always read-modify-
|
|
* write with the change bits masked to 0 unless intentionally clearing one,
|
|
* to avoid accidentally acknowledging an event by a stray read-modify-write. */
|
|
|
|
/* -------------------------------------------------------------------------
|
|
* Runtime Registers (BAR0 + rts_off)
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
typedef struct {
|
|
volatile uint32_t iman; /* Interrupt Management: bit0=IP, bit1=IE */
|
|
volatile uint32_t imod; /* Interrupt Moderation */
|
|
volatile uint32_t erstsz; /* Event Ring Segment Table Size */
|
|
volatile uint32_t reserved0;
|
|
volatile uint64_t erstba; /* Event Ring Segment Table Base Address */
|
|
volatile uint64_t erdp; /* Event Ring Dequeue Pointer; bit3=EHB */
|
|
} xhci_intr_regs_t;
|
|
|
|
typedef struct {
|
|
volatile uint32_t mf_index; /* Microframe Index */
|
|
volatile uint32_t reserved0[7];
|
|
/* Interrupter Register Sets follow, one xhci_intr_regs_t per interrupter,
|
|
* starting immediately after this 0x20-byte header. Interrupter 0 is
|
|
* accessed via xhci_intr_regs_t at (runtime_base + 0x20). */
|
|
} xhci_runtime_regs_t;
|
|
|
|
#define XHCI_IMAN_IP (1u << 0) /* Interrupt Pending */
|
|
#define XHCI_IMAN_IE (1u << 1) /* Interrupt Enable */
|
|
#define XHCI_ERDP_EHB (1ull << 3) /* Event Handler Busy */
|
|
#define XHCI_ERDP_PTR_MASK (~0xFull) /* pointer occupies bits[63:4] */
|
|
|
|
/* -------------------------------------------------------------------------
|
|
* Doorbell Array (BAR0 + db_off) — array of uint32_t, one per device slot
|
|
* plus doorbell 0 for the Command Ring. Write-only.
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
typedef volatile uint32_t xhci_doorbell_t;
|
|
|
|
#define XHCI_DB_TARGET(ep) ((uint32_t)(ep) & 0xFFu) /* 0 = command ring */
|
|
#define XHCI_DB_STREAM_ID(sid) (((uint32_t)(sid) & 0xFFFFu) << 16)
|
|
|
|
/* -------------------------------------------------------------------------
|
|
* TRB (Transfer Request Block) — 16 bytes, the unit of both Command Ring
|
|
* and Event Ring entries (and Transfer Rings, used later for BOT I/O).
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
typedef struct {
|
|
volatile uint64_t parameter;
|
|
volatile uint32_t status;
|
|
volatile uint32_t control;
|
|
} xhci_trb_t;
|
|
|
|
#define XHCI_TRB_CONTROL_CYCLE (1u << 0) /* Cycle bit */
|
|
#define XHCI_TRB_CONTROL_TYPE_SHIFT 10
|
|
#define XHCI_TRB_CONTROL_TYPE_MASK (0x3Fu << XHCI_TRB_CONTROL_TYPE_SHIFT)
|
|
#define XHCI_TRB_TYPE(ctrl) (((ctrl) & XHCI_TRB_CONTROL_TYPE_MASK) >> XHCI_TRB_CONTROL_TYPE_SHIFT)
|
|
|
|
/* TRB types used by this driver (subset — xHCI defines many more) */
|
|
#define XHCI_TRB_TYPE_LINK 6 /* ring-wraparound marker, Command/Transfer Rings only */
|
|
#define XHCI_TRB_TYPE_ENABLE_SLOT_CMD 9
|
|
#define XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD 11
|
|
#define XHCI_TRB_TYPE_TRANSFER_EVENT 32
|
|
#define XHCI_TRB_TYPE_COMMAND_COMPLETION_EVT 33
|
|
#define XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVT 34
|
|
|
|
/* Control bits used only by Link TRBs */
|
|
#define XHCI_TRB_CONTROL_TC (1u << 1) /* Toggle Cycle */
|
|
|
|
/* Command Completion Event TRB layout (xHCI 1.2 spec table 6-32):
|
|
* parameter[63:4] = Command TRB Pointer, status[31:24] = Completion Code,
|
|
* status[23:0] = unused here, control[31:24] = Slot ID (Enable Slot's
|
|
* result, also present on Address Device completions). */
|
|
#define XHCI_EVT_COMPLETION_CODE(status) (((uint32_t)(status) >> 24) & 0xFFu)
|
|
#define XHCI_EVT_SLOT_ID(control) (((uint32_t)(control) >> 24) & 0xFFu)
|
|
#define XHCI_COMPLETION_CODE_SUCCESS 1u
|
|
|
|
/* Port Status Change Event TRB layout (xHCI 1.2 spec table 6-34):
|
|
* parameter[31:24] = Port ID (1-based, matches PORTSC array indexing
|
|
* 1..MaxPorts); parameter[23:0] and the rest of the TRB are reserved. */
|
|
#define XHCI_PSC_EVT_PORT_ID(parameter) (((uint32_t)(parameter) >> 24) & 0xFFu)
|
|
|
|
/* -------------------------------------------------------------------------
|
|
* Ring sizing — decided up front per Milestone 2's punch list (2a).
|
|
*
|
|
* Fixed, single-page rings: 256 TRBs x 16 bytes = 4096 bytes = one page.
|
|
* This project's usage (MSC hotplug detection + read/write to one drive at
|
|
* a time) does not need a high-throughput, dynamically-growable ring —
|
|
* matches this codebase's existing preference for fixed, page-sized
|
|
* allocations over dynamic growth (e.g. KRD_MAX_BLOCKS's fixed 1024-block
|
|
* RAMDRIVE). One Command Ring, one Event Ring (Interrupter 0 only — this
|
|
* driver does not use multiple interrupters).
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
#define XHCI_RING_TRB_COUNT 256u
|
|
#define XHCI_RING_BYTES (XHCI_RING_TRB_COUNT * sizeof(xhci_trb_t))
|
|
|
|
#endif /* STARKERNEL_XHCI_H */
|