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:
co-authored by
Claude Sonnet 5
parent
6d330efdd8
commit
2b7743027c
+42
-7
@@ -3207,9 +3207,10 @@ Final three-arch acceptance (probe-free, `clean qemu`): all three boot to `ok>`
|
||||
**2e. Hotplug detection (the actual trigger for everything in Section U)**
|
||||
- [x] On a Port Status Change event, read the corresponding Port Register to determine
|
||||
connect vs. disconnect — **done 2026-08-22**, see writeup below
|
||||
- [x] On connect: allocate a Device Slot (Enable Slot command via the Command Ring) — **Enable
|
||||
Slot done 2026-08-22**, see writeup below; Address Device + device descriptor read still
|
||||
open (needs enumeration groundwork from 2f)
|
||||
- [x] On connect: allocate a Device Slot (Enable Slot command via the Command Ring), address
|
||||
the device (Address Device command) — **both done 2026-08-22**, see writeup below.
|
||||
Device descriptor read is 2f's job and depends on this (not the reverse — an earlier
|
||||
version of this note had the dependency backwards)
|
||||
- [ ] On disconnect: tear down the corresponding device slot and signal to whatever higher-
|
||||
level code (Section U's identity/VM logic) that the device is gone — **this is the
|
||||
first point where Section U/V's design actually gets a real trigger to hang off**
|
||||
@@ -3282,10 +3283,44 @@ disconnect/reconnect cycle repeats cleanly with no wedge. Full cycle on amd64
|
||||
(`logs/20260822-102849/amd64/`); single connect confirmed on aarch64
|
||||
(`logs/20260822-103005/aarch64/`) and riscv64 (`logs/20260822-103137/riscv64/`).
|
||||
|
||||
Still open: correlating the Command Completion Event back to its issuing command via the real
|
||||
Command TRB Pointer (not needed yet, see above), Address Device + device descriptor read
|
||||
(needs 2f's enumeration groundwork), and the callback surface into Section U's higher-level
|
||||
code.
|
||||
**Address Device, done 2026-08-22.** Sequenced after Enable Slot succeeds, via a new
|
||||
`dev->connect_state` (`XHCI_CONN_IDLE` / `_AWAIT_ENABLE_SLOT` / `_AWAIT_ADDRESS_DEVICE`) —
|
||||
Enable Slot and Address Device are issued sequentially for a given connect, never concurrently,
|
||||
so `connect_state` alone identifies which command a Command Completion Event answers; real
|
||||
Command TRB Pointer matching is still deferred until commands from different connects can
|
||||
overlap in flight.
|
||||
|
||||
Two things checked live before writing any context code, per the same "verify, don't assume"
|
||||
discipline used for the amd64 PIRQ formula in 2d: **`HCCPARAMS1.CSZ`** (32- vs 64-byte Slot/
|
||||
Endpoint/Input Context layout — confirmed `CSZ=0`, 32-byte, on this driver's target QEMU
|
||||
`qemu-xhci`; only 32-byte contexts are implemented, `xhci_cmd_address_device()` refuses rather
|
||||
than mis-laying-out a 64-byte controller) and **`PORTSC.PED`** at connect time (whether the
|
||||
port already reached Default state, or needs a software-driven `PORTSC.PR` reset first —
|
||||
confirmed `PED` already set, `PORTSC=0x00021203`, SuperSpeed, on this test device: the USB3
|
||||
link trains and enables itself, so no port-reset state machine was needed for this increment;
|
||||
a USB2 device may still need one, untested here).
|
||||
|
||||
Layout: **Input Context** (Input Control Context + Slot Context + EP0 Context, 96 bytes) is
|
||||
what the command TRB's `parameter` points at; **Device Context** (Slot Context + EP0 Context,
|
||||
64 bytes, no Input Control Context) is what `DCBAA[slot_id]` points at — two separate
|
||||
allocations, conflating them is the standard mistake here. Both, plus a new EP0 Transfer Ring
|
||||
(same fixed-ring-plus-Link-TRB pattern as the Command Ring), are lazily allocated once and
|
||||
reused across every connect — single-device scope, no free path exists or is needed. Slot
|
||||
Context: Route String 0 (directly on root hub), Speed from the live `PORTSC.Port Speed` read
|
||||
at connect time, Context Entries=1, Root Hub Port Number=the connecting port. EP0 Context:
|
||||
Control Bidirectional, CErr=3, Max Packet Size defaulted by speed (SuperSpeed 512/High 64/
|
||||
Full 64/Low 8 — spec's own recommended pre-descriptor-read defaults, corrected once 2f reads
|
||||
the real `bMaxPacketSize0`), Average TRB Length=8.
|
||||
|
||||
Verified live via QMP hotplug, all three architectures, first attempt: `enable slot
|
||||
succeeded` -> `address device command submitted` -> `address device succeeded` on every boot,
|
||||
no debugging needed. `logs/20260822-113852/amd64/`, `logs/20260822-114043/aarch64/`,
|
||||
`logs/20260822-114214/riscv64/`.
|
||||
|
||||
Still open: correlating Command Completion Events via the real Command TRB Pointer (not needed
|
||||
yet, see above), device descriptor read (2f), USB2 port-reset handling (untested — this
|
||||
increment's test device was SuperSpeed and self-enabled), disconnect teardown (Disable Slot
|
||||
command, DCBAA entry clear), and the callback surface into Section U's higher-level code.
|
||||
|
||||
**2f. USB device enumeration (post-connect, before it's usable as storage)**
|
||||
- [ ] Request and parse the device descriptor (confirm vendor/product IDs are even needed,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-22T14:31:35Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-22T15:42:12Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
@@ -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).
|
||||
*
|
||||
|
||||
@@ -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 */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+186
-14
@@ -94,6 +94,27 @@ static int xhci_wait_bit(volatile uint32_t *reg, uint32_t mask, int want_set,
|
||||
}
|
||||
}
|
||||
|
||||
/* console_println() only takes a string literal -- no formatted print
|
||||
* exists on this driver's console path. Matches the established pattern
|
||||
* elsewhere in this kernel (e.g. src/starkernel/vm/parity.c's
|
||||
* print_hex64()) rather than adding one: a small static hex-dump helper,
|
||||
* "label: 0xXXXXXXXX". Debugging register values without this is
|
||||
* guesswork. */
|
||||
static void xhci_log_hex32(const char *label, uint32_t val)
|
||||
{
|
||||
char buf[11];
|
||||
buf[0] = '0';
|
||||
buf[1] = 'x';
|
||||
for (int i = 9; i >= 2; i--) {
|
||||
int d = val & 0xF;
|
||||
buf[i] = (d < 10) ? ('0' + d) : ('a' + d - 10);
|
||||
val >>= 4;
|
||||
}
|
||||
buf[10] = '\0';
|
||||
console_puts(label);
|
||||
console_println(buf);
|
||||
}
|
||||
|
||||
/* Only one controller is supported (matches xhci_dev_t's own doc comment);
|
||||
* latched at the end of a successful xhci_bringup() for
|
||||
* xhci_poll_events()'s use. */
|
||||
@@ -104,6 +125,8 @@ static xhci_dev_t *g_xhci_dev = NULL;
|
||||
* (see the "Command Ring submission" section) so it can stay close to
|
||||
* xhci_poll_events(), the read side of the same ring pair. */
|
||||
int xhci_cmd_enable_slot(xhci_dev_t *dev);
|
||||
int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
|
||||
uint32_t port_id, uint32_t speed);
|
||||
|
||||
int xhci_bringup(xhci_dev_t *dev)
|
||||
{
|
||||
@@ -255,8 +278,23 @@ int xhci_bringup(xhci_dev_t *dev)
|
||||
* see xhci_dev_t's own doc comment; no allocation needed. */
|
||||
for (uint32_t i = 0; i < XHCI_MAX_TRACKED_PORTS; i++) dev->port_slot_id[i] = 0;
|
||||
dev->pending_connect_port_id = 0;
|
||||
dev->pending_connect_speed = 0;
|
||||
dev->connect_state = XHCI_CONN_IDLE;
|
||||
dev->pending_connect_slot_id = 0;
|
||||
dev->input_ctx = NULL;
|
||||
dev->device_ctx = NULL;
|
||||
dev->ep0_ring = NULL;
|
||||
dev->ep0_ring_cycle = 1;
|
||||
dev->ep0_ring_enq = 0;
|
||||
|
||||
console_println("xhci: controller running");
|
||||
/* Milestone 2e prep: HCCPARAMS1.CSZ decides 32- vs 64-byte Slot/
|
||||
* Endpoint/Input Context layout for Address Device -- must be read
|
||||
* live, not assumed, before any context structure is designed. */
|
||||
xhci_log_hex32("xhci: hcc_params1=", dev->cap->hcc_params1);
|
||||
console_println(XHCI_HCCPARAMS1_CSZ(dev->cap->hcc_params1)
|
||||
? "xhci: context size = 64 bytes"
|
||||
: "xhci: context size = 32 bytes");
|
||||
|
||||
g_xhci_dev = dev;
|
||||
return 0;
|
||||
@@ -268,13 +306,18 @@ int xhci_bringup(xhci_dev_t *dev)
|
||||
* same ring pair, already verified live for Port Status Change events.
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/* extra_control_bits ORs additional fields into the TRB's control dword
|
||||
* beyond type/cycle -- e.g. Address Device's Slot ID at bits[31:24]
|
||||
* (Enable Slot needs none, passes 0). Never includes the cycle bit itself;
|
||||
* that's computed here from cmd_ring_cycle so callers can't get it wrong. */
|
||||
static void xhci_submit_command(xhci_dev_t *dev, uint64_t parameter,
|
||||
uint32_t status, uint32_t trb_type)
|
||||
uint32_t status, uint32_t trb_type,
|
||||
uint32_t extra_control_bits)
|
||||
{
|
||||
xhci_trb_t *trb = &dev->cmd_ring[dev->cmd_ring_enq];
|
||||
trb->parameter = parameter;
|
||||
trb->status = status;
|
||||
trb->control = (trb_type << XHCI_TRB_CONTROL_TYPE_SHIFT) |
|
||||
trb->control = (trb_type << XHCI_TRB_CONTROL_TYPE_SHIFT) | extra_control_bits |
|
||||
(dev->cmd_ring_cycle ? XHCI_TRB_CONTROL_CYCLE : 0);
|
||||
|
||||
dev->cmd_ring_enq++;
|
||||
@@ -299,11 +342,110 @@ static void xhci_submit_command(xhci_dev_t *dev, uint64_t parameter,
|
||||
int xhci_cmd_enable_slot(xhci_dev_t *dev)
|
||||
{
|
||||
if (!dev || !dev->cmd_ring) return -1;
|
||||
xhci_submit_command(dev, 0, 0, XHCI_TRB_TYPE_ENABLE_SLOT_CMD);
|
||||
xhci_submit_command(dev, 0, 0, XHCI_TRB_TYPE_ENABLE_SLOT_CMD, 0);
|
||||
console_println("xhci: enable slot command submitted");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Default EP0 Max Packet Size by PORTSC.Port Speed, used before any device
|
||||
* descriptor has been read (xHCI 1.2 spec's own recommended defaults --
|
||||
* the real value comes from bMaxPacketSize0 once 2f reads the device
|
||||
* descriptor and issues an Evaluate Context to correct it if needed). */
|
||||
static uint32_t xhci_default_ep0_max_packet(uint32_t speed)
|
||||
{
|
||||
switch (speed) {
|
||||
case 4: return 512; /* SuperSpeed */
|
||||
case 3: return 64; /* High Speed */
|
||||
case 2: return 8; /* Low Speed */
|
||||
default: return 64; /* Full Speed (1) and anything unrecognised */
|
||||
}
|
||||
}
|
||||
|
||||
int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
|
||||
uint32_t port_id, uint32_t speed)
|
||||
{
|
||||
if (!dev || !dev->cmd_ring || !dev->dcbaa) return -1;
|
||||
|
||||
if (XHCI_HCCPARAMS1_CSZ(dev->cap->hcc_params1)) {
|
||||
console_println("xhci: 64-byte contexts required, not implemented -- refusing");
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Lazily allocate once; reused across every connect (single-device
|
||||
* scope -- see xhci_dev_t's doc comment). All three re-initialised
|
||||
* fully below regardless of whether this is the first call. */
|
||||
if (!dev->input_ctx) {
|
||||
dev->input_ctx = kmalloc_aligned(
|
||||
sizeof(xhci_input_ctrl_ctx32_t) + sizeof(xhci_slot_ctx32_t) +
|
||||
sizeof(xhci_ep_ctx32_t), 64);
|
||||
if (!dev->input_ctx) return -1;
|
||||
}
|
||||
if (!dev->device_ctx) {
|
||||
dev->device_ctx = kmalloc_aligned(
|
||||
sizeof(xhci_slot_ctx32_t) + sizeof(xhci_ep_ctx32_t), 64);
|
||||
if (!dev->device_ctx) return -1;
|
||||
}
|
||||
if (!dev->ep0_ring) {
|
||||
dev->ep0_ring = (xhci_trb_t *)kmalloc_aligned(XHCI_RING_BYTES, 64);
|
||||
if (!dev->ep0_ring) return -1;
|
||||
}
|
||||
|
||||
/* EP0 Transfer Ring: same fixed-ring-plus-Link-TRB pattern as the
|
||||
* Command Ring (xhci_bringup()'s own comment on why). Freshly
|
||||
* reinitialised on every call, not just the first -- cheap (4KiB) and
|
||||
* avoids carrying stale TRBs from a previous connect. */
|
||||
for (uint32_t i = 0; i < XHCI_RING_TRB_COUNT; i++) {
|
||||
dev->ep0_ring[i].parameter = 0;
|
||||
dev->ep0_ring[i].status = 0;
|
||||
dev->ep0_ring[i].control = 0;
|
||||
}
|
||||
dev->ep0_ring[XHCI_RING_TRB_COUNT - 1].parameter = (uint64_t)(uintptr_t)dev->ep0_ring;
|
||||
dev->ep0_ring[XHCI_RING_TRB_COUNT - 1].control =
|
||||
(XHCI_TRB_TYPE_LINK << XHCI_TRB_CONTROL_TYPE_SHIFT) |
|
||||
XHCI_TRB_CONTROL_TC | XHCI_TRB_CONTROL_CYCLE;
|
||||
dev->ep0_ring_cycle = 1;
|
||||
dev->ep0_ring_enq = 0;
|
||||
|
||||
/* Device Context: Slot Context followed by EP0 Context, no Input
|
||||
* Control Context (that only exists in the Input Context below).
|
||||
* DCBAA[slot_id] must point here, per spec -- zeroed since the
|
||||
* controller writes this on Address Device success, software must not
|
||||
* pre-fill it. */
|
||||
uint8_t *dctx = (uint8_t *)dev->device_ctx;
|
||||
for (size_t i = 0; i < sizeof(xhci_slot_ctx32_t) + sizeof(xhci_ep_ctx32_t); i++) dctx[i] = 0;
|
||||
((uint64_t *)dev->dcbaa)[slot_id] = (uint64_t)(uintptr_t)dev->device_ctx;
|
||||
|
||||
/* Input Context: Input Control Context, then Slot Context, then EP0
|
||||
* Context -- this is what the command TRB's parameter points at (never
|
||||
* the Device Context; conflating the two is the standard mistake
|
||||
* here). */
|
||||
uint8_t *ictx = (uint8_t *)dev->input_ctx;
|
||||
size_t total = sizeof(xhci_input_ctrl_ctx32_t) + sizeof(xhci_slot_ctx32_t) +
|
||||
sizeof(xhci_ep_ctx32_t);
|
||||
for (size_t i = 0; i < total; i++) ictx[i] = 0;
|
||||
|
||||
xhci_input_ctrl_ctx32_t *ctrl = (xhci_input_ctrl_ctx32_t *)ictx;
|
||||
ctrl->add_flags = XHCI_INPUT_CTRL_ADD_SLOT | XHCI_INPUT_CTRL_ADD_EP0;
|
||||
|
||||
xhci_slot_ctx32_t *slot = (xhci_slot_ctx32_t *)(ictx + sizeof(xhci_input_ctrl_ctx32_t));
|
||||
slot->dword0 = (speed << XHCI_SLOT_CTX_SPEED_SHIFT) | (1u << XHCI_SLOT_CTX_CONTEXT_ENTRIES_SHIFT);
|
||||
slot->dword1 = port_id << XHCI_SLOT_CTX_ROOT_PORT_SHIFT;
|
||||
slot->dword2 = 0u << XHCI_SLOT_CTX_INTR_TARGET_SHIFT; /* Interrupter 0 */
|
||||
|
||||
xhci_ep_ctx32_t *ep0 = (xhci_ep_ctx32_t *)(ictx + sizeof(xhci_input_ctrl_ctx32_t) +
|
||||
sizeof(xhci_slot_ctx32_t));
|
||||
ep0->dword1 = (3u << XHCI_EP_CTX_CERR_SHIFT) |
|
||||
(XHCI_EP_CTX_TYPE_CONTROL_BIDI << XHCI_EP_CTX_TYPE_SHIFT) |
|
||||
(xhci_default_ep0_max_packet(speed) << XHCI_EP_CTX_MAX_PACKET_SHIFT);
|
||||
ep0->tr_dequeue_ptr = ((uint64_t)(uintptr_t)dev->ep0_ring) | 1u; /* DCS = 1 */
|
||||
ep0->dword4 = 8u; /* Average TRB Length -- spec's own recommended default for EP0 */
|
||||
|
||||
xhci_submit_command(dev, (uint64_t)(uintptr_t)dev->input_ctx, 0,
|
||||
XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD, slot_id << 24);
|
||||
console_println("xhci: address device command submitted");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Milestone 2d: Event Ring servicing, polled from sk_repl_idle().
|
||||
*
|
||||
@@ -352,6 +494,17 @@ void xhci_poll_events(void)
|
||||
uint32_t portsc = port->portsc;
|
||||
if (portsc & XHCI_PORTSC_CCS) {
|
||||
console_println("xhci: port status change -- device connected");
|
||||
/* Milestone 2e prep: Address Device requires the port
|
||||
* in Default state. USB3 links train and enable
|
||||
* themselves; USB2 needs software to drive PORTSC.PR
|
||||
* and wait for PRC/PED before the device will respond
|
||||
* to addressing -- not yet known which this driver's
|
||||
* ports need, so log raw PORTSC and PED rather than
|
||||
* assume. */
|
||||
xhci_log_hex32("xhci: portsc=", portsc);
|
||||
console_println((portsc & XHCI_PORTSC_PED)
|
||||
? "xhci: port enabled (PED set)"
|
||||
: "xhci: port not yet enabled (PED clear)");
|
||||
/* Only one Enable Slot in flight at a time (see
|
||||
* xhci_dev_t's doc comment) -- if another connect's
|
||||
* slot request is still outstanding, this one is
|
||||
@@ -361,8 +514,10 @@ void xhci_poll_events(void)
|
||||
* scenario. */
|
||||
if (port_id > XHCI_MAX_TRACKED_PORTS) {
|
||||
console_println("xhci: port beyond tracked range -- enable slot skipped");
|
||||
} else if (dev->pending_connect_port_id == 0) {
|
||||
} else if (dev->connect_state == XHCI_CONN_IDLE) {
|
||||
dev->pending_connect_port_id = port_id;
|
||||
dev->pending_connect_speed = XHCI_PORTSC_SPEED(portsc);
|
||||
dev->connect_state = XHCI_CONN_AWAIT_ENABLE_SLOT;
|
||||
xhci_cmd_enable_slot(dev);
|
||||
} else {
|
||||
console_println("xhci: enable slot already pending -- dropped");
|
||||
@@ -390,24 +545,41 @@ void xhci_poll_events(void)
|
||||
case XHCI_TRB_TYPE_COMMAND_COMPLETION_EVT: {
|
||||
uint32_t code = XHCI_EVT_COMPLETION_CODE(trb->status);
|
||||
uint32_t slot_id = XHCI_EVT_SLOT_ID(trb->control);
|
||||
/* Correlates to the single in-flight Enable Slot, not to
|
||||
* the Command TRB Pointer in trb->parameter -- this driver
|
||||
* only ever has one command outstanding (see
|
||||
* xhci_dev_t's doc comment), so pending_connect_port_id
|
||||
* alone is enough to identify which port this completion
|
||||
* belongs to; a real Command TRB Pointer match becomes
|
||||
* necessary once Address Device commands can also be
|
||||
* in flight concurrently with Enable Slot. */
|
||||
if (dev->pending_connect_port_id != 0) {
|
||||
/* Correlates to connect_state, not to the Command TRB
|
||||
* Pointer in trb->parameter -- Enable Slot and Address
|
||||
* Device are issued sequentially for a given connect (see
|
||||
* xhci_dev_t's doc comment), never concurrently, so
|
||||
* connect_state alone identifies which command this
|
||||
* completion answers. A real Command TRB Pointer match
|
||||
* becomes necessary once commands for different connects
|
||||
* can overlap in flight. */
|
||||
if (dev->connect_state == XHCI_CONN_AWAIT_ENABLE_SLOT) {
|
||||
uint32_t port_id = dev->pending_connect_port_id;
|
||||
dev->pending_connect_port_id = 0;
|
||||
if (code == XHCI_COMPLETION_CODE_SUCCESS &&
|
||||
port_id >= 1 && port_id <= XHCI_MAX_TRACKED_PORTS) {
|
||||
dev->port_slot_id[port_id - 1] = slot_id;
|
||||
dev->pending_connect_slot_id = slot_id;
|
||||
console_println("xhci: enable slot succeeded");
|
||||
dev->connect_state = XHCI_CONN_AWAIT_ADDRESS_DEVICE;
|
||||
if (xhci_cmd_address_device(dev, slot_id, port_id,
|
||||
dev->pending_connect_speed) != 0) {
|
||||
console_println("xhci: address device setup failed");
|
||||
dev->connect_state = XHCI_CONN_IDLE;
|
||||
dev->pending_connect_port_id = 0;
|
||||
}
|
||||
} else {
|
||||
console_println("xhci: enable slot failed");
|
||||
dev->connect_state = XHCI_CONN_IDLE;
|
||||
dev->pending_connect_port_id = 0;
|
||||
}
|
||||
} else if (dev->connect_state == XHCI_CONN_AWAIT_ADDRESS_DEVICE) {
|
||||
if (code == XHCI_COMPLETION_CODE_SUCCESS) {
|
||||
console_println("xhci: address device succeeded");
|
||||
} else {
|
||||
console_println("xhci: address device failed");
|
||||
}
|
||||
dev->connect_state = XHCI_CONN_IDLE;
|
||||
dev->pending_connect_port_id = 0;
|
||||
} else {
|
||||
console_println("xhci: command completion event");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user