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
+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