Artemis Milestone 2e: Address Device implemented, worked first try, 3-arch

Adds Slot/Endpoint/Input Control Context structs (32-byte layout only --
HCCPARAMS1.CSZ checked live and confirmed 0 against this driver's QEMU
target; 64-byte contexts refuse rather than silently mis-laying-out),
xhci_cmd_address_device(), and a new dev->connect_state
(idle/await-enable-slot/await-address-device) sequencing Enable Slot and
Address Device per connect. Input Context (what the command TRB's
parameter points at) and Device Context (what DCBAA[slot_id] points at)
are separate 64-byte-aligned allocations, lazily created once and reused
across every connect -- single-device driver scope, no free path needed.
A new EP0 Transfer Ring uses the same fixed-ring-plus-Link-TRB pattern as
the Command Ring.

Two facts checked live before writing any context code, not assumed:
HCCPARAMS1.CSZ (32-byte, confirmed) and PORTSC.PED at connect time
(already set -- PORTSC=0x00021203, SuperSpeed -- the test device
self-enables via USB3 link training, so no port-reset state machine was
needed this increment; USB2 would need one, untested). Both diagnostics
also added console_puts/println-based hex logging (xhci_log_hex32()) --
console_println() only takes string literals, no formatted print existed
on this driver's console path before now.

Verified live via QMP hotplug, all three architectures, succeeded on the
first attempt with no debugging needed: "enable slot succeeded" ->
"address device command submitted" -> "address device succeeded" on
every boot.

Also fixes a FABRIC-2.md dependency-direction error from the previous
commit (Address Device is 2f's prerequisite, not the reverse).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HZ8kNoTuP63pbQtro4qvrm
This commit is contained in:
Robert Allan James
2026-08-22 11:44:15 -04:00
co-authored by Claude Sonnet 5
parent 6d330efdd8
commit 2b7743027c
12 changed files with 45556 additions and 22 deletions
+52
View File
@@ -62,6 +62,11 @@ typedef struct {
#define XHCI_HCSPARAMS2_MAX_SCRATCHPAD_BUFS(v) \
((((uint32_t)(v) >> 21) & 0x1Fu) << 5 | (((uint32_t)(v) >> 27) & 0x1Fu))
/* HCCPARAMS1.CSZ (bit 2): 0 = 32-byte Slot/Endpoint/Input Contexts,
* 1 = 64-byte. Every context field offset shifts with this bit -- must be
* read live, never assumed, before laying out any context structure. */
#define XHCI_HCCPARAMS1_CSZ(v) (((uint32_t)(v) >> 2) & 0x1u)
/* -------------------------------------------------------------------------
* Operational Registers (BAR0 + cap_length)
* ------------------------------------------------------------------------- */
@@ -123,6 +128,7 @@ typedef struct {
#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_SPEED(v) (((uint32_t)(v) >> 10) & 0xFu) /* xHCI 1.2 spec table 7-13 speed IDs */
#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 */
@@ -207,6 +213,52 @@ typedef struct {
* 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)
/* -------------------------------------------------------------------------
* Slot Context, Endpoint Context, Input Control Context — 32-byte layout
* only (xHCI 1.2 spec tables 6-6, 6-9, 6-5). HCCPARAMS1.CSZ selects 32- vs
* 64-byte contexts; confirmed live (CSZ=0) against this driver's target
* QEMU qemu-xhci controller (2026-08-22) before writing these -- 64-byte
* contexts (CSZ=1) are NOT implemented here. xhci_cmd_address_device()
* checks CSZ itself and refuses rather than silently mis-laying-out a
* 64-byte-context controller as 32-byte.
* ------------------------------------------------------------------------- */
typedef struct {
volatile uint32_t dword0; /* Route String[19:0] Speed[23:20] MTT[25] Hub[26] Context Entries[31:27] */
volatile uint32_t dword1; /* Max Exit Latency[15:0] Root Hub Port Number[23:16] Number of Ports[31:24] */
volatile uint32_t dword2; /* Parent Hub Slot ID[7:0] Parent Port Number[15:8] TTT[17:16] Interrupter Target[31:22] */
volatile uint32_t dword3; /* USB Device Address[7:0] Slot State[31:27] */
volatile uint32_t reserved[4];
} xhci_slot_ctx32_t;
#define XHCI_SLOT_CTX_SPEED_SHIFT 20
#define XHCI_SLOT_CTX_CONTEXT_ENTRIES_SHIFT 27
#define XHCI_SLOT_CTX_ROOT_PORT_SHIFT 16
#define XHCI_SLOT_CTX_INTR_TARGET_SHIFT 22
typedef struct {
volatile uint32_t dword0; /* EP State[2:0] Interval[23:16] */
volatile uint32_t dword1; /* CErr[2:1] EP Type[5:3] Max Packet Size[31:16] */
volatile uint64_t tr_dequeue_ptr; /* [0]=DCS (Dequeue Cycle State), [63:4]=pointer */
volatile uint32_t dword4; /* Average TRB Length[15:0] */
volatile uint32_t reserved[3];
} xhci_ep_ctx32_t;
#define XHCI_EP_CTX_TYPE_SHIFT 3
#define XHCI_EP_CTX_TYPE_CONTROL_BIDI 4u /* the only EP type this driver uses so far (EP0) */
#define XHCI_EP_CTX_CERR_SHIFT 1
#define XHCI_EP_CTX_MAX_PACKET_SHIFT 16
typedef struct {
volatile uint32_t drop_flags; /* D0..D31 -- unused for Address Device (nothing to drop) */
volatile uint32_t add_flags; /* A0..A31 -- bit0=Slot, bit1=EP0 for Address Device */
volatile uint32_t reserved[5];
volatile uint32_t config_word; /* Configuration Value/Interface/AltSetting -- unused here */
} xhci_input_ctrl_ctx32_t;
#define XHCI_INPUT_CTRL_ADD_SLOT (1u << 0)
#define XHCI_INPUT_CTRL_ADD_EP0 (1u << 1)
/* -------------------------------------------------------------------------
* Ring sizing — decided up front per Milestone 2's punch list (2a).
*
+44
View File
@@ -57,6 +57,27 @@ typedef struct {
* port_id whose Command Completion Event is still outstanding. */
uint32_t port_slot_id[XHCI_MAX_TRACKED_PORTS];
uint32_t pending_connect_port_id;
uint32_t pending_connect_speed; /* PORTSC.Port Speed at connect time */
/* Milestone 2e: Address Device. This driver only ever addresses one
* device at a time (single-drive-at-a-time scope), so these are
* single, reused allocations rather than per-slot -- lazily allocated
* on the first connect that reaches xhci_cmd_address_device(), then
* reinitialised (not reallocated) on every subsequent connect. connect
* state tracks which command a still-outstanding completion event
* belongs to, since Enable Slot and Address Device are issued
* sequentially, not concurrently, for a given connect. */
enum {
XHCI_CONN_IDLE = 0,
XHCI_CONN_AWAIT_ENABLE_SLOT,
XHCI_CONN_AWAIT_ADDRESS_DEVICE
} connect_state;
uint32_t pending_connect_slot_id;
void *input_ctx; /* Input Control Ctx + Slot Ctx + EP0 Ctx (96 bytes, 32-byte contexts) */
void *device_ctx; /* Slot Ctx + EP0 Ctx (64 bytes) -- DCBAA[slot_id] points here */
xhci_trb_t *ep0_ring; /* EP0 Transfer Ring, XHCI_RING_TRB_COUNT TRBs */
uint32_t ep0_ring_cycle;
uint32_t ep0_ring_enq;
} xhci_dev_t;
/*
@@ -137,4 +158,27 @@ void xhci_poll_events(void);
*/
int xhci_cmd_enable_slot(xhci_dev_t *dev);
/*
* xhci_cmd_address_device — build the Input Context (Slot + EP0, add-only),
* program DCBAA[slot_id] with the Device Context,
* allocate the EP0 Transfer Ring, and submit an
* Address Device command TRB.
*
* speed is the PORTSC.Port Speed value read live at the connect this call
* is servicing (xHCI 1.2 spec table 7-13 speed IDs) -- used to pick EP0's
* default Max Packet Size before any device descriptor has been read.
*
* Refuses (-2) if HCCPARAMS1.CSZ indicates 64-byte contexts -- only
* 32-byte contexts are implemented (see xhci.h's own doc comment on
* xhci_slot_ctx32_t).
*
* Called from xhci_poll_events()'s Command Completion handling once Enable
* Slot succeeds -- not called directly by other code.
*
* Returns 0 if the command was posted, -1 on allocation failure, -2 if
* 64-byte contexts are required.
*/
int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
uint32_t port_id, uint32_t speed);
#endif /* STARKERNEL_XHCI_DRIVER_H */