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
+54
-2
@@ -3455,9 +3455,16 @@ arch afterward, no wedge. `logs/20260825-073235/amd64/`, `logs/20260825-073417/a
|
||||
done 2026-08-25, configuration done 2026-08-25**, see writeups below
|
||||
- [x] Implement CBW (Command Block Wrapper) construction and send, for a SCSI READ(10) —
|
||||
**done 2026-08-25**, see writeup below
|
||||
- [ ] Implement CSW (Command Status Wrapper) receive and status check
|
||||
- [x] Implement CSW (Command Status Wrapper) receive and status check — **done 2026-08-25**,
|
||||
see writeup below. Also reads the Data-In stage, not just CSW (the checklist's own
|
||||
split between this item and "identify/configure endpoints" above didn't leave a
|
||||
separate line for it, but it's new work, not folded silently into either neighbor —
|
||||
see writeup)
|
||||
- [ ] Get one real SCSI READ(10) working end to end — first proof the whole stack works,
|
||||
before write
|
||||
before write. **Partially exercised, not satisfied**: the CBW→Data-In→CSW mechanism
|
||||
itself is confirmed working (see writeup below), but the SCSI command itself returns
|
||||
CSW status FAILED against the current test fixture -- this item stays open until a
|
||||
READ(10) actually returns PASS with real data, not just a well-formed CSW
|
||||
- [ ] Implement CBW/data/CSW for SCSI WRITE(10) — this is where the earlier "read/write,
|
||||
unquestionable" requirement actually gets satisfied
|
||||
- [ ] Implement basic error/stall recovery (CSW failure status, endpoint stall clear) — at
|
||||
@@ -3598,6 +3605,51 @@ directly on the command line) before trusting the result. `ls -t` on this log tr
|
||||
known-unreliable pattern for "find the log for the process I just launched" — the PID's own
|
||||
command line is the ground truth, not directory mtime ordering.
|
||||
|
||||
**Data-In stage read and CSW receive/validation, done 2026-08-25.** Completes the CBW→Data-In→
|
||||
CSW chain for READ(10): `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 `xhci_bulk_out_enqueue_and_ring()` from the CBW-send increment, targeting
|
||||
`bulk_in_ring`/`bulk_in_ep_addr` instead). All three stages now chain automatically, same
|
||||
deferred `next_action` pattern as every earlier control-transfer/command sequence in this
|
||||
driver: CBW send completion (`XHCI_XFER_CBW_SENT`) defers into the Data-In read, whose own
|
||||
completion (`XHCI_XFER_BOT_DATA_IN`) defers into the CSW receive, whose completion
|
||||
(`XHCI_XFER_CSW_RECEIVED`) is where signature/tag/status validation actually happens.
|
||||
|
||||
Data-In reads into a new fixed 512-byte `bot_data_buf` -- this increment's whole scope is a
|
||||
single 512-byte block (matches the block size QEMU's `usb-storage` reports), not a real
|
||||
variable-size/multi-block transfer; that's 2h's problem once the block subsystem calls this
|
||||
path with real sizes. `xhci_bot_send_read10()` now refuses (`-1`) if the requested
|
||||
`num_blocks*block_size` exceeds this buffer, rather than silently truncating or overflowing.
|
||||
|
||||
CSW validation (USB Mass Storage Class BOT spec section 5.2) checks, in order: `dCSWSignature`
|
||||
against `USB_BOT_CSW_SIGNATURE` ("USBS"), then `dCSWTag` against a new `bot_last_tag` field
|
||||
(latched from the CBW that started this transaction) -- both checked *before* trusting
|
||||
`bCSWStatus` at all, since a garbled or misaligned CSW read could otherwise be misread as a
|
||||
clean pass. `usb_bot_csw_t` follows the same discipline as `usb_bot_cbw_t`: a real struct
|
||||
(every field naturally aligned, little-endian throughout), but its DMA length is the explicit
|
||||
`USB_BOT_CSW_LENGTH` (13) constant, never `sizeof()`, since the compiler may pad the struct to
|
||||
16 bytes.
|
||||
|
||||
Verified live via a temporary probe (written, run once, log captured, then reverted per this
|
||||
project's own probe convention), all three architectures, byte-identical: `CBW (READ10)
|
||||
submitted` → `CBW send completed` → `BOT Data-In read submitted` → `BOT Data-In read completed`
|
||||
→ `CSW receive submitted` → `CSW status = FAILED`, `CSW data residue=0x00000200`. The BOT
|
||||
transport mechanism itself is confirmed correct end to end -- CBW sent, Data-In stage run, a
|
||||
well-formed CSW received with the right signature and the right tag echoed back, no wedge, and
|
||||
a clean disconnect afterward even immediately following the exchange. The SCSI command itself
|
||||
failing is a separate, expected-at-this-stage condition: no SCSI unit-initialization sequence
|
||||
(TEST UNIT READY / clearing a UNIT ATTENTION condition, standard behavior for the first command
|
||||
issued to a freshly attached SCSI target) is implemented yet, and this probe issues a bare
|
||||
READ(10) with no retry -- consistent with a UNIT ATTENTION rather than a driver defect, though
|
||||
not root-caused further this increment (that root-causing, plus whatever else it takes to get
|
||||
an actual PASS, is squarely the still-open "get one real READ(10) working end to end" item
|
||||
above). `logs/20260825-091727/amd64/`, `logs/20260825-092121/aarch64/`,
|
||||
`logs/20260825-092401/riscv64/`. The probe (`XHCI_NEXT_ACTION_BOT_TEST_READ` and its trigger in
|
||||
the SET_CONFIGURATION completion handler) was removed after capture; only
|
||||
`xhci_bot_read_data_in()`/`xhci_bot_receive_csw()` and their Transfer Event handling remain.
|
||||
Re-verified probe-free afterward, all three architectures, clean boots with no BOT activity:
|
||||
`logs/20260825-092701/amd64/`, `logs/20260825-092842/aarch64/`, `logs/20260825-093048/riscv64/`.
|
||||
|
||||
**2h. Integration with the existing block subsystem**
|
||||
- [ ] Wire a working USB MSC device into `blk_subsys_attach_device()` (or
|
||||
`blk_subsys_add_raw_device()`, whichever fits — confirm which, since USB is
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-25T13:03:55Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-25T13:30:17Z -->
|
||||
<!-- 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.
@@ -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 +
|
||||
|
||||
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
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
+115
-8
@@ -132,6 +132,8 @@ 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);
|
||||
int xhci_bot_send_read10(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
|
||||
uint16_t num_blocks, uint32_t block_size);
|
||||
int xhci_bot_read_data_in(xhci_dev_t *dev, uint32_t slot_id);
|
||||
int xhci_bot_receive_csw(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);
|
||||
@@ -309,6 +311,8 @@ int xhci_bringup(xhci_dev_t *dev)
|
||||
dev->bulk_out_ring_cycle = 1;
|
||||
dev->bulk_out_ring_enq = 0;
|
||||
dev->bot_next_tag = 1;
|
||||
dev->bot_last_tag = 0;
|
||||
dev->bot_expected_data_len = 0;
|
||||
dev->next_action = XHCI_NEXT_ACTION_NONE;
|
||||
dev->next_action_slot_id = 0;
|
||||
dev->next_action_length = 0;
|
||||
@@ -656,13 +660,18 @@ static void xhci_bulk_out_enqueue_and_ring(xhci_dev_t *dev, uint32_t slot_id,
|
||||
int xhci_bot_send_read10(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
|
||||
uint16_t num_blocks, uint32_t block_size)
|
||||
{
|
||||
if (!dev || !dev->bulk_out_ring) return -1;
|
||||
if (dev->bulk_out_ep_addr == 0) return -1;
|
||||
if (!dev || !dev->bulk_out_ring || !dev->bulk_in_ring) return -1;
|
||||
if (dev->bulk_out_ep_addr == 0 || dev->bulk_in_ep_addr == 0) return -1;
|
||||
|
||||
uint32_t data_len = (uint32_t)num_blocks * block_size;
|
||||
if (data_len > sizeof(dev->bot_data_buf)) return -1;
|
||||
|
||||
usb_bot_cbw_t *cbw = &dev->bot_cbw;
|
||||
cbw->dCBWSignature = USB_BOT_CBW_SIGNATURE;
|
||||
cbw->dCBWTag = dev->bot_next_tag++;
|
||||
cbw->dCBWDataTransferLength = (uint32_t)num_blocks * block_size;
|
||||
dev->bot_last_tag = cbw->dCBWTag;
|
||||
dev->bot_expected_data_len = data_len;
|
||||
cbw->dCBWDataTransferLength = data_len;
|
||||
cbw->bmCBWFlags = USB_BOT_CBW_FLAG_DATA_IN; /* READ(10): device -> host data stage */
|
||||
cbw->bCBWLUN = USB_BOT_CBW_LUN_DEFAULT;
|
||||
cbw->bCBWCBLength = SCSI_CDB_LEN_READ10;
|
||||
@@ -705,6 +714,64 @@ int xhci_bot_send_read10(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Enqueue one Normal TRB to the bulk IN Transfer Ring and ring its
|
||||
* doorbell -- same shape as xhci_bulk_out_enqueue_and_ring() (a BOT
|
||||
* Data-In or CSW read is, like a CBW send, always exactly one TRB), just
|
||||
* targeting bulk_in_ring/bulk_in_ep_addr instead of the OUT side. */
|
||||
static void xhci_bulk_in_enqueue_and_ring(xhci_dev_t *dev, uint32_t slot_id,
|
||||
uint64_t parameter, uint32_t status,
|
||||
uint32_t control_bits)
|
||||
{
|
||||
xhci_trb_t *trb = &dev->bulk_in_ring[dev->bulk_in_ring_enq];
|
||||
trb->parameter = parameter;
|
||||
trb->status = status;
|
||||
trb->control = control_bits | (dev->bulk_in_ring_cycle ? XHCI_TRB_CONTROL_CYCLE : 0);
|
||||
|
||||
dev->bulk_in_ring_enq++;
|
||||
if (dev->bulk_in_ring_enq == XHCI_RING_TRB_COUNT - 1) {
|
||||
dev->bulk_in_ring[XHCI_RING_TRB_COUNT - 1].control =
|
||||
(XHCI_TRB_TYPE_LINK << XHCI_TRB_CONTROL_TYPE_SHIFT) |
|
||||
XHCI_TRB_CONTROL_TC |
|
||||
(dev->bulk_in_ring_cycle ? XHCI_TRB_CONTROL_CYCLE : 0);
|
||||
dev->bulk_in_ring_enq = 0;
|
||||
dev->bulk_in_ring_cycle ^= 1u;
|
||||
}
|
||||
|
||||
dev->doorbell[slot_id] = XHCI_DB_TARGET(XHCI_EP_ADDR_TO_DCI(dev->bulk_in_ep_addr));
|
||||
}
|
||||
|
||||
int xhci_bot_read_data_in(xhci_dev_t *dev, uint32_t slot_id)
|
||||
{
|
||||
if (!dev || !dev->bulk_in_ring) return -1;
|
||||
|
||||
dev->transfer_purpose = XHCI_XFER_BOT_DATA_IN;
|
||||
dev->pending_transfer_slot_id = slot_id;
|
||||
|
||||
xhci_bulk_in_enqueue_and_ring(dev, slot_id, (uint64_t)(uintptr_t)dev->bot_data_buf,
|
||||
dev->bot_expected_data_len,
|
||||
(XHCI_TRB_TYPE_NORMAL << XHCI_TRB_CONTROL_TYPE_SHIFT) |
|
||||
XHCI_TRB_CONTROL_IOC);
|
||||
console_println("xhci: BOT Data-In read submitted");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xhci_bot_receive_csw(xhci_dev_t *dev, uint32_t slot_id)
|
||||
{
|
||||
if (!dev || !dev->bulk_in_ring) return -1;
|
||||
|
||||
dev->transfer_purpose = XHCI_XFER_CSW_RECEIVED;
|
||||
dev->pending_transfer_slot_id = slot_id;
|
||||
|
||||
/* Length is USB_BOT_CSW_LENGTH (13), not sizeof(dev->bot_csw) -- same
|
||||
* padding hazard as the CBW, see usb_bot_csw_t's own doc comment. */
|
||||
xhci_bulk_in_enqueue_and_ring(dev, slot_id, (uint64_t)(uintptr_t)&dev->bot_csw,
|
||||
USB_BOT_CSW_LENGTH,
|
||||
(XHCI_TRB_TYPE_NORMAL << XHCI_TRB_CONTROL_TYPE_SHIFT) |
|
||||
XHCI_TRB_CONTROL_IOC);
|
||||
console_println("xhci: CSW receive 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
|
||||
@@ -1219,12 +1286,40 @@ void xhci_poll_events(void)
|
||||
break;
|
||||
}
|
||||
case XHCI_XFER_CBW_SENT: {
|
||||
/* CBW send is confirmed done; the Data-In
|
||||
* stage and CSW receive/validation are 2g's
|
||||
* next items, not implemented yet -- nothing
|
||||
* further chains from here in this
|
||||
* increment. */
|
||||
console_println("xhci: CBW send completed");
|
||||
/* Deferred (see xhci_dev_t's next_action doc
|
||||
* comment) rather than called directly --
|
||||
* same doorbell-ordering hazard as every
|
||||
* other chained request in this driver. */
|
||||
dev->next_action = XHCI_NEXT_ACTION_BOT_DATA_IN;
|
||||
dev->next_action_slot_id = xfer_slot_id;
|
||||
break;
|
||||
}
|
||||
case XHCI_XFER_BOT_DATA_IN: {
|
||||
console_println("xhci: BOT Data-In read completed");
|
||||
dev->next_action = XHCI_NEXT_ACTION_BOT_CSW_RECEIVE;
|
||||
dev->next_action_slot_id = xfer_slot_id;
|
||||
break;
|
||||
}
|
||||
case XHCI_XFER_CSW_RECEIVED: {
|
||||
/* USB Mass Storage Class BOT spec section 5.2:
|
||||
* a valid CSW must have the right signature
|
||||
* and echo the CBW's own tag -- checked before
|
||||
* trusting bCSWStatus at all, since a garbled
|
||||
* or misaligned CSW read could otherwise be
|
||||
* misread as a clean pass. */
|
||||
if (dev->bot_csw.dCSWSignature != USB_BOT_CSW_SIGNATURE) {
|
||||
console_println("xhci: CSW signature mismatch -- discarding");
|
||||
} else if (dev->bot_csw.dCSWTag != dev->bot_last_tag) {
|
||||
console_println("xhci: CSW tag mismatch -- discarding");
|
||||
} else if (dev->bot_csw.bCSWStatus == USB_BOT_CSW_STATUS_PASS) {
|
||||
console_println("xhci: CSW status = PASS");
|
||||
} else if (dev->bot_csw.bCSWStatus == USB_BOT_CSW_STATUS_FAILED) {
|
||||
console_println("xhci: CSW status = FAILED");
|
||||
} else {
|
||||
console_println("xhci: CSW status = PHASE ERROR");
|
||||
}
|
||||
xhci_log_hex32("xhci: CSW data residue=", dev->bot_csw.dCSWDataResidue);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -1291,5 +1386,17 @@ void xhci_poll_events(void)
|
||||
if (xhci_ep0_set_configuration(dev, next_slot_id, next_config_value) != 0) {
|
||||
console_println("xhci: deferred set configuration request setup failed");
|
||||
}
|
||||
} else if (dev->next_action == XHCI_NEXT_ACTION_BOT_DATA_IN) {
|
||||
uint32_t next_slot_id = dev->next_action_slot_id;
|
||||
dev->next_action = XHCI_NEXT_ACTION_NONE;
|
||||
if (xhci_bot_read_data_in(dev, next_slot_id) != 0) {
|
||||
console_println("xhci: deferred BOT Data-In read setup failed");
|
||||
}
|
||||
} else if (dev->next_action == XHCI_NEXT_ACTION_BOT_CSW_RECEIVE) {
|
||||
uint32_t next_slot_id = dev->next_action_slot_id;
|
||||
dev->next_action = XHCI_NEXT_ACTION_NONE;
|
||||
if (xhci_bot_receive_csw(dev, next_slot_id) != 0) {
|
||||
console_println("xhci: deferred CSW receive setup failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user