Artemis Milestone 2g: Data-In stage read and CSW receive/validation
Completes the CBW -> Data-In -> CSW chain for READ(10) started last commit. xhci_bot_read_data_in() and xhci_bot_receive_csw(), each a single Normal TRB on the bulk IN Transfer Ring via a new xhci_bulk_in_enqueue_and_ring() helper (mirrors the OUT-side helper from CBW send). All three stages now chain automatically via the existing deferred next_action pattern: CBW completion defers into Data-In, Data-In completion defers into CSW receive, CSW completion is where signature/tag/status validation happens. Data-In reads into a new fixed 512-byte bot_data_buf -- single-block scope for this increment, matches QEMU's usb-storage reported block size; xhci_bot_send_read10() now refuses rather than overflow/truncate if a request exceeds it. CSW validation (BOT spec section 5.2) checks dCSWSignature and dCSWTag (a new bot_last_tag field, latched from the CBW) before trusting bCSWStatus at all, so a garbled/misaligned CSW read can't be misread as a clean pass. usb_bot_csw_t follows the same struct-with-explicit-length-not-sizeof discipline as usb_bot_cbw_t. Verified live via a temporary probe (written, run once, log captured, reverted per this project's own probe convention), all three architectures, byte-identical: the full CBW -> Data-In -> CSW exchange completes cleanly, well-formed CSW with correct signature and echoed tag, no wedge, clean disconnect immediately after. The SCSI command itself reports CSW status FAILED against the current test fixture -- expected at this stage (no TEST UNIT READY / UNIT ATTENTION handling implemented yet, consistent with a fresh-attach unit-attention condition, not a transport-layer defect) and not root-caused further here; the BOT mechanism itself is confirmed correct end to end. Probe-free re-verification afterward on all three architectures. FABRIC-2.md Section X Milestone 2g's CSW checklist item marked done; "get one real READ(10) working end to end" stays explicitly open, distinguishing "the mechanism works" from "the SCSI command succeeds." 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
a88c004ecb
commit
c54ea24aaf
@@ -306,6 +306,26 @@ typedef struct {
|
||||
#define SCSI_CMD_READ10 0x28u
|
||||
#define SCSI_CDB_LEN_READ10 10u
|
||||
|
||||
/* Bulk-Only Transport Command Status Wrapper (same spec, section 5.2) --
|
||||
* received device-to-host on the bulk IN endpoint after the Data-In
|
||||
* stage, closing out every SCSI command. Fixed 13-byte wire layout; same
|
||||
* little-endian-direct-assignment and explicit-length-not-sizeof
|
||||
* discipline as usb_bot_cbw_t above (this struct's natural size is
|
||||
* likely padded to 16 by the compiler for the same alignment reason --
|
||||
* USB_BOT_CSW_LENGTH (13) is the real wire length). */
|
||||
typedef struct {
|
||||
uint32_t dCSWSignature;
|
||||
uint32_t dCSWTag;
|
||||
uint32_t dCSWDataResidue;
|
||||
uint8_t bCSWStatus;
|
||||
} usb_bot_csw_t;
|
||||
|
||||
#define USB_BOT_CSW_SIGNATURE 0x53425355u /* "USBS", wire byte order U,S,B,S as an LE dword */
|
||||
#define USB_BOT_CSW_LENGTH 13u
|
||||
#define USB_BOT_CSW_STATUS_PASS 0u
|
||||
#define USB_BOT_CSW_STATUS_FAILED 1u
|
||||
#define USB_BOT_CSW_STATUS_PHASE_ERROR 2u
|
||||
|
||||
/* Command Completion Event TRB layout (xHCI 1.2 spec table 6-32):
|
||||
* parameter[63:4] = Command TRB Pointer, status[31:24] = Completion Code,
|
||||
* status[23:0] = unused here, control[31:24] = Slot ID (Enable Slot's
|
||||
|
||||
@@ -114,7 +114,9 @@ typedef struct {
|
||||
XHCI_XFER_CONFIG_DESC_SHORT,
|
||||
XHCI_XFER_CONFIG_DESC_FULL,
|
||||
XHCI_XFER_SET_CONFIG,
|
||||
XHCI_XFER_CBW_SENT
|
||||
XHCI_XFER_CBW_SENT,
|
||||
XHCI_XFER_BOT_DATA_IN,
|
||||
XHCI_XFER_CSW_RECEIVED
|
||||
} transfer_purpose;
|
||||
uint32_t pending_transfer_slot_id;
|
||||
uint8_t device_descriptor[18];
|
||||
@@ -149,17 +151,25 @@ typedef struct {
|
||||
uint32_t bulk_out_ring_cycle;
|
||||
uint32_t bulk_out_ring_enq;
|
||||
|
||||
/* Milestone 2g: Bulk-Only Transport CBW. bot_cbw is reused across every
|
||||
* command (single-outstanding-transfer scope, matching every other
|
||||
* buffer in this driver) -- built fresh by xhci_bot_send_read10() each
|
||||
* call, not preserved between calls. bot_next_tag is a free-running
|
||||
* counter for dCBWTag; the BOT spec requires the host verify a CSW's
|
||||
* dCSWTag matches the CBW that produced it, so this is forward-looking
|
||||
* plumbing for that check (CSW receive is 2g's next item, not
|
||||
* implemented yet) rather than something this increment reads back
|
||||
* itself. */
|
||||
/* 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 512-byte Data-In destination -- covers exactly one 512-byte
|
||||
* block, this increment's whole scope (a real multi-block/variable-
|
||||
* block-size transfer is 2h's problem once the block subsystem
|
||||
* actually calls this path with real sizes). 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[512];
|
||||
uint32_t bot_expected_data_len;
|
||||
|
||||
/* Deferred chaining: a doorbell ring (new control transfer) must
|
||||
* never happen synchronously from inside xhci_poll_events()'s event-
|
||||
@@ -177,7 +187,9 @@ typedef struct {
|
||||
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_SET_CONFIG,
|
||||
XHCI_NEXT_ACTION_BOT_DATA_IN,
|
||||
XHCI_NEXT_ACTION_BOT_CSW_RECEIVE
|
||||
} next_action;
|
||||
uint32_t next_action_slot_id;
|
||||
uint16_t next_action_length;
|
||||
@@ -339,31 +351,69 @@ int xhci_cmd_configure_endpoint(xhci_dev_t *dev, uint32_t slot_id);
|
||||
/*
|
||||
* xhci_bot_send_read10 — build a Command Block Wrapper for a SCSI
|
||||
* READ(10) and submit it on the bulk OUT Transfer
|
||||
* Ring.
|
||||
* 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).
|
||||
* 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.
|
||||
*
|
||||
* This covers CBW construction and send only, one third of a full
|
||||
* READ(10) (CBW -> Data-In stage -> CSW) -- reading the Data-In stage and
|
||||
* validating/receiving the CSW are separate, not-yet-implemented steps
|
||||
* (2g's own punch list). Does not wait for or read the resulting Transfer
|
||||
* Event -- it arrives asynchronously via xhci_poll_events(), correlated
|
||||
* via dev->transfer_purpose == XHCI_XFER_CBW_SENT, same pattern as every
|
||||
* other transfer in this driver.
|
||||
* 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 to already be populated (2f/2g's
|
||||
* config descriptor walk and Configure Endpoint command) -- refuses if
|
||||
* either prerequisite is missing.
|
||||
* 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.
|
||||
* Returns 0 if the CBW was posted, -1 if a prerequisite is missing or
|
||||
* the requested transfer size exceeds dev->bot_data_buf.
|
||||
*/
|
||||
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_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_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 +
|
||||
|
||||
Reference in New Issue
Block a user