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.
This commit is contained in:
@@ -196,6 +196,8 @@ typedef struct {
|
||||
#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_RESET_ENDPOINT_CMD 14
|
||||
#define XHCI_TRB_TYPE_SET_TR_DEQUEUE_POINTER_CMD 16
|
||||
#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
|
||||
@@ -234,11 +236,32 @@ typedef struct {
|
||||
|
||||
#define USB_REQ_GET_DESCRIPTOR 6u
|
||||
#define USB_REQ_SET_CONFIGURATION 9u
|
||||
#define USB_REQ_CLEAR_FEATURE 1u
|
||||
#define USB_DESC_TYPE_DEVICE 1u
|
||||
#define USB_DESC_TYPE_CONFIG 2u
|
||||
#define USB_DIR_DEVICE_TO_HOST 0x80u
|
||||
#define USB_DIR_HOST_TO_DEVICE 0x00u
|
||||
|
||||
/* Standard USB Device/Endpoint feature selectors (USB 2.0 spec table 9-6) --
|
||||
* ENDPOINT_HALT (0) is the halt condition on a specific endpoint, cleared
|
||||
* (and the endpoint's data toggle reset) by a CLEAR_FEATURE request whose
|
||||
* wValue is this selector and whose wIndex is the endpoint's own address --
|
||||
* the USB-level half of G.1's stall recovery (xHCI Reset Endpoint +
|
||||
* SET_TR_DEQUEUE_POINTER clear the xHC-side state; this clears the device-
|
||||
* side halt so the endpoint will actually drive new transfers again).
|
||||
* bmRequestType type field (bits 6:5 of the request type) -- 0 = standard,
|
||||
* 1 = class, and the recipient field (bits 4:0) -- 0 = device, 2 = endpoint.
|
||||
* BOT Mass Storage Reset (USB Mass Storage Class Bulk-Only Transport spec
|
||||
* section 3.1) is a class, interface-recipient (recipient 1) request, the
|
||||
* BOT-spec-mandated full teardown + restart of a stalled command sequence. */
|
||||
#define USB_REQ_TYPE_STANDARD 0u
|
||||
#define USB_REQ_TYPE_CLASS 1u
|
||||
#define USB_RECIP_DEVICE 0u
|
||||
#define USB_RECIP_INTERFACE 1u
|
||||
#define USB_RECIP_ENDPOINT 2u
|
||||
#define USB_FEATURE_ENDPOINT_HALT 0u
|
||||
#define USB_BOT_MASS_STORAGE_RESET 0xFFu
|
||||
|
||||
/* Standard USB Interface descriptor field offsets (9 bytes, USB 2.0 spec
|
||||
* table 9-12) -- Mass Storage class detection reads these three fields.
|
||||
* Not decoded via a struct like usb_setup_packet_t: the Interface
|
||||
@@ -328,6 +351,20 @@ typedef struct {
|
||||
* xhci_dev_t's bot_tur_retries doc comment. */
|
||||
#define XHCI_BOT_TUR_MAX_RETRIES 3u
|
||||
|
||||
/* Bounded recovery count for a stalled bulk endpoint before giving up on
|
||||
* it -- see xhci_dev_t's bot_stall_recoveries doc comment (G.1 / §F.14).
|
||||
* Mirrors the shape of XHCI_BOT_TUR_MAX_RETRIES: a small fixed budget of
|
||||
* full recoveries, each of which is itself the multi-step xHCI Reset
|
||||
* Endpoint -> Set TR Dequeue Pointer -> CLEAR_FEATURE(ENDPOINT_HALT)
|
||||
* sequence (escalating to a BOT Mass Storage Reset on the last try),
|
||||
* after which the original SCSI command is retried from scratch. Two
|
||||
* full recoveries, then escalation and terminal failure, is a deliberately
|
||||
* tight bound chosen to match this driver's "recover or fail clean, never
|
||||
* wedge the controller, never loop forever" contract -- a genuinely
|
||||
* wedged device gets two chances to clear, then the block layer sees a
|
||||
* clean BOT_STATUS_FAILED. */
|
||||
#define XHCI_BOT_STALL_MAX_RECOVERIES 2u
|
||||
|
||||
/* SCSI READ CAPACITY(10) (SBC-3 section 5.14) -- 10-byte CDB, opcode 0x25,
|
||||
* every other CDB byte reserved/zero for the standard "report capacity"
|
||||
* form (LBA field left 0, PMI bit left clear). 8-byte Data-In reply:
|
||||
@@ -368,6 +405,7 @@ typedef struct {
|
||||
#define XHCI_EVT_COMPLETION_CODE(status) (((uint32_t)(status) >> 24) & 0xFFu)
|
||||
#define XHCI_EVT_SLOT_ID(control) (((uint32_t)(control) >> 24) & 0xFFu)
|
||||
#define XHCI_COMPLETION_CODE_SUCCESS 1u
|
||||
#define XHCI_COMPLETION_CODE_STALL_ERROR 6u
|
||||
|
||||
/* Port Status Change Event TRB layout (xHCI 1.2 spec table 6-34):
|
||||
* parameter[31:24] = Port ID (1-based, matches PORTSC array indexing
|
||||
|
||||
@@ -72,7 +72,9 @@ typedef struct {
|
||||
XHCI_CONN_AWAIT_ENABLE_SLOT,
|
||||
XHCI_CONN_AWAIT_ADDRESS_DEVICE,
|
||||
XHCI_CONN_AWAIT_DISABLE_SLOT,
|
||||
XHCI_CONN_AWAIT_CONFIGURE_ENDPOINT
|
||||
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-
|
||||
@@ -117,7 +119,9 @@ typedef struct {
|
||||
XHCI_XFER_CBW_SENT,
|
||||
XHCI_XFER_BOT_DATA_IN,
|
||||
XHCI_XFER_BOT_DATA_OUT,
|
||||
XHCI_XFER_CSW_RECEIVED
|
||||
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];
|
||||
@@ -241,6 +245,34 @@ typedef struct {
|
||||
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
|
||||
@@ -296,7 +328,9 @@ typedef struct {
|
||||
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_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;
|
||||
@@ -468,6 +502,52 @@ int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
|
||||
*/
|
||||
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
|
||||
@@ -816,4 +896,54 @@ int xhci_ep0_get_config_descriptor(xhci_dev_t *dev, uint32_t slot_id, uint16_t l
|
||||
*/
|
||||
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 */
|
||||
|
||||
Reference in New Issue
Block a user