Artemis Milestone 2g: Configure Endpoint command

Adds the xHCI Configure Endpoint command for the two bulk endpoints
identified by the previous increment, and fixes control-transfer
sequencing to match the spec: xHCI 1.2 section 4.3.5 requires Configure
Endpoint before SET_CONFIGURATION is sent to the device, the reverse of
the order this driver used through 2f (which happened to work against
QEMU's lenient qemu-xhci emulation but wasn't spec-correct).

New XHCI_TRB_TYPE_CONFIGURE_ENDPOINT_CMD, EP Context type constants for
Bulk IN/OUT, and an XHCI_EP_ADDR_TO_DCI() macro (DCI = 2*EndpointNumber
+ Direction) in xhci.h. xhci_cmd_configure_endpoint() builds the Input
Context (Slot + one EP Context per DCI up to the highest bulk endpoint
in use) and submits the command via the existing next_action deferral
mechanism, correlated on completion via a new
XHCI_CONN_AWAIT_CONFIGURE_ENDPOINT connect_state, then chains into the
existing SET_CONFIGURATION path.

Two allocations had to grow beyond what Address Device sized them for:
the Input Context (previously room for one EP Context only) and, less
obviously, the Device Context that DCBAA[slot_id] itself points at --
the controller only touches DCIs named in a command's own Add/Drop
flags, so growing that buffer required copying its existing Slot+EP0
content forward rather than zeroing it, to avoid handing the controller
a blank EP0 out from under an endpoint this command isn't touching.
Bulk Transfer Rings (bulk_in_ring/bulk_out_ring) are allocated and
wired into the new EP Contexts but not yet exercised by an actual
transfer -- CBW/CSW submission is next.

Verified live via QMP hotplug, all three architectures, byte-identical:
bulk endpoint identification -> configure endpoint command submitted ->
configure endpoint succeeded -> the existing set configuration ->
device configured chain, then a clean disconnect/disable-slot teardown
afterward with the larger Device Context installed.

FABRIC-2.md Section X Milestone 2g's endpoint identify+configure
checklist item marked fully done.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R4VMX6VSKCten8nGgaMkq4
This commit is contained in:
Robert Allan James
2026-08-25 08:40:35 -04:00
co-authored by Claude Sonnet 5
parent 96d55fcd87
commit 92ce1f85dd
12 changed files with 27511 additions and 17 deletions
+15 -1
View File
@@ -194,6 +194,7 @@ typedef struct {
#define XHCI_TRB_TYPE_ENABLE_SLOT_CMD 9
#define XHCI_TRB_TYPE_DISABLE_SLOT_CMD 10
#define XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD 11
#define XHCI_TRB_TYPE_CONFIGURE_ENDPOINT_CMD 12
#define XHCI_TRB_TYPE_SETUP_STAGE 2 /* Transfer Ring, control transfers only */
#define XHCI_TRB_TYPE_DATA_STAGE 3
#define XHCI_TRB_TYPE_STATUS_STAGE 4
@@ -319,10 +320,23 @@ typedef struct {
} 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_TYPE_CONTROL_BIDI 4u /* EP0 */
#define XHCI_EP_CTX_TYPE_BULK_OUT 2u /* xHCI 1.2 spec table 6-9 */
#define XHCI_EP_CTX_TYPE_BULK_IN 6u
#define XHCI_EP_CTX_CERR_SHIFT 1
#define XHCI_EP_CTX_MAX_PACKET_SHIFT 16
/* Device Context Index (DCI) for a given endpoint, xHCI 1.2 spec section
* 4.5.1: DCI = 2*EndpointNumber + Direction (Direction=1 for IN, 0 for
* OUT/control) -- EP0 is always DCI 1 regardless of direction, a special
* case this macro does not need to handle since EP0's DCI is hardcoded
* (XHCI_INPUT_CTRL_ADD_EP0's bit position) everywhere it's used. Takes a
* full bEndpointAddress (bit 7 = direction, bits 3:0 = number), matching
* how bulk_in_ep_addr/bulk_out_ep_addr are stored. */
#define XHCI_EP_ADDR_TO_DCI(addr) \
((2u * ((uint32_t)(addr) & USB_EP_ADDR_NUM_MASK)) + \
(((uint32_t)(addr) & USB_EP_ADDR_DIR_MASK) ? 1u : 0u))
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 */
+51 -1
View File
@@ -71,7 +71,8 @@ typedef struct {
XHCI_CONN_IDLE = 0,
XHCI_CONN_AWAIT_ENABLE_SLOT,
XHCI_CONN_AWAIT_ADDRESS_DEVICE,
XHCI_CONN_AWAIT_DISABLE_SLOT
XHCI_CONN_AWAIT_DISABLE_SLOT,
XHCI_CONN_AWAIT_CONFIGURE_ENDPOINT
} connect_state;
uint32_t pending_connect_slot_id;
/* Milestone 2e/2g: disconnect teardown. Same single-outstanding-
@@ -132,6 +133,21 @@ typedef struct {
uint8_t bulk_out_ep_addr;
uint16_t bulk_out_max_packet;
/* Milestone 2g: bulk endpoint Transfer Rings, one per direction --
* same fixed-ring-plus-Link-TRB pattern as ep0_ring, lazily allocated
* once and reused across every connect (single-device scope, matching
* every other ring in this driver). Not usable for actual transfers
* until xhci_cmd_configure_endpoint() succeeds -- allocating them
* early (rather than only after success) keeps the allocation site in
* one place and lets the Input Context's EP Contexts point at real,
* already-initialised rings before the command is even submitted. */
xhci_trb_t *bulk_in_ring;
uint32_t bulk_in_ring_cycle;
uint32_t bulk_in_ring_enq;
xhci_trb_t *bulk_out_ring;
uint32_t bulk_out_ring_cycle;
uint32_t bulk_out_ring_enq;
/* Deferred chaining: a doorbell ring (new control transfer) must
* never happen synchronously from inside xhci_poll_events()'s event-
* processing loop, before ERDP has been updated for the event
@@ -147,6 +163,7 @@ typedef struct {
XHCI_NEXT_ACTION_NONE = 0,
XHCI_NEXT_ACTION_GET_DEVICE_DESC,
XHCI_NEXT_ACTION_GET_CONFIG_DESC,
XHCI_NEXT_ACTION_CONFIGURE_ENDPOINT,
XHCI_NEXT_ACTION_SET_CONFIG
} next_action;
uint32_t next_action_slot_id;
@@ -273,6 +290,39 @@ int xhci_cmd_disable_slot(xhci_dev_t *dev, uint32_t slot_id);
int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
uint32_t port_id, uint32_t speed);
/*
* xhci_cmd_configure_endpoint — build the Input Context (Slot + the two
* bulk EP Contexts, add-only), allocate the
* bulk Transfer Rings, and submit a
* Configure Endpoint command TRB for
* slot_id.
*
* Per xHCI 1.2 spec section 4.3.5, this must be issued after enumeration
* has identified the endpoints a device's chosen configuration/interface
* actually uses, and before the USB SET_CONFIGURATION request is sent to
* the device -- the reverse of that order (which this driver used before
* this increment) works against QEMU's lenient emulation but is not
* spec-correct. Requires dev->bulk_in_ep_addr/bulk_out_ep_addr to already
* be populated (2f's config descriptor walk) -- refuses if either is
* still 0 (not found).
*
* The Slot Context's Route String/Speed/Root Hub Port/Interrupter Target
* fields are copied from the already-addressed device's own Device
* Context (populated by a prior successful Address Device) rather than
* reconstructed from scratch -- those values aren't retained anywhere
* else by the time enumeration reaches this point (pending_connect_port_id
* is cleared as soon as Address Device completes). Only Context Entries is
* changed, to the highest DCI now in use.
*
* Called from xhci_poll_events()'s deferred next_action dispatch once the
* full Configuration descriptor has confirmed a Mass Storage/BOT interface
* and identified both bulk endpoints -- not called directly by other code.
*
* Returns 0 if the command was posted, -1 on allocation failure or missing
* prerequisite state, -2 if 64-byte contexts are required.
*/
int xhci_cmd_configure_endpoint(xhci_dev_t *dev, uint32_t slot_id);
/*
* xhci_ep0_get_device_descriptor — issue a standard GET_DESCRIPTOR
* (Device) control transfer (Setup +