Files
Robert Allan James 49a3faa331 G.1: xHCI bulk-endpoint stall recovery (per F.14), built + verified
Full BOT-spec stall recovery per FABRIC-3.md F.14: new STALL_ERROR handling,
Reset Endpoint + Set TR Dequeue Pointer commands, CLEAR_FEATURE(ENDPOINT_HALT),
escalating to Bulk-Only Mass Storage Reset, capped retries
(XHCI_BOT_STALL_MAX_RECOVERIES=2) mirroring bot_tur_retries, clean terminal
failure via xhci_stall_fail().

Purely additive recovery path off the non-success transfer-event branch; the
normal path is unchanged. Builds clean on amd64/aarch64/riscv64. QEMU amd64
boot regression passes: zero stalls, BOT attach (READ CAPACITY10 -> READ10 ->
home-blocks) completes, normal-path xHCI trace identical to baseline. Live
stall injection is not provable under qemu-xhci; deferred to v2.5.0 hardware.

FABRIC-3.md G.1 documented; ROADMAP release-versioning policy folded in.
2026-08-29 00:58:59 -04:00

950 lines
50 KiB
C

/*
* xhci_driver.h — xHCI USB host controller driver public API for StarKernel
*
* Register-layout definitions live in xhci.h; this header is the driver's
* own state and public entry points, matching virtio_blk.h's split.
*/
#ifndef STARKERNEL_XHCI_DRIVER_H
#define STARKERNEL_XHCI_DRIVER_H
#include <stdint.h>
#include "starkernel/pci.h"
#include "starkernel/xhci.h"
/* Driver state for one xHCI controller instance. Only one controller is
* supported (matches virtio_blk's single-device precedent). */
typedef struct {
PciDevice pci;
uint64_t bar0_phys; /* physical MMIO base, BAR0 */
xhci_cap_regs_t *cap; /* BAR0 + 0 */
xhci_op_regs_t *op; /* BAR0 + cap->cap_length */
xhci_runtime_regs_t *runtime; /* BAR0 + cap->rts_off */
xhci_doorbell_t *doorbell; /* BAR0 + cap->db_off */
uint32_t max_slots;
uint32_t max_ports;
uint32_t max_intrs;
uint32_t max_scratchpad_bufs;
/* Set up by xhci_bringup(); NULL/0 until then. */
void *dcbaa; /* Device Context Base Address Array */
void *scratchpad_arr; /* array of scratchpad buffer pointers, if any */
xhci_trb_t *cmd_ring; /* Command Ring, XHCI_RING_TRB_COUNT TRBs;
* index XHCI_RING_TRB_COUNT-1 is a
* permanent Link TRB back to index 0 */
uint32_t cmd_ring_cycle; /* current Command Ring Cycle State (RCS) */
uint32_t cmd_ring_enq; /* next free Command Ring index (0..COUNT-2) */
xhci_trb_t *evt_ring; /* Event Ring, XHCI_RING_TRB_COUNT TRBs */
void *evt_ring_seg_table; /* Event Ring Segment Table (1 entry) */
uint32_t evt_ring_cycle; /* current Event Ring Cycle State */
uint32_t evt_ring_deq; /* current Event Ring dequeue index */
xhci_intr_regs_t *intr0; /* Interrupter 0 register set, cached
* by xhci_bringup() for
* xhci_poll_events() */
/* Milestone 2e: connect -> Enable Slot correlation. port_slot_id is
* indexed by port_id - 1 (1-based port IDs, matching PORTSC/Port
* Status Change Event numbering); 0 means no slot allocated for that
* port yet. Fixed-size, not heap-allocated -- XHCI_MAX_TRACKED_PORTS
* comfortably covers any real or emulated root hub's port count
* without adding a new kmalloc_aligned() call to xhci_bringup(); ports
* beyond this bound (checked against both this array and max_ports)
* are simply not tracked, matching this driver's existing preference
* for fixed allocations over dynamic growth (xhci.h's own ring-sizing
* rationale). Only one Enable Slot is ever in flight at a time (this
* driver issues commands synchronously with respect to connect events,
* not a queue) -- pending_connect_port_id is 0 when idle, or the
* 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,
XHCI_CONN_AWAIT_DISABLE_SLOT,
XHCI_CONN_AWAIT_CONFIGURE_ENDPOINT,
XHCI_CONN_AWAIT_RESET_ENDPOINT,
XHCI_CONN_AWAIT_SET_TR_DEQUEUE
} connect_state;
uint32_t pending_connect_slot_id;
/* Milestone 2e/2g: disconnect teardown. Same single-outstanding-
* command assumption as Enable Slot/Address Device above -- a
* disconnect that arrives while another Command Ring command is
* already outstanding is dropped rather than queued (matches the
* existing "enable slot already pending -- dropped" precedent).
* pending_disable_slot_id is captured at disconnect time, since the
* port's own tracked slot ID (port_slot_id[]) is cleared immediately
* on disconnect so a fresh connect on the same port isn't confused
* for one already in progress -- by the time the Disable Slot
* command's completion arrives, the port array no longer has it. */
uint32_t pending_disable_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;
/* Milestone 2f: EP0 control transfers. Like connect_state, this
* driver only ever has one control transfer outstanding at a time --
* pending_transfer_slot_id is 0 when idle, else the slot ID whose
* Transfer Event (posted only by the Status Stage TRB, which alone
* has IOC set) is still outstanding. transfer_purpose says which
* request that is, since xhci_poll_events() needs to know which
* buffer to interpret and what (if anything) to chain next on
* success -- e.g. a successful short Configuration descriptor read
* chains into a full-length read once wTotalLength is known.
* device_descriptor is the full 18-byte standard USB device
* descriptor; config_descriptor holds the Configuration descriptor
* and everything after it in the same read (Interface + Endpoint
* descriptors, concatenated, per USB spec) -- fixed 128 bytes,
* comfortably covers a single-interface Mass Storage device's full
* descriptor set without a dynamic allocation. All reused (not
* per-slot), matching this driver's single-device scope. */
enum {
XHCI_XFER_NONE = 0,
XHCI_XFER_DEVICE_DESC,
XHCI_XFER_CONFIG_DESC_SHORT,
XHCI_XFER_CONFIG_DESC_FULL,
XHCI_XFER_SET_CONFIG,
XHCI_XFER_CBW_SENT,
XHCI_XFER_BOT_DATA_IN,
XHCI_XFER_BOT_DATA_OUT,
XHCI_XFER_CSW_RECEIVED,
XHCI_XFER_CLEAR_HALT,
XHCI_XFER_BOT_RESET
} transfer_purpose;
uint32_t pending_transfer_slot_id;
uint8_t device_descriptor[18];
uint8_t config_descriptor[128];
uint16_t config_total_length;
/* Milestone 2g: bulk endpoints, discovered by walking the Endpoint
* descriptors that follow the confirmed Mass Storage/BOT Interface
* descriptor in config_descriptor. bEndpointAddress in full (not just
* the endpoint number) -- bit 7 is needed later to pick the right
* Doorbell target / EP Context DCI, and callers that want direction
* alone can just mask it. 0 means "not found yet" for both --
* endpoint address 0 is always EP0 (control), never a valid bulk
* endpoint address, so it's a safe not-found sentinel. */
uint8_t bulk_in_ep_addr;
uint16_t bulk_in_max_packet;
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;
/* Milestone 2g: Bulk-Only Transport. bot_cbw/bot_csw are reused across
* every command (single-outstanding-transfer scope, matching every
* other buffer in this driver) -- built/overwritten fresh each call,
* not preserved between calls. bot_next_tag is a free-running counter
* for dCBWTag; bot_last_tag latches the tag of the CBW currently in
* flight, so the CSW stage can verify dCSWTag matches (BOT spec
* requirement) without needing to re-derive it. bot_data_buf is a
* fixed 1024-byte Data-In destination -- sized to cover exactly one
* Forth block (BLKIO_FORTH_BLOCK_SIZE, block_subsystem.c's own unit)
* as two consecutive 512-byte SCSI blocks, which is what
* xhci_bot_read_block() actually requests once Milestone 2h's blkio
* backend calls this path with real block-subsystem-driven sizes; grown
* from the single-512-byte-block buffer of the increment that first
* added it. bot_expected_data_len is the byte count the Data-In stage
* was told to read, staged at CBW build time and consumed once the
* Data-In TRB is actually enqueued. */
usb_bot_cbw_t bot_cbw;
usb_bot_csw_t bot_csw;
uint32_t bot_next_tag;
uint32_t bot_last_tag;
uint8_t bot_data_buf[1024];
uint32_t bot_expected_data_len;
/* Milestone 2g follow-up: TEST UNIT READY unit-init sequence, ahead of
* a real READ(10). bot_cmd_kind says which SCSI command the CBW/CSW
* currently in flight actually is, since XHCI_XFER_CSW_RECEIVED alone
* doesn't distinguish a TUR completion from a READ10 completion --
* both go through the identical CBW->Data-In(if any)->CSW chain.
* bot_tur_retries counts TUR attempts that came back FAILED/PHASE
* ERROR (a fresh SCSI target's standard first-command UNIT ATTENTION
* behavior, not a driver defect -- see SCSI_CMD_TEST_UNIT_READY's own
* doc comment in xhci.h); capped at XHCI_BOT_TUR_MAX_RETRIES. The
* pending bot_read10_* fields latch a caller's requested READ(10) so
* it can be issued once TUR reports PASS -- xhci_bot_read_block() is
* the entry point that stages these and kicks off TUR first, rather
* than callers driving xhci_bot_send_read10() directly.
*
* Milestone 2h adds BOT_CMD_READ_CAPACITY10 (see
* xhci_bot_send_read_capacity10()) and bot_last_status: every command
* kind now resets bot_cmd_kind to BOT_CMD_NONE and sets
* bot_last_status once its own CSW is fully processed (TEST UNIT
* READY is the one exception -- a PASS or an in-progress retry both
* stay non-terminal, chaining into the next command instead). This is
* what lets xhci_bot_wait_for_idle() -- a synchronous busy-wait,
* called from OUTSIDE xhci_poll_events(), never from within it --
* detect "this command's whole chain is finished" without needing to
* know which specific command it was waiting on. */
enum {
BOT_CMD_NONE = 0,
BOT_CMD_TEST_UNIT_READY,
BOT_CMD_READ10,
BOT_CMD_READ_CAPACITY10,
BOT_CMD_WRITE10
} bot_cmd_kind;
enum {
BOT_STATUS_IDLE = 0,
BOT_STATUS_PASS,
BOT_STATUS_FAILED,
BOT_STATUS_TIMEOUT
} bot_last_status;
/* Which command a TUR-PASS should chain into -- TEST UNIT READY's own
* completion handling can't tell READ10 and READ CAPACITY10 apart
* otherwise, since both now go through the identical TUR-first
* sequencing xhci_bot_read_block()/xhci_bot_get_capacity() both use.
* Set by whichever of those two entry points kicked off the TUR. */
enum {
BOT_TUR_CHAIN_NONE = 0,
BOT_TUR_CHAIN_READ10,
BOT_TUR_CHAIN_READ_CAPACITY10,
BOT_TUR_CHAIN_WRITE10
} bot_tur_chain_target;
uint32_t bot_tur_retries;
uint32_t bot_read10_lba;
uint16_t bot_read10_num_blocks;
uint32_t bot_read10_block_size;
/* WRITE(10) mirror of bot_read10_* above -- kept as separate fields
* rather than renaming/reusing the read ones, so the already-tested
* READ10 path is never touched by this addition (FABRIC-3.md §F.1). */
uint32_t bot_write10_lba;
uint16_t bot_write10_num_blocks;
uint32_t bot_write10_block_size;
/* Latched from a successful READ CAPACITY(10) Data-In reply -- see
* SCSI_CMD_READ_CAPACITY10's own doc comment in xhci.h for field
* meaning. Untouched (stale) on a FAILED/TIMEOUT completion; callers
* must check xhci_bot_wait_for_idle()'s return value, not just read
* these blindly. */
uint32_t bot_cap_last_lba;
uint32_t bot_cap_block_size;
/* Milestone 2 / G.1 / §F.14: bulk-endpoint stall recovery. A bulk
* transfer that completes with XHCI_COMPLETION_CODE_STALL_ERROR leaves
* the xHC endpoint in the Halted state and the device endpoint in its
* own halt; neither can drive new transfers until explicitly cleared.
* This driver runs exactly one bulk transfer at a time, so a single
* recovery thread driven by bot_stall_recoveries + the stall_* fields
* below fully describes the recovery — there is no concurrency to
* serialize. The recovery itself is the BOT-spec standard sequence:
* xHCI Reset Endpoint -> Set TR Dequeue Pointer -> USB
* CLEAR_FEATURE(ENDPOINT_HALT), escalating to a Bulk-Only Mass Storage
* Reset + CLEAR_FEATURE on both bulk endpoints on a repeated stall,
* then the original command stage is retried from scratch. The two
* xHCI command steps are correlated via connect_state's two new
* AWAIT_ values; the CLEAR_FEATURE / BOT-reset control transfers are
* correlated via transfer_purpose's two new XHCI_XFER_* values; the
* deferred issue + final re-issue ride next_action's two new
* XHCI_NEXT_ACTION_* values — see xhci_poll_events()'s completion
* handlers for the state machine that consumes these. */
uint32_t bot_stall_recoveries; /* full recoveries performed for the
current command chain, capped at
XHCI_BOT_STALL_MAX_RECOVERIES */
uint32_t stall_dci; /* Device Context Index of the stalled bulk ep */
uint8_t stall_ep_addr; /* bEndpointAddress (bit7=dir) of the stalled bulk ep */
uint8_t bot_reset_clear_remaining; /* CLEAR_FEATUREs still owed in a
BOT-reset escalation (2 = both eps) */
uint32_t stall_retry_action; /* XHCI_NEXT_ACTION_* stage to re-issue once
recovery completes */
/* Milestone 2h: set by the SET_CONFIGURATION completion handler
* (inside xhci_poll_events()'s own call frame, so it only sets a flag
* -- no doorbell ring, no xhci_bot_wait_for_idle() call, both unsafe
* from there) once a device is confirmed Mass Storage/SCSI/BOT and
* configured. Consumed by sk_repl_idle() strictly after its own
* xhci_poll_events() call has returned, which is the only place safe
* to actually act on it -- calls blkio_usb_open_msc() (READ CAPACITY(10)
* + xhci_bot_wait_for_idle(), both requiring that same "outside
* xhci_poll_events()" constraint) then blk_subsys_attach_device(). */
uint8_t bot_msc_attach_pending;
uint32_t bot_msc_attach_slot_id;
/* Set by sk_repl_idle() once blk_subsys_attach_device() actually
* succeeds (not by the SET_CONFIGURATION handler itself -- attach can
* still fail, e.g. a bad capacity query, in which case there is
* nothing to detach later). Read by the PORTSC disconnect handler
* below to decide whether this disconnect needs a block-subsystem
* detach at all -- a device that never successfully attached (or that
* was already detached) produces no spurious detach flag. */
uint8_t bot_msc_attached;
/* Set by the PORTSC disconnect handler (see xhci_poll_events()'s own
* disconnect handling) only when bot_msc_attached is set -- same
* flag+consume-in-sk_repl_idle() shape as bot_msc_attach_pending,
* chosen deliberately over hooking the Disable Slot completion:
* disconnect is the unambiguous signal, while Disable Slot is only
* even issued when connect_state == XHCI_CONN_IDLE (see the "command
* ring busy" skip path) and would silently miss a detach otherwise.
* No xhci_bot_wait_for_idle() call is needed for detach itself (no
* device round-trip -- it's local block_subsystem.c bookkeeping), but
* consuming it in sk_repl_idle() anyway matches the attach path's own
* shape and keeps xhci.c decoupled from block_subsystem.c. */
uint8_t bot_msc_detach_pending;
/* 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
* currently being handled -- confirmed live (amd64 QEMU) to hang the
* guest outright when tried (a doorbell rung mid-acknowledgment of
* the previous event, evidenced by checkpoint logging showing
* execution stop exactly at the doorbell MMIO write). Chained
* requests (device descriptor -> short config read -> full config
* read) instead set these fields during event processing; the actual
* doorbell ring happens once, after the main loop and the ERDP
* write, from a small dispatch at the end of xhci_poll_events(). */
enum {
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,
XHCI_NEXT_ACTION_BOT_DATA_IN,
XHCI_NEXT_ACTION_BOT_DATA_OUT,
XHCI_NEXT_ACTION_BOT_CSW_RECEIVE,
XHCI_NEXT_ACTION_BOT_SEND_TUR,
XHCI_NEXT_ACTION_BOT_SEND_READ10,
XHCI_NEXT_ACTION_BOT_SEND_READ_CAPACITY10,
XHCI_NEXT_ACTION_BOT_SEND_WRITE10,
XHCI_NEXT_ACTION_CLEAR_HALT,
XHCI_NEXT_ACTION_BOT_RESET
} next_action;
uint32_t next_action_slot_id;
uint16_t next_action_length;
uint8_t next_action_config_value; /* SET_CONFIGURATION's wValue, staged by
* the CONFIG_DESC_FULL handler once
* bConfigurationValue is known */
} xhci_dev_t;
/*
* xhci_find_and_map — locate the xHCI controller on PCI bus 0, enable it
* (I/O+MEM+bus-master), map its BAR0 MMIO region, and
* fill in the four register-region pointers in *dev.
*
* dev must point to a zero-initialised xhci_dev_t.
*
* Returns 0 on success.
* Returns -1 if no xHCI device was found on the PCI bus.
* Returns -2 if the BAR0 mapping failed.
*/
int xhci_find_and_map(xhci_dev_t *dev);
/*
* xhci_bringup — reset the controller, allocate and program the DCBAA,
* Command Ring, and Event Ring (Interrupter 0), then start
* the controller (RUN/STOP=1) and confirm it left the
* halted state.
*
* Must be called after a successful xhci_find_and_map(). Does not enable
* interrupts (USBCMD.INTE / IMAN.IE) -- this driver is polled, not
* interrupt-driven (see xhci_poll_events()'s own doc comment for why).
*
* Returns 0 on success.
* Returns -1 on reset timeout.
* Returns -2 on allocation failure.
* Returns -3 if the controller failed to leave the halted state after RUN.
* On success, latches dev into the module-static pointer xhci_poll_events()
* reads -- only one controller is supported, matching virtio_blk's
* single-device precedent.
*/
int xhci_bringup(xhci_dev_t *dev);
/*
* xhci_get_dev — return the module-static xhci_dev_t* xhci_poll_events()
* itself reads (only one controller is supported), or NULL
* if xhci_bringup() has not completed successfully yet.
*
* Milestone 2h: callers outside this driver (sk_repl_idle(), eventually
* the block-subsystem attach glue) have no other way to reach the device
* handle -- every earlier caller of this driver's API already had one in
* hand (kernel_main.c's own local xhci_dev_t), which doesn't help code
* that only runs later, on a hotplug event it wasn't the one to observe.
*/
xhci_dev_t *xhci_get_dev(void);
/*
* xhci_poll_events — read Interrupter 0's Event Ring, dispatching each TRB
* by type: Port Status Change reads PORTSC to log
* connect/disconnect and acknowledges CSC; Command
* Completion and Transfer Event are logged only (slot
* allocation and BOT transfers are later increments).
* Advances the Event Ring dequeue pointer and clears
* ERDP.EHB when done.
*
* Polled, not interrupt-driven: an initial attempt at IRQ delivery
* (Milestone 2d's first draft) found the amd64 PCI INTx routing formula
* gives a demonstrably wrong GSI (checked live via QMP query-pci: xHCI at
* PCI slot 4 reports IRQ 10, the formula predicted 16), and the
* aarch64/riscv64 slot/pin-derived source IDs were unverified at the new
* slot this controller occupies. Rather than guess further at chipset
* PIRQ routing, this matches Section U item 6's own design intent
* (Captain Bob: "interrupt-driven, coarse cadence, cheap early-exit...
* quick check blocks... done") via sk_repl_idle()'s existing coarse-cadence
* hook instead of a per-arch IRQ path -- USB insertion is a human-timescale
* event, not a hot path, so polling costs nothing meaningful here.
*
* No arguments and no return value -- only one xHCI controller is
* supported, so the caller needs no device handle. A no-op if
* xhci_bringup() has not completed successfully (dev pointer not yet
* latched).
*/
void xhci_poll_events(void);
/*
* xhci_cmd_enable_slot — submit an Enable Slot command TRB to the Command
* Ring and ring doorbell 0. Does not wait for or
* read the resulting Command Completion Event -- it
* arrives asynchronously via xhci_poll_events(),
* which correlates the returned Slot ID back to
* dev->pending_connect_port_id and records it in
* dev->port_slot_id[].
*
* Called from xhci_poll_events()'s own Port Status Change handling on a
* real connect event -- not called directly by other code.
*
* Returns 0 if the command was posted, -1 if dev/dev->cmd_ring is not set
* up (xhci_bringup() has not completed).
*/
int xhci_cmd_enable_slot(xhci_dev_t *dev);
/*
* xhci_cmd_disable_slot — submit a Disable Slot command TRB for slot_id
* and ring doorbell 0. Does not wait for or read
* the resulting Command Completion Event -- it
* arrives asynchronously via xhci_poll_events(),
* which clears DCBAA[slot_id] on success.
*
* Called from xhci_poll_events()'s own Port Status Change handling on a
* real disconnect event, for a slot that was actually addressed -- not
* called directly by other code.
*
* Returns 0 if the command was posted, -1 if dev/dev->cmd_ring is not set
* up.
*/
int xhci_cmd_disable_slot(xhci_dev_t *dev, uint32_t slot_id);
/*
* 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);
/*
* 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_cmd_reset_endpoint — submit a Reset Endpoint command TRB for
* slot_id's endpoint `ep_id` (a bEndpointAddress,
* bit 7 = direction). Transitions that endpoint
* from the Halted state back to Stopped in the
* xHC's internal context — the xHCI-level first
* step of G.1 / §F.14 stall recovery, mirroring
* xhci_cmd_disable_slot()'s shape exactly (submit,
* ring doorbell 0, don't wait). The device-side
* halt is cleared separately by
* xhci_ep0_clear_endpoint_halt() once the two
* xHCI command steps (Reset Endpoint, then Set TR
* Dequeue Pointer) have completed.
*
* Called from xhci_poll_events()'s transfer-event STALL handler -- not
* called directly by other code.
*
* Returns 0 if the command was posted, -1 if dev/dev->cmd_ring is not set
* up.
*/
int xhci_cmd_reset_endpoint(xhci_dev_t *dev, uint32_t slot_id, uint32_t ep_id);
/*
* xhci_cmd_set_tr_dequeue_pointer — submit a Set TR Dequeue Pointer command
* TRB for slot_id's endpoint ep_id,
* repositioning its Transfer Ring's
* dequeue pointer to `new_dequeue` (a
* pointer into the ring, e.g. the ring's
* current producer slot) with cycle state
* `dcs`. The xHCI-level second step of
* G.1 / §F.14 stall recovery: after Reset
* Endpoint has un-halted the ring, this
* tells the controller where to resume /
* discard from so a freshly enqueued TRB
* is consumed cleanly.
*
* Called from xhci_poll_events()'s command-completion handler once Reset
* Endpoint succeeds -- not called directly by other code.
*
* Returns 0 if the command was posted, -1 if dev/dev->cmd_ring is not set
* up.
*/
int xhci_cmd_set_tr_dequeue_pointer(xhci_dev_t *dev, uint32_t slot_id,
uint32_t ep_id, uint64_t new_dequeue,
uint32_t dcs);
/*
* xhci_bot_send_read10 — build a Command Block Wrapper for a SCSI
* READ(10) and submit it on the bulk OUT Transfer
* Ring; the Data-In stage and CSW receive follow
* automatically once this CBW's own completion
* arrives (see xhci_bot_read_data_in()/
* xhci_bot_receive_csw() below), same deferred-
* chaining pattern as device descriptor -> config
* descriptor -> Configure Endpoint -> SET_CONFIG.
*
* lba is the starting Logical Block Address, num_blocks the SCSI transfer
* length (blocks, not bytes -- READ(10)'s own field), block_size the
* device's actual bytes-per-block, used only to compute
* dCBWDataTransferLength (the data stage's total byte length CBW
* declares up front, not carried in the CDB itself). num_blocks*block_size
* must fit in dev->bot_data_buf (512 bytes, this increment's whole scope
* -- see xhci_dev_t's own doc comment) -- refuses otherwise.
*
* Does not wait for or read any of the three stages' Transfer Events --
* they arrive asynchronously via xhci_poll_events(), correlated via
* dev->transfer_purpose, same pattern as every other transfer in this
* driver. The final result (CSW signature/tag/status validated) is only
* ever logged, not returned to any caller -- there is no synchronous
* "did the read succeed" API yet; that's 2h's problem once something
* actually needs the data back.
*
* Requires bulk_out_ep_addr/bulk_out_ring and bulk_in_ep_addr/
* bulk_in_ring to already be populated (2f/2g's config descriptor walk
* and Configure Endpoint command) -- refuses if any prerequisite is
* missing.
*
* Returns 0 if the CBW was posted, -1 if a prerequisite is missing or
* the requested transfer size exceeds dev->bot_data_buf.
*
* Low-level primitive -- sets dev->bot_cmd_kind = BOT_CMD_READ10 but does
* not run TEST UNIT READY first. Most callers want xhci_bot_read_block()
* below instead; this is called directly only by xhci_poll_events()'s own
* deferred dispatch (XHCI_NEXT_ACTION_BOT_SEND_READ10, once a prior TUR
* has reported PASS) and by xhci_bot_read_block() itself when unit-ready
* confirmation isn't wanted.
*/
int xhci_bot_send_read10(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
uint16_t num_blocks, uint32_t block_size);
/*
* xhci_bot_send_write10 — build a Command Block Wrapper for a SCSI
* WRITE(10) and submit it on the bulk OUT
* Transfer Ring; the Data-Out stage and CSW
* receive follow automatically once this CBW's
* own completion arrives (see
* xhci_bot_write_data_out()/xhci_bot_receive_csw()
* below) -- direct mirror of
* xhci_bot_send_read10() above, same deferred-
* chaining pattern, opposite data direction.
*
* Unlike READ(10), the caller must have already placed the num_blocks*
* block_size bytes to be written into dev->bot_data_buf *before* calling
* this -- there is no separate "stage the payload" step, matching how
* xhci_bot_read_block()'s caller reads the result back out of
* bot_data_buf only *after* the whole chain completes. bmCBWFlags is 0
* (host -> device data stage), not USB_BOT_CBW_FLAG_DATA_IN -- the one
* CBW-level difference from xhci_bot_send_read10(). CDB layout (SBC-3
* section 5.32) is otherwise identical to READ(10)'s: opcode, then LBA
* and Transfer Length as the same big-endian fields.
*
* lba/num_blocks/block_size have the same meaning and the same
* num_blocks*block_size <= sizeof(dev->bot_data_buf) bound as
* xhci_bot_send_read10(). Requires the same bulk endpoint/ring
* prerequisites -- refuses if any are missing.
*
* Returns 0 if the CBW was posted, -1 if a prerequisite is missing or
* the requested transfer size exceeds dev->bot_data_buf.
*
* Low-level primitive -- sets dev->bot_cmd_kind = BOT_CMD_WRITE10 but
* does not run TEST UNIT READY first. Most callers want
* xhci_bot_write_block() below instead; this is called directly only by
* xhci_poll_events()'s own deferred dispatch
* (XHCI_NEXT_ACTION_BOT_SEND_WRITE10, once a prior TUR has reported
* PASS).
*/
int xhci_bot_send_write10(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
uint16_t num_blocks, uint32_t block_size);
/*
* xhci_bot_send_test_unit_ready — build a Command Block Wrapper for SCSI
* TEST UNIT READY (6-byte CDB, no data
* stage) and submit it on the bulk OUT
* Transfer Ring. Sets
* dev->bot_expected_data_len = 0 so
* xhci_poll_events()'s CBW-completion
* handler skips the Data-In stage and
* goes straight to CSW receive, per BOT
* spec section 6.3 (host expects no
* data). dev->bot_cmd_kind is set to
* BOT_CMD_TEST_UNIT_READY so the CSW
* handler knows to interpret the result
* as a unit-ready check, not a data
* command.
*
* Requires the same bulk endpoint/ring prerequisites as
* xhci_bot_send_read10() -- refuses if any are missing.
*
* Called by xhci_bot_read_block() to start its TUR-then-READ10 sequence,
* and by xhci_poll_events()'s own deferred dispatch
* (XHCI_NEXT_ACTION_BOT_SEND_TUR) to retry a failed TUR -- not intended
* to be called directly by other code.
*
* Returns 0 if the CBW was posted, -1 if a prerequisite is missing.
*/
int xhci_bot_send_test_unit_ready(xhci_dev_t *dev, uint32_t slot_id);
/*
* xhci_bot_read_block — the real entry point for reading a block from the
* attached SCSI device. Latches lba/num_blocks/
* block_size into dev->bot_read10_*, resets
* dev->bot_tur_retries to 0, and issues a TEST
* UNIT READY first rather than a bare READ(10).
*
* A freshly attached SCSI target conventionally fails its first command
* with CHECK CONDITION/UNIT ATTENTION until that condition is drained
* (see SCSI_CMD_TEST_UNIT_READY's own doc comment in xhci.h) -- this
* function's whole purpose is absorbing that via a bounded number of TUR
* retries (XHCI_BOT_TUR_MAX_RETRIES) before the actual READ(10) is ever
* sent, rather than making every caller reimplement that sequencing.
* xhci_poll_events()'s deferred dispatch chains TUR -> (retry TUR |
* READ10) -> Data-In -> CSW automatically once this call kicks it off;
* the eventual result (PASS/FAILED, or "gave up after N TUR retries") is
* only ever logged, matching xhci_bot_send_read10()'s own current scope
* -- there is still no synchronous "did the read succeed, here's the
* data" API (2h's problem, per xhci_bot_send_read10()'s doc comment).
*
* Returns 0 if TEST UNIT READY was posted, -1 if a prerequisite is
* missing or the requested transfer size exceeds dev->bot_data_buf (the
* same check xhci_bot_send_read10() performs, done up front here so a
* bad request is rejected before spending a TUR round-trip on it).
*/
int xhci_bot_read_block(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
uint16_t num_blocks, uint32_t block_size);
/*
* xhci_bot_write_block — the real entry point for writing a block to the
* attached SCSI device. Direct mirror of
* xhci_bot_read_block() above: latches
* lba/num_blocks/block_size into
* dev->bot_write10_*, resets dev->bot_tur_retries
* to 0, and issues a TEST UNIT READY first rather
* than a bare WRITE(10), for the same first-command
* UNIT ATTENTION reason.
*
* As with xhci_bot_send_write10(), the caller must have already placed
* the payload bytes into dev->bot_data_buf before calling this.
*
* Returns 0 if TEST UNIT READY was posted, -1 if a prerequisite is
* missing or the requested transfer size exceeds dev->bot_data_buf (same
* check xhci_bot_send_write10() performs, done up front here so a bad
* request is rejected before spending a TUR round-trip on it).
*/
int xhci_bot_write_block(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
uint16_t num_blocks, uint32_t block_size);
/*
* xhci_bot_send_read_capacity10 — build a Command Block Wrapper for SCSI
* READ CAPACITY(10) (SBC-3 section 5.14,
* opcode 0x25, see xhci.h) and submit it
* on the bulk OUT Transfer Ring. Sets
* dev->bot_cmd_kind = BOT_CMD_READ_CAPACITY10
* and dev->bot_expected_data_len =
* SCSI_READ_CAPACITY10_DATA_LEN (8) so
* the existing CBW-completion handler
* runs the Data-In stage (unlike TEST
* UNIT READY, this command does have a
* short reply). On a PASS CSW,
* dev->bot_cap_last_lba/bot_cap_block_size
* are parsed from the 8-byte reply and
* bot_cmd_kind resets to BOT_CMD_NONE --
* this command is always terminal, it
* never chains into anything else.
*
* Requires the same bulk endpoint/ring prerequisites as
* xhci_bot_send_read10()/xhci_bot_send_test_unit_ready() -- refuses if any
* are missing.
*
* Low-level primitive -- does not run TEST UNIT READY first. Sent to a
* freshly attached device with no TUR ahead of it, this eats the same
* first-command UNIT ATTENTION READ(10) used to before xhci_bot_read_block()
* existed (confirmed live -- see this driver's own Milestone 2h capture
* log). Most callers want xhci_bot_get_capacity() below instead; this is
* called directly only by xhci_poll_events()'s own deferred dispatch
* (XHCI_NEXT_ACTION_BOT_SEND_READ_CAPACITY10, once a prior TUR has
* reported PASS).
*
* Returns 0 if the CBW was posted, -1 if a prerequisite is missing.
*/
int xhci_bot_send_read_capacity10(xhci_dev_t *dev, uint32_t slot_id);
/*
* xhci_bot_get_capacity — the real entry point for learning a device's
* block size/capacity. Sets
* dev->bot_tur_chain_target =
* BOT_TUR_CHAIN_READ_CAPACITY10, resets
* dev->bot_tur_retries, and issues a TEST UNIT
* READY first rather than a bare READ CAPACITY(10)
* -- same reasoning as xhci_bot_read_block(),
* and the same TUR retry budget
* (XHCI_BOT_TUR_MAX_RETRIES).
*
* Returns 0 if TEST UNIT READY was posted, -1 if a prerequisite is
* missing (same checks xhci_bot_send_read_capacity10() performs, done up
* front here so a bad request is rejected before spending a TUR
* round-trip on it).
*/
int xhci_bot_get_capacity(xhci_dev_t *dev, uint32_t slot_id);
/*
* xhci_bot_wait_for_idle — busy-wait for the BOT command currently in
* flight (bot_cmd_kind != BOT_CMD_NONE) to reach
* a terminal state, by calling xhci_poll_events()
* in a loop up to max_iters times.
*
* This is Milestone 2h's synchronous bridge over an otherwise fully
* asynchronous, polled driver -- block_subsystem.c's blkio_read()/
* blkio_info() etc. are ordinary synchronous function calls with no way
* to "come back later" for a result, so something has to spin until the
* driver's own event-driven state machine finishes.
*
* MUST NOT be called from inside xhci_poll_events() itself, or from any
* function xhci_poll_events() calls (a next_action dispatch, a Transfer
* Event handler) -- xhci_poll_events() is not reentrant, and this
* function's own busy-wait loop calls it again on every iteration; doing
* so from within an already-running call would recurse into live Event
* Ring/ERDP processing (the same class of hazard this driver's
* next_action deferral mechanism exists to avoid for doorbell rings, see
* xhci_dev_t's own doc comment on next_action). Only call this from a
* context that is definitely outside that call frame -- a caller in
* sk_repl_idle() invoked strictly after its own xhci_poll_events() call
* has already returned, for example.
*
* Returns BOT_STATUS_PASS/BOT_STATUS_FAILED (dev->bot_last_status, as left
* by whichever command was in flight) once bot_cmd_kind returns to
* BOT_CMD_NONE, or BOT_STATUS_TIMEOUT if max_iters is exhausted first
* (the command may still complete later -- this driver has no cancel
* operation, the caller just stops waiting). Returns BOT_STATUS_FAILED
* immediately if dev is NULL.
*/
int xhci_bot_wait_for_idle(xhci_dev_t *dev, uint32_t max_iters);
/*
* xhci_bot_read_data_in — submit a Normal TRB on the bulk IN Transfer
* Ring to read dev->bot_expected_data_len bytes
* into dev->bot_data_buf.
*
* Called from xhci_poll_events()'s deferred next_action dispatch once a
* CBW's own Command completion (XHCI_XFER_CBW_SENT) succeeds -- not
* called directly by other code.
*
* Returns 0 if the TRB was posted, -1 if bulk_in_ring isn't set up.
*/
int xhci_bot_read_data_in(xhci_dev_t *dev, uint32_t slot_id);
/*
* xhci_bot_write_data_out — submit a Normal TRB on the bulk OUT Transfer
* Ring to write dev->bot_expected_data_len
* bytes from dev->bot_data_buf. Direct mirror
* of xhci_bot_read_data_in() above, opposite
* ring/direction.
*
* Called from xhci_poll_events()'s deferred next_action dispatch once a
* WRITE(10) CBW's own Command completion (XHCI_XFER_CBW_SENT) succeeds --
* not called directly by other code.
*
* Returns 0 if the TRB was posted, -1 if bulk_out_ring isn't set up.
*/
int xhci_bot_write_data_out(xhci_dev_t *dev, uint32_t slot_id);
/*
* xhci_bot_receive_csw — submit a Normal TRB on the bulk IN Transfer Ring
* to read the 13-byte Command Status Wrapper into
* dev->bot_csw.
*
* Called from xhci_poll_events()'s deferred next_action dispatch once the
* Data-In stage's own Transfer Event (XHCI_XFER_BOT_DATA_IN) succeeds --
* not called directly by other code. The CSW's own completion
* (XHCI_XFER_CSW_RECEIVED) is where signature/tag/status validation
* against dev->bot_last_tag actually happens, in xhci_poll_events()
* itself, not here.
*
* Returns 0 if the TRB was posted, -1 if bulk_in_ring isn't set up.
*/
int xhci_bot_receive_csw(xhci_dev_t *dev, uint32_t slot_id);
/*
* xhci_ep0_get_device_descriptor — issue a standard GET_DESCRIPTOR
* (Device) control transfer (Setup +
* Data-IN + Status-OUT stages) on
* slot_id's EP0, reading the 18-byte
* result into dev->device_descriptor.
* Sets dev->transfer_purpose so
* xhci_poll_events() knows how to
* interpret the completion.
*
* Called once Address Device succeeds -- not called directly by other
* code.
*
* Returns 0 if the transfer was posted, -1 if dev/dev->ep0_ring is not
* set up.
*/
int xhci_ep0_get_device_descriptor(xhci_dev_t *dev, uint32_t slot_id);
/*
* xhci_ep0_get_config_descriptor — issue a GET_DESCRIPTOR (Configuration)
* control transfer for `length` bytes,
* reading into dev->config_descriptor
* (capped to its fixed size). Used
* twice per device: once for a short
* 9-byte read (just the Configuration
* descriptor header, to learn
* wTotalLength) and once for the full
* read once that length is known --
* xhci_poll_events() chains the second
* call automatically on the first
* read's success.
*
* Called once the device descriptor read succeeds -- not called directly
* by other code.
*
* Returns 0 if the transfer was posted, -1 if dev/dev->ep0_ring is not
* set up.
*/
int xhci_ep0_get_config_descriptor(xhci_dev_t *dev, uint32_t slot_id, uint16_t length);
/*
* xhci_ep0_set_configuration — issue a SET_CONFIGURATION control transfer
* (Setup + Status stage only, no Data stage)
* with wValue = config_value. Moves the
* device from Addressed into Configured
* state -- required before any endpoint
* other than EP0 (i.e. the bulk IN/OUT
* endpoints 2g needs) can be used.
*
* Called once the Configuration descriptor read confirms a Mass Storage/
* SCSI/BOT device, with config_value = that descriptor's own
* bConfigurationValue field -- not called directly by other code.
*
* Returns 0 if the transfer was posted, -1 if dev/dev->ep0_ring is not
* set up.
*/
int xhci_ep0_set_configuration(xhci_dev_t *dev, uint32_t slot_id, uint8_t config_value);
/*
* xhci_ep0_clear_endpoint_halt — issue a CLEAR_FEATURE(ENDPOINT_HALT)
* standard control request (Setup + Status
* only, no Data stage, via the existing
* xhci_ep0_control_write_nodata() machinery)
* with wValue = ENDPOINT_HALT and wIndex =
* ep_addr. This is the USB-level step of
* G.1 / §F.14 stall recovery that clears
* the *device's* own halt condition (and
* resets its data toggle), so the endpoint
* will actually drive new transfers after
* the xHC-side Reset Endpoint + Set TR
* Dequeue Pointer commands have run.
* Sets transfer_purpose = XHCI_XFER_CLEAR_HALT
* so xhci_poll_events() can complete the
* recovery and re-issue the stalled command.
*
* Called from xhci_poll_events()'s deferred next_action dispatch (the
* XHCI_NEXT_ACTION_CLEAR_HALT branch, which may run it more than once in a
* BOT-Reset escalation to clear both bulk endpoints) -- not called directly
* by other code.
*
* Returns 0 if the transfer was posted, -1 if dev/dev->ep0_ring is not set
* up.
*/
int xhci_ep0_clear_endpoint_halt(xhci_dev_t *dev, uint32_t slot_id, uint8_t ep_addr);
/*
* xhci_ep0_bot_mass_storage_reset — issue the Bulk-Only Transport class
* request Mass Storage Reset
* (bmRequestType = 0x21 class/interface,
* bRequest = 0xFF, no Data stage, again via
* the existing xhci_ep0_control_write_nodata()
* machinery). The escalation step of G.1 /
* §F.14 stall recovery: BOT spec section
* 5.3.4's full reset of a wedged command
* sequence, followed by
* xhci_ep0_clear_endpoint_halt() on *both*
* bulk endpoints before the original
* command is retried from scratch. Sets
* transfer_purpose = XHCI_XFER_BOT_RESET.
*
* Called from xhci_poll_events()'s deferred next_action dispatch (the
* XHCI_NEXT_ACTION_BOT_RESET branch) -- not called directly by other code.
*
* Returns 0 if the transfer was posted, -1 if dev/dev->ep0_ring is not set
* up.
*/
int xhci_ep0_bot_mass_storage_reset(xhci_dev_t *dev, uint32_t slot_id);
#endif /* STARKERNEL_XHCI_DRIVER_H */