Artemis Milestone 2e (in progress): xHCI Command Ring write path proven live

Enable Slot command TRB submitted via a new xhci_submit_command()/
xhci_cmd_enable_slot(), ring doorbell 0, confirmed by a real Command
Completion Event on all three architectures -- the first time this driver
has written a TRB rather than only reading the Event Ring (2d). Added the
Command Ring's previously-missing Link TRB (xHCI 1.2 spec sec 4.9.2) for
wraparound correctness.

Port Register connect/disconnect handling, slot-ID/context bookkeeping,
Address Device, and the callback surface into Section U's code are still
open -- this is the discriminating first step, not full 2e.

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 09:37:55 -04:00
co-authored by Claude Sonnet 5
parent 2b16daba16
commit 9c8ad8f6ff
11 changed files with 27242 additions and 2 deletions
+73
View File
@@ -98,6 +98,12 @@ static int xhci_wait_bit(volatile uint32_t *reg, uint32_t mask, int want_set,
* xhci_poll_events()'s use. */
static xhci_dev_t *g_xhci_dev = NULL;
/* Forward declaration -- xhci_bringup() below issues one Enable Slot as a
* command-ring smoke test; the implementation lives after xhci_bringup()
* (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_bringup(xhci_dev_t *dev)
{
if (!dev || !dev->op) return -2;
@@ -182,6 +188,20 @@ int xhci_bringup(xhci_dev_t *dev)
dev->cmd_ring[i].control = 0;
}
dev->cmd_ring_cycle = 1;
dev->cmd_ring_enq = 0;
/* Last slot is a permanent Link TRB back to index 0 (xHCI 1.2 spec
* §4.9.2 — software must terminate every ring segment with one; without
* it the controller reads uninitialised memory past the segment instead
* of wrapping). Toggle Cycle (TC) tells the controller to flip its own
* consumer cycle state when it processes this TRB, matching the
* producer-side toggle xhci_submit_command() below performs on wrap. */
dev->cmd_ring[XHCI_RING_TRB_COUNT - 1].parameter =
(uint64_t)(uintptr_t)dev->cmd_ring;
dev->cmd_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->op->crcr = ((uint64_t)(uintptr_t)dev->cmd_ring & XHCI_CRCR_PTR_MASK) |
XHCI_CRCR_RCS;
@@ -233,6 +253,59 @@ int xhci_bringup(xhci_dev_t *dev)
console_println("xhci: controller running");
g_xhci_dev = dev;
/* Milestone 2e smoke test: prove the write path (TRB enqueue, cycle
* bit, doorbell ring) before building slot allocation on top of it.
* A real Enable Slot is harmless to issue speculatively -- it just
* reserves a Device Slot Context the driver doesn't use yet -- and its
* Command Completion Event is the only live proof that a TRB written by
* software was actually consumed by the controller. Real connect-driven
* Enable Slot calls (Milestone 2e proper) replace/reuse this call site
* once Port Status Change handling exists. */
xhci_cmd_enable_slot(dev);
return 0;
}
/* -------------------------------------------------------------------------
* Command Ring submission -- Milestone 2e. Shared by Enable Slot now and
* Address Device next; xhci_poll_events() above is the read side of this
* same ring pair, already verified live for Port Status Change events.
* ------------------------------------------------------------------------- */
static void xhci_submit_command(xhci_dev_t *dev, uint64_t parameter,
uint32_t status, uint32_t trb_type)
{
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) |
(dev->cmd_ring_cycle ? XHCI_TRB_CONTROL_CYCLE : 0);
dev->cmd_ring_enq++;
if (dev->cmd_ring_enq == XHCI_RING_TRB_COUNT - 1) {
/* About to hand the Link TRB to the controller -- its cycle bit
* must match the producer cycle state at the moment of handoff,
* and the producer state flips here too (this is the wrap). */
dev->cmd_ring[XHCI_RING_TRB_COUNT - 1].control =
(XHCI_TRB_TYPE_LINK << XHCI_TRB_CONTROL_TYPE_SHIFT) |
XHCI_TRB_CONTROL_TC |
(dev->cmd_ring_cycle ? XHCI_TRB_CONTROL_CYCLE : 0);
dev->cmd_ring_enq = 0;
dev->cmd_ring_cycle ^= 1u;
}
/* Doorbell 0 targets the Command Ring (XHCI_DB_TARGET(0)); write-only,
* one write per new TRB posted -- xhci.h's own doc comment on the
* Doorbell Array. */
dev->doorbell[0] = XHCI_DB_TARGET(0);
}
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);
console_println("xhci: enable slot command submitted");
return 0;
}