Artemis Milestone 2g: CBW construction and send for SCSI READ(10)
First real use of the bulk Transfer Rings Configure Endpoint wired up. xhci_bot_send_read10() builds a 31-byte Command Block Wrapper (USB Mass Storage Class Bulk-Only Transport spec section 5.1) and submits it as a single Normal TRB on the bulk OUT ring via a new xhci_bulk_out_enqueue_and_ring() helper -- a CBW is always exactly one TRB, so unlike the EP0 helper this one rings its own doorbell rather than leaving that to a caller assembling a group. usb_bot_cbw_t is a real struct (every field up to the CDB array is naturally aligned, and this driver's targets are all little-endian already assumed everywhere else), but its DMA length is the explicit USB_BOT_CBW_LENGTH (31) constant, never sizeof(*cbw), since the compiler may pad the struct to 32 bytes. The SCSI READ(10) CDB itself is written byte-by-byte since its LBA/Transfer Length fields are big-endian on the wire, unlike everything else in this driver -- the one place two byte orders are both live in the same function. Completion is correlated via the existing pending_transfer_slot_id/ transfer_purpose gate (new XHCI_XFER_CBW_SENT purpose) -- no ring-specific dispatch needed, since this driver's single-outstanding- transfer scope already implies which ring produced an event. This covers construction and send only (one third of a full READ(10): CBW -> Data-In stage -> CSW) -- reading the Data-In stage and CSW receive/validation are separate, explicitly not-yet-implemented items. Verified live via a temporary probe (written, run once, log captured, reverted per this project's own probe convention) -- all three architectures, byte-identical: CBW submitted -> CBW send completed, then a clean disconnect even with the Data-In stage never drained (confirms no wedge on a dangling BOT transaction). Probe-free re-verification afterward on all three architectures. FABRIC-2.md Section X Milestone 2g's CBW checklist item marked done. Also records a monitoring gotcha hit three times this session: `ls -t` over the logs/ tree can return a stale leftover log from an earlier run in the same session -- fixed going forward by reading the log path off the actual running QEMU process's own command line instead, and a memory note added so it doesn't recur next session. 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
92ce1f85dd
commit
a88c004ecb
@@ -190,6 +190,7 @@ typedef struct {
|
||||
#define XHCI_TRB_TYPE(ctrl) (((ctrl) & XHCI_TRB_CONTROL_TYPE_MASK) >> XHCI_TRB_CONTROL_TYPE_SHIFT)
|
||||
|
||||
/* TRB types used by this driver (subset — xHCI defines many more) */
|
||||
#define XHCI_TRB_TYPE_NORMAL 1 /* Transfer Ring, bulk/interrupt/isoch -- not EP0 */
|
||||
#define XHCI_TRB_TYPE_LINK 6 /* ring-wraparound marker, Command/Transfer Rings only */
|
||||
#define XHCI_TRB_TYPE_ENABLE_SLOT_CMD 9
|
||||
#define XHCI_TRB_TYPE_DISABLE_SLOT_CMD 10
|
||||
@@ -275,6 +276,36 @@ typedef struct {
|
||||
#define USB_EP_ATTR_TYPE_MASK 0x03u
|
||||
#define USB_EP_TYPE_BULK 0x02u
|
||||
|
||||
/* Bulk-Only Transport Command Block Wrapper (USB Mass Storage Class Bulk-
|
||||
* Only Transport spec, section 5.1) -- sent host-to-device on the bulk OUT
|
||||
* endpoint ahead of every SCSI command's data phase. Fixed 31-byte wire
|
||||
* layout; every multi-byte field is little-endian, which this driver's
|
||||
* targets (amd64/aarch64/riscv64, all little-endian) already assume
|
||||
* throughout (no htole32-style conversions anywhere in this codebase) --
|
||||
* direct field assignment is wire-correct as-is. sizeof() is NOT used as
|
||||
* this struct's DMA length anywhere (may be padded to 32 by the compiler
|
||||
* to satisfy the uint32_t members' alignment) -- USB_BOT_CBW_LENGTH (31)
|
||||
* is the correct, explicit wire length, matching the same
|
||||
* offset-constant-not-sizeof discipline already used for the USB
|
||||
* descriptor field offsets above. */
|
||||
typedef struct {
|
||||
uint32_t dCBWSignature;
|
||||
uint32_t dCBWTag;
|
||||
uint32_t dCBWDataTransferLength;
|
||||
uint8_t bmCBWFlags;
|
||||
uint8_t bCBWLUN;
|
||||
uint8_t bCBWCBLength;
|
||||
uint8_t CBWCB[16];
|
||||
} usb_bot_cbw_t;
|
||||
|
||||
#define USB_BOT_CBW_SIGNATURE 0x43425355u /* "USBC", wire byte order U,S,B,C as an LE dword */
|
||||
#define USB_BOT_CBW_LENGTH 31u
|
||||
#define USB_BOT_CBW_FLAG_DATA_IN 0x80u /* bmCBWFlags: device-to-host data stage */
|
||||
#define USB_BOT_CBW_LUN_DEFAULT 0u /* no multi-LUN support -- single-LUN devices only */
|
||||
|
||||
#define SCSI_CMD_READ10 0x28u
|
||||
#define SCSI_CDB_LEN_READ10 10u
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -113,7 +113,8 @@ typedef struct {
|
||||
XHCI_XFER_DEVICE_DESC,
|
||||
XHCI_XFER_CONFIG_DESC_SHORT,
|
||||
XHCI_XFER_CONFIG_DESC_FULL,
|
||||
XHCI_XFER_SET_CONFIG
|
||||
XHCI_XFER_SET_CONFIG,
|
||||
XHCI_XFER_CBW_SENT
|
||||
} transfer_purpose;
|
||||
uint32_t pending_transfer_slot_id;
|
||||
uint8_t device_descriptor[18];
|
||||
@@ -148,6 +149,18 @@ 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. */
|
||||
usb_bot_cbw_t bot_cbw;
|
||||
uint32_t bot_next_tag;
|
||||
|
||||
/* 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
|
||||
@@ -323,6 +336,34 @@ 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_bot_send_read10 — build a Command Block Wrapper for a SCSI
|
||||
* READ(10) and submit it on the bulk OUT Transfer
|
||||
* Ring.
|
||||
*
|
||||
* 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).
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Returns 0 if the CBW was posted, -1 if a prerequisite is missing.
|
||||
*/
|
||||
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_ep0_get_device_descriptor — issue a standard GET_DESCRIPTOR
|
||||
* (Device) control transfer (Setup +
|
||||
|
||||
Reference in New Issue
Block a user