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:
co-authored by
Claude Sonnet 5
parent
96d55fcd87
commit
92ce1f85dd
+50
-8
@@ -3451,11 +3451,8 @@ arch afterward, no wedge. `logs/20260825-073235/amd64/`, `logs/20260825-073417/a
|
||||
`logs/20260825-073728/riscv64/`.
|
||||
|
||||
**2g. Bulk-Only Transport (BOT) — the actual read/write path**
|
||||
- [~] Identify the device's bulk IN and bulk OUT endpoints — **identification done
|
||||
2026-08-25**, see writeup below; *configuring* them (a Configure Endpoint command,
|
||||
wiring their EP Contexts/DCIs so the controller will actually run transfers on them)
|
||||
is still open — this increment only reads the addresses out of the descriptor, it
|
||||
doesn't yet act on them
|
||||
- [x] Identify and configure the device's bulk IN and bulk OUT endpoints — **identification
|
||||
done 2026-08-25, configuration done 2026-08-25**, see writeups below
|
||||
- [ ] Implement CBW (Command Block Wrapper) construction and send, for a SCSI READ(10)
|
||||
- [ ] Implement CSW (Command Status Wrapper) receive and status check
|
||||
- [ ] Get one real SCSI READ(10) working end to end — first proof the whole stack works,
|
||||
@@ -3489,9 +3486,54 @@ individually-tracked instance — see the disconnect-teardown writeup above for
|
||||
matters), all three architectures, byte-identical results everywhere: `bulk IN
|
||||
endpoint=0x81`, `bulk OUT endpoint=0x02`, immediately followed by the existing `set
|
||||
configuration submitted` → `device configured` chain, unaffected. `logs/20260825-081058/amd64/`,
|
||||
`logs/20260825-081300/aarch64/`, `logs/20260825-081517/riscv64/`. *Configuring* these
|
||||
endpoints (a Configure Endpoint command, EP Context setup) is the next open item in this
|
||||
milestone — this increment only identifies them.
|
||||
`logs/20260825-081300/aarch64/`, `logs/20260825-081517/riscv64/`.
|
||||
|
||||
**Configure Endpoint command, done 2026-08-25.** Per xHCI 1.2 spec section 4.3.5, a Configure
|
||||
Endpoint command must be issued once enumeration has identified the endpoints a device's
|
||||
configuration actually uses, and *before* the USB SET_CONFIGURATION request is sent to the
|
||||
device — the reverse of the order this driver used through 2f (SET_CONFIGURATION issued with
|
||||
no endpoints ever configured). That earlier order happened to work against QEMU's lenient
|
||||
`qemu-xhci` emulation, but wasn't spec-correct; this increment fixes the sequencing as part of
|
||||
adding the command, chaining Configure Endpoint in between the bulk-endpoint-identification
|
||||
step and SET_CONFIGURATION via the same `next_action` deferral mechanism as every other
|
||||
control transfer/command in this driver (a new `XHCI_NEXT_ACTION_CONFIGURE_ENDPOINT` value,
|
||||
and a new `XHCI_CONN_AWAIT_CONFIGURE_ENDPOINT` connect-state to correlate the command's own
|
||||
Command Completion Event, same pattern as Enable Slot/Address Device/Disable Slot above).
|
||||
|
||||
Two things had to grow beyond what Address Device allocated, both realised only while writing
|
||||
this: the **Input Context** (Address Device's 96-byte allocation only ever held one EP
|
||||
Context; Configure Endpoint needs one EP Context per Device Context Index up to the highest
|
||||
DCI now in use — DCI = `2*EndpointNumber + Direction`, `XHCI_EP_ADDR_TO_DCI()`, new in
|
||||
`xhci.h` — so it's reallocated at a size covering the Slot Context plus every DCI from 1
|
||||
through the bulk endpoints' own, unused intermediate DCIs left zeroed since their Add flags
|
||||
are 0), and the **Device Context** that `DCBAA[slot_id]` itself points at (same reasoning,
|
||||
but with a sharper constraint: xHCI 1.2 spec section 4.6.6 has the controller touch only the
|
||||
DCIs actually named in a command's Add/Drop flags, so the *existing* Slot+EP0 content has to
|
||||
be copied into the newly, larger buffer rather than left zeroed — a freshly zeroed Device
|
||||
Context would hand the controller a blank EP0 out from under an endpoint this command isn't
|
||||
touching, corrupting live control-transfer state). The Slot Context written into the Input
|
||||
Context is itself copied from the live Device Context (Route String/Speed/Root Hub
|
||||
Port/Interrupter Target aren't retained anywhere else by this point in enumeration —
|
||||
`pending_connect_port_id` is cleared as soon as Address Device completes), with only Context
|
||||
Entries changed to the new highest DCI.
|
||||
|
||||
Max Burst Size is left at 0 (single-burst) in both bulk EP Contexts — SuperSpeed Endpoint
|
||||
Companion descriptor parsing isn't implemented, so this driver doesn't yet know a real
|
||||
device's burst capability; matches every device tested against so far under QEMU's emulation,
|
||||
flagged as a revisit point if a real high-throughput SS device needs it. Average TRB Length is
|
||||
a placeholder (1024, a scheduling hint per spec, not a correctness constraint) until real
|
||||
CBW/data/CSW transfer sizes are known — 2g's next items.
|
||||
|
||||
Verified live via QMP hotplug, all three architectures, byte-identical results: `xhci: bulk IN
|
||||
endpoint=0x81` / `bulk OUT endpoint=0x02` → `configure endpoint command submitted` →
|
||||
`configure endpoint succeeded` → the existing `set configuration submitted` → `device
|
||||
configured` chain, unaffected. Disconnect confirmed clean afterward on every arch, no wedge —
|
||||
`disable slot succeeded` still fires normally with the larger Device Context now installed.
|
||||
`logs/20260825-083142/amd64/`, `logs/20260825-083425/aarch64/`, `logs/20260825-083744/riscv64/`.
|
||||
Bulk Transfer Rings (`bulk_in_ring`/`bulk_out_ring`, same fixed-ring-plus-Link-TRB pattern as
|
||||
every other ring in this driver) are allocated and wired into the EP Contexts by this
|
||||
increment but not yet used for an actual transfer — CBW/CSW submission (2g's next items) is
|
||||
what exercises them for the first time.
|
||||
|
||||
**2h. Integration with the existing block subsystem**
|
||||
- [ ] Wire a working USB MSC device into `blk_subsys_attach_device()` (or
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-25T12:03:09Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-25T12:37:41Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
Binary file not shown.
@@ -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 */
|
||||
|
||||
@@ -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 +
|
||||
|
||||
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
+191
-6
@@ -129,6 +129,7 @@ int xhci_cmd_enable_slot(xhci_dev_t *dev);
|
||||
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);
|
||||
int xhci_cmd_configure_endpoint(xhci_dev_t *dev, uint32_t slot_id);
|
||||
int xhci_ep0_get_device_descriptor(xhci_dev_t *dev, uint32_t slot_id);
|
||||
int xhci_ep0_get_config_descriptor(xhci_dev_t *dev, uint32_t slot_id, uint16_t length);
|
||||
int xhci_ep0_set_configuration(xhci_dev_t *dev, uint32_t slot_id, uint8_t config_value);
|
||||
@@ -299,6 +300,12 @@ int xhci_bringup(xhci_dev_t *dev)
|
||||
dev->bulk_in_max_packet = 0;
|
||||
dev->bulk_out_ep_addr = 0;
|
||||
dev->bulk_out_max_packet = 0;
|
||||
dev->bulk_in_ring = NULL;
|
||||
dev->bulk_in_ring_cycle = 1;
|
||||
dev->bulk_in_ring_enq = 0;
|
||||
dev->bulk_out_ring = NULL;
|
||||
dev->bulk_out_ring_cycle = 1;
|
||||
dev->bulk_out_ring_enq = 0;
|
||||
dev->next_action = XHCI_NEXT_ACTION_NONE;
|
||||
dev->next_action_slot_id = 0;
|
||||
dev->next_action_length = 0;
|
||||
@@ -473,6 +480,144 @@ int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialise one bulk Transfer Ring in place -- same fixed-ring-plus-
|
||||
* Link-TRB pattern as ep0_ring/the Command Ring, factored out since
|
||||
* Configure Endpoint needs to do this twice (IN and OUT). */
|
||||
static void xhci_init_bulk_ring(xhci_trb_t *ring)
|
||||
{
|
||||
for (uint32_t i = 0; i < XHCI_RING_TRB_COUNT; i++) {
|
||||
ring[i].parameter = 0;
|
||||
ring[i].status = 0;
|
||||
ring[i].control = 0;
|
||||
}
|
||||
ring[XHCI_RING_TRB_COUNT - 1].parameter = (uint64_t)(uintptr_t)ring;
|
||||
ring[XHCI_RING_TRB_COUNT - 1].control =
|
||||
(XHCI_TRB_TYPE_LINK << XHCI_TRB_CONTROL_TYPE_SHIFT) |
|
||||
XHCI_TRB_CONTROL_TC | XHCI_TRB_CONTROL_CYCLE;
|
||||
}
|
||||
|
||||
int xhci_cmd_configure_endpoint(xhci_dev_t *dev, uint32_t slot_id)
|
||||
{
|
||||
if (!dev || !dev->cmd_ring || !dev->input_ctx || !dev->device_ctx) return -1;
|
||||
if (dev->bulk_in_ep_addr == 0 || dev->bulk_out_ep_addr == 0) return -1;
|
||||
|
||||
if (XHCI_HCCPARAMS1_CSZ(dev->cap->hcc_params1)) {
|
||||
console_println("xhci: 64-byte contexts required, not implemented -- refusing");
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (!dev->bulk_in_ring) {
|
||||
dev->bulk_in_ring = (xhci_trb_t *)kmalloc_aligned(XHCI_RING_BYTES, 64);
|
||||
if (!dev->bulk_in_ring) return -1;
|
||||
}
|
||||
if (!dev->bulk_out_ring) {
|
||||
dev->bulk_out_ring = (xhci_trb_t *)kmalloc_aligned(XHCI_RING_BYTES, 64);
|
||||
if (!dev->bulk_out_ring) return -1;
|
||||
}
|
||||
xhci_init_bulk_ring(dev->bulk_in_ring);
|
||||
dev->bulk_in_ring_cycle = 1;
|
||||
dev->bulk_in_ring_enq = 0;
|
||||
xhci_init_bulk_ring(dev->bulk_out_ring);
|
||||
dev->bulk_out_ring_cycle = 1;
|
||||
dev->bulk_out_ring_enq = 0;
|
||||
|
||||
uint32_t in_dci = XHCI_EP_ADDR_TO_DCI(dev->bulk_in_ep_addr);
|
||||
uint32_t out_dci = XHCI_EP_ADDR_TO_DCI(dev->bulk_out_ep_addr);
|
||||
uint32_t max_dci = (in_dci > out_dci) ? in_dci : out_dci;
|
||||
|
||||
/* DCBAA[slot_id]'s Device Context also needs to grow to hold the new
|
||||
* EP Contexts -- it's currently sized for Slot+EP0 only (from Address
|
||||
* Device). Unlike the Input Context below, this one's *existing*
|
||||
* content must be preserved, not zeroed: xHCI 1.2 spec section 4.6.6
|
||||
* only has the controller write the DCIs actually named in this
|
||||
* command's Add/Drop flags (EP0's entry here is neither), so it
|
||||
* expects to find EP0's live output state (its current TR Dequeue
|
||||
* Pointer in particular) still intact in the Device Context it reads
|
||||
* -- swapping in a freshly zeroed buffer would hand the controller a
|
||||
* blank EP0 out from under an endpoint it isn't being asked to touch. */
|
||||
size_t old_device_ctx_bytes = sizeof(xhci_slot_ctx32_t) + sizeof(xhci_ep_ctx32_t);
|
||||
size_t new_device_ctx_bytes = (1 + (size_t)max_dci) * sizeof(xhci_ep_ctx32_t);
|
||||
void *new_device_ctx = kmalloc_aligned(new_device_ctx_bytes, 64);
|
||||
if (!new_device_ctx) return -1;
|
||||
uint8_t *ndctx = (uint8_t *)new_device_ctx;
|
||||
for (size_t i = 0; i < new_device_ctx_bytes; i++) ndctx[i] = 0;
|
||||
const uint8_t *odctx = (const uint8_t *)dev->device_ctx;
|
||||
for (size_t i = 0; i < old_device_ctx_bytes; i++) ndctx[i] = odctx[i];
|
||||
dev->device_ctx = new_device_ctx;
|
||||
((uint64_t *)dev->dcbaa)[slot_id] = (uint64_t)(uintptr_t)dev->device_ctx;
|
||||
|
||||
/* dev->input_ctx is reused from Address Device -- same allocation
|
||||
* (96 bytes: Input Control Ctx + Slot Ctx + one EP Ctx) is too small
|
||||
* to also hold two more EP Contexts. Configure Endpoint's Input
|
||||
* Context must span every DCI up to max_dci (xHCI 1.2 spec section
|
||||
* 6.2.5.1: "the Input Context data structure shall contain output
|
||||
* context data structures... up to the value of the Context Entries
|
||||
* field"), not just the ones actually being added -- unused slots
|
||||
* between EP0 (DCI 1) and the bulk endpoints are left zeroed
|
||||
* (Add/Drop flags for those DCIs are 0, so the controller ignores
|
||||
* their content). Reallocated here rather than growing the existing
|
||||
* 96-byte block in place. */
|
||||
size_t ctx_count = 1 /* Slot */ + max_dci; /* DCI 1..max_dci, one xhci_ep_ctx32_t each */
|
||||
size_t total = sizeof(xhci_input_ctrl_ctx32_t) + ctx_count * sizeof(xhci_ep_ctx32_t);
|
||||
void *new_input_ctx = kmalloc_aligned(total, 64);
|
||||
if (!new_input_ctx) return -1;
|
||||
dev->input_ctx = new_input_ctx;
|
||||
|
||||
uint8_t *ictx = (uint8_t *)dev->input_ctx;
|
||||
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 | (1u << in_dci) | (1u << out_dci);
|
||||
|
||||
/* Slot Context: copied from the already-addressed device's own
|
||||
* Device Context (Route String/Speed/Root Hub Port/Interrupter
|
||||
* Target aren't retained anywhere else by this point in enumeration
|
||||
* -- see this function's own doc comment), Context Entries updated
|
||||
* to the highest DCI now in use. dword3 (Device Address, Slot State)
|
||||
* is an Output-only field and stays zero, matching Address Device's
|
||||
* own Input Context handling. */
|
||||
xhci_slot_ctx32_t *dev_slot = (xhci_slot_ctx32_t *)dev->device_ctx;
|
||||
xhci_slot_ctx32_t *in_slot = (xhci_slot_ctx32_t *)(ictx + sizeof(xhci_input_ctrl_ctx32_t));
|
||||
uint32_t entries_mask = ~((uint32_t)0x1Fu << XHCI_SLOT_CTX_CONTEXT_ENTRIES_SHIFT);
|
||||
in_slot->dword0 = (dev_slot->dword0 & entries_mask) |
|
||||
(max_dci << XHCI_SLOT_CTX_CONTEXT_ENTRIES_SHIFT);
|
||||
in_slot->dword1 = dev_slot->dword1;
|
||||
in_slot->dword2 = dev_slot->dword2;
|
||||
|
||||
/* EP Contexts for the two bulk endpoints, at their own DCI slot
|
||||
* within the Input Context (index = DCI - 1, since the Slot Context
|
||||
* occupies index -1 relative to DCI numbering -- DCI 1 is the first
|
||||
* EP Context). Average TRB Length is a scheduling hint, not a
|
||||
* correctness constraint (spec: "should approximate the length of
|
||||
* the transfers that will be enqueued") -- 1024 is a placeholder;
|
||||
* revisit once real CBW/data/CSW transfer sizes are known (2g's next
|
||||
* items). Max Burst Size is left 0 (single-burst) since SuperSpeed
|
||||
* Endpoint Companion descriptor parsing isn't implemented yet --
|
||||
* matches every real device this driver has been tested against
|
||||
* under QEMU's emulation, revisit if a real high-throughput SS
|
||||
* device needs it. */
|
||||
xhci_ep_ctx32_t *in_ep = (xhci_ep_ctx32_t *)(ictx + sizeof(xhci_input_ctrl_ctx32_t) +
|
||||
(size_t)in_dci * sizeof(xhci_ep_ctx32_t));
|
||||
in_ep->dword1 = (3u << XHCI_EP_CTX_CERR_SHIFT) |
|
||||
(XHCI_EP_CTX_TYPE_BULK_IN << XHCI_EP_CTX_TYPE_SHIFT) |
|
||||
((uint32_t)dev->bulk_in_max_packet << XHCI_EP_CTX_MAX_PACKET_SHIFT);
|
||||
in_ep->tr_dequeue_ptr = ((uint64_t)(uintptr_t)dev->bulk_in_ring) | 1u; /* DCS = 1 */
|
||||
in_ep->dword4 = 1024u;
|
||||
|
||||
xhci_ep_ctx32_t *out_ep = (xhci_ep_ctx32_t *)(ictx + sizeof(xhci_input_ctrl_ctx32_t) +
|
||||
(size_t)out_dci * sizeof(xhci_ep_ctx32_t));
|
||||
out_ep->dword1 = (3u << XHCI_EP_CTX_CERR_SHIFT) |
|
||||
(XHCI_EP_CTX_TYPE_BULK_OUT << XHCI_EP_CTX_TYPE_SHIFT) |
|
||||
((uint32_t)dev->bulk_out_max_packet << XHCI_EP_CTX_MAX_PACKET_SHIFT);
|
||||
out_ep->tr_dequeue_ptr = ((uint64_t)(uintptr_t)dev->bulk_out_ring) | 1u; /* DCS = 1 */
|
||||
out_ep->dword4 = 1024u;
|
||||
|
||||
xhci_submit_command(dev, (uint64_t)(uintptr_t)dev->input_ctx, 0,
|
||||
XHCI_TRB_TYPE_CONFIGURE_ENDPOINT_CMD, slot_id << 24);
|
||||
console_println("xhci: configure endpoint command submitted");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Enqueue one TRB to the EP0 Transfer Ring without ringing the doorbell
|
||||
* -- Setup/Data/Status stage TRBs are enqueued as a group, then the
|
||||
* doorbell is rung once after all three are posted, matching how a real
|
||||
@@ -805,6 +950,21 @@ void xhci_poll_events(void)
|
||||
}
|
||||
dev->connect_state = XHCI_CONN_IDLE;
|
||||
dev->pending_disable_slot_id = 0;
|
||||
} else if (dev->connect_state == XHCI_CONN_AWAIT_CONFIGURE_ENDPOINT) {
|
||||
if (code == XHCI_COMPLETION_CODE_SUCCESS) {
|
||||
console_println("xhci: configure endpoint succeeded");
|
||||
/* Chain into SET_CONFIGURATION -- next_action_
|
||||
* slot_id/next_action_config_value are still the
|
||||
* values staged when this Configure Endpoint was
|
||||
* itself deferred (see the XFER_CONFIG_DESC_FULL
|
||||
* handler above); only next_action's enum tag
|
||||
* gets cleared on consumption, not the payload
|
||||
* fields, so they're still valid to reuse here. */
|
||||
dev->next_action = XHCI_NEXT_ACTION_SET_CONFIG;
|
||||
} else {
|
||||
console_println("xhci: configure endpoint failed");
|
||||
}
|
||||
dev->connect_state = XHCI_CONN_IDLE;
|
||||
} else {
|
||||
console_println("xhci: command completion event");
|
||||
}
|
||||
@@ -924,19 +1084,36 @@ void xhci_poll_events(void)
|
||||
}
|
||||
ep_off = (uint16_t)(ep_off + ep_desc_len);
|
||||
}
|
||||
if (dev->bulk_in_ep_addr == 0 || dev->bulk_out_ep_addr == 0) {
|
||||
console_println("xhci: warning -- BOT device missing a bulk IN or OUT endpoint");
|
||||
}
|
||||
/* Deferred (see xhci_dev_t's
|
||||
* next_action doc comment) rather
|
||||
* than called directly here --
|
||||
* same doorbell-ordering hazard
|
||||
* as the device/config descriptor
|
||||
* chaining above. */
|
||||
dev->next_action = XHCI_NEXT_ACTION_SET_CONFIG;
|
||||
dev->next_action_slot_id = xfer_slot_id;
|
||||
* chaining above. next_action_
|
||||
* config_value is staged now but
|
||||
* not consumed until SET_CONFIG
|
||||
* actually runs, after Configure
|
||||
* Endpoint completes below. */
|
||||
dev->next_action_config_value =
|
||||
dev->config_descriptor[USB_CONFIG_OFF_CONFIG_VALUE];
|
||||
if (dev->bulk_in_ep_addr == 0 || dev->bulk_out_ep_addr == 0) {
|
||||
/* Can't Configure Endpoint
|
||||
* without knowing both bulk
|
||||
* endpoints -- go straight to
|
||||
* SET_CONFIGURATION so the
|
||||
* device is still usable for
|
||||
* whatever doesn't need BOT
|
||||
* (nothing, today, but this
|
||||
* keeps the two concerns
|
||||
* separate rather than
|
||||
* failing enumeration
|
||||
* outright). */
|
||||
console_println("xhci: warning -- BOT device missing a bulk IN or OUT endpoint, skipping configure endpoint");
|
||||
dev->next_action = XHCI_NEXT_ACTION_SET_CONFIG;
|
||||
} else {
|
||||
dev->next_action = XHCI_NEXT_ACTION_CONFIGURE_ENDPOINT;
|
||||
}
|
||||
dev->next_action_slot_id = xfer_slot_id;
|
||||
} else {
|
||||
console_println("xhci: not a Mass Storage/SCSI/BOT device -- not usable as a drive");
|
||||
}
|
||||
@@ -1003,6 +1180,14 @@ void xhci_poll_events(void)
|
||||
if (xhci_ep0_get_config_descriptor(dev, next_slot_id, next_length) != 0) {
|
||||
console_println("xhci: deferred config descriptor request setup failed");
|
||||
}
|
||||
} else if (dev->next_action == XHCI_NEXT_ACTION_CONFIGURE_ENDPOINT) {
|
||||
uint32_t next_slot_id = dev->next_action_slot_id;
|
||||
dev->next_action = XHCI_NEXT_ACTION_NONE;
|
||||
dev->connect_state = XHCI_CONN_AWAIT_CONFIGURE_ENDPOINT;
|
||||
if (xhci_cmd_configure_endpoint(dev, next_slot_id) != 0) {
|
||||
console_println("xhci: deferred configure endpoint request setup failed");
|
||||
dev->connect_state = XHCI_CONN_IDLE;
|
||||
}
|
||||
} else if (dev->next_action == XHCI_NEXT_ACTION_SET_CONFIG) {
|
||||
uint32_t next_slot_id = dev->next_action_slot_id;
|
||||
uint8_t next_config_value = dev->next_action_config_value;
|
||||
|
||||
Reference in New Issue
Block a user