Implement SCSI WRITE(10), closing the graph's highest-leverage blocker

Direct mirror of the existing READ(10) implementation (FABRIC-3.md §F.1),
data direction flipped: new XHCI_XFER_BOT_DATA_OUT/XHCI_NEXT_ACTION_BOT_DATA_OUT
states, xhci_bot_send_write10()/xhci_bot_write_block()/xhci_bot_write_data_out()
in xhci.c, new SCSI_CMD_WRITE10 opcode and BOT_CMD_WRITE10/BOT_TUR_CHAIN_WRITE10
enum values. usb_blk_write() in blkio_usb.c is real now, no longer the
BLKIO_ENOSUP stub. read_only flips to 0 in blkio_info() now that it's proven.

Verified live end-to-end on all three architectures with a genuine cold-reboot
round-trip (not just a same-session read): BLK-CONFIRM-FORMAT's BAM/reloc
writes and an explicit block content write both completed via clean WRITE10
cycles (CSW PASS), and the written byte read back correctly after a full
kernel rebuild + fresh boot -- amd64=65, aarch64=170, riscv64=201, each at
LBN 32734 on a disposable usbwrite-test.img attached via QEMU usb-storage.

Added Makefile.starkernel's QEMU_EXTRA (empty by default, no behavior change)
to attach the disposable test image for this validation; the drive must be
hotplugged via QMP after boot reaches ok>, not attached at QEMU launch --
attaching before xhci_bringup()'s controller reset means no fresh Port
Status Change event fires (see project_xhci_milestone_2d_polling memory).

Found and reported, not fixed, during testing: EMPTY-BUFFERS
(empty_all_buffers(), block_words.c) does not implement standard Forth-79
semantics -- it force-writes zero to every block on every attached device
instead of discarding cache assignments. This corrupted disk/artemis.img
during an earlier test run; restored from git, confirmed byte-identical.
Avoided in the final validation runs (detach/reattach used instead to force
a fresh read).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
This commit is contained in:
Robert Allan James
2026-08-28 07:19:31 -04:00
co-authored by Claude Sonnet 5
parent ff67bbdec3
commit 5e9802845a
15 changed files with 89095 additions and 21 deletions
+11
View File
@@ -731,6 +731,14 @@ OVMF_VARS_RO := /usr/share/OVMF/OVMF_VARS_4M.fd
# Usage: make -f Makefile.starkernel qemu QEMU_DISPLAY=none # headless/CI
QEMU_DISPLAY ?= gtk
# QEMU_EXTRA — extra raw qemu-system-* args appended verbatim to every arch's
# qemu invocation below. Empty by default (no behavior change for a normal
# boot). Existing use: attaching a disposable USB test image for WRITE(10)
# validation, e.g.
# QEMU_EXTRA="-drive if=none,id=usbtest,file=disk/usbwrite-test.img,format=raw \
# -device usb-storage,bus=xhci0.0,drive=usbtest"
QEMU_EXTRA ?=
qemu: all
ifeq ($(ARCH),amd64)
@echo "=== Building ISO (amd64) ==="
@@ -782,6 +790,7 @@ ifeq ($(ARCH),amd64)
-chardev socket,id=cserial,path=$$SERIAL_SOCK,server=on,wait=off,logfile=$$LOG \
-serial chardev:cserial \
-qmp unix:$$QMP_SOCK,server=on,wait=off \
$(QEMU_EXTRA) \
-display $(QEMU_DISPLAY) \
-no-reboot; \
kill $$TAILPID 2>/dev/null; wait $$TAILPID 2>/dev/null || true; \
@@ -865,6 +874,7 @@ else ifeq ($(ARCH),aarch64)
-chardev socket,id=cserial,path=$$SERIAL_SOCK,server=on,wait=off,logfile=$$LOG \
-serial chardev:cserial \
-qmp unix:$$QMP_SOCK,server=on,wait=off \
$(QEMU_EXTRA) \
-display $(QEMU_DISPLAY) \
-m 4096 \
-no-reboot \
@@ -953,6 +963,7 @@ else ifeq ($(ARCH),riscv64)
-chardev socket,id=cserial,path=$$SERIAL_SOCK,server=on,wait=off,logfile=$$LOG \
-serial chardev:cserial \
-qmp unix:$$QMP_SOCK,server=on,wait=off \
$(QEMU_EXTRA) \
-display $(QEMU_DISPLAY) \
-no-reboot \
-d guest_errors; \
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-08-27T02:05:00Z -->
<!-- Generated by mkcapsule --manifest 2026-08-28T11:17:15Z -->
<!-- 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.
+6 -4
View File
@@ -6,10 +6,12 @@
* driver's synchronous bridge (xhci_bot_wait_for_idle(), xhci_bot_get_capacity(),
* xhci_bot_read_block()) from Milestone 2h's foundational increment.
*
* Read-only this increment: no SCSI WRITE(10) exists in the xHCI driver yet
* (block_subsystem.c's own attach-time disk probing never writes, per
* blk_format_or_load_disk()'s "NEVER writes to disk here" discipline, so a
* read-only backend is sufficient to land this piece first).
* Read-write since 2026-08-28 (FABRIC-3.md §F.1): SCSI WRITE(10) is real
* (xhci_bot_send_write10()/xhci_bot_write_block()/xhci_bot_write_data_out(),
* xhci.c), mirroring READ(10)'s existing CBW/data-stage/CSW machinery with
* the data direction flipped. Verified live on amd64: BLK-CONFIRM-FORMAT's
* BAM/reloc zero-page writes and an explicit block content write both
* survived a cold reboot and read back correctly.
*
* Only one USB MSC device is supported (single-outstanding-transaction
* scope, matching the xHCI driver it sits on).
+8
View File
@@ -306,6 +306,14 @@ typedef struct {
#define SCSI_CMD_READ10 0x28u
#define SCSI_CDB_LEN_READ10 10u
/* SCSI WRITE(10) (SBC-3 section 5.32) -- direct mirror of READ(10): same
* 10-byte CDB layout (opcode, LBA, transfer length), opposite data
* direction (host -> device). See xhci_bot_send_write10()'s own doc
* comment for the CBW-level difference (bmCBWFlags clears the DATA_IN
* bit instead of setting it). */
#define SCSI_CMD_WRITE10 0x2Au
#define SCSI_CDB_LEN_WRITE10 10u
/* SCSI TEST UNIT READY (SPC-4 section 6.33) -- 6-byte CDB, all-zero apart
* from the opcode, no data stage. Convention (not spec-mandated, but
* standard SCSI target behavior): the first command a target sees after
+89 -3
View File
@@ -116,6 +116,7 @@ typedef struct {
XHCI_XFER_SET_CONFIG,
XHCI_XFER_CBW_SENT,
XHCI_XFER_BOT_DATA_IN,
XHCI_XFER_BOT_DATA_OUT,
XHCI_XFER_CSW_RECEIVED
} transfer_purpose;
uint32_t pending_transfer_slot_id;
@@ -202,7 +203,8 @@ typedef struct {
BOT_CMD_NONE = 0,
BOT_CMD_TEST_UNIT_READY,
BOT_CMD_READ10,
BOT_CMD_READ_CAPACITY10
BOT_CMD_READ_CAPACITY10,
BOT_CMD_WRITE10
} bot_cmd_kind;
enum {
BOT_STATUS_IDLE = 0,
@@ -218,12 +220,19 @@ typedef struct {
enum {
BOT_TUR_CHAIN_NONE = 0,
BOT_TUR_CHAIN_READ10,
BOT_TUR_CHAIN_READ_CAPACITY10
BOT_TUR_CHAIN_READ_CAPACITY10,
BOT_TUR_CHAIN_WRITE10
} bot_tur_chain_target;
uint32_t bot_tur_retries;
uint32_t bot_read10_lba;
uint16_t bot_read10_num_blocks;
uint32_t bot_read10_block_size;
/* WRITE(10) mirror of bot_read10_* above -- kept as separate fields
* rather than renaming/reusing the read ones, so the already-tested
* READ10 path is never touched by this addition (FABRIC-3.md §F.1). */
uint32_t bot_write10_lba;
uint16_t bot_write10_num_blocks;
uint32_t bot_write10_block_size;
/* Latched from a successful READ CAPACITY(10) Data-In reply -- see
* SCSI_CMD_READ_CAPACITY10's own doc comment in xhci.h for field
* meaning. Untouched (stale) on a FAILED/TIMEOUT completion; callers
@@ -282,10 +291,12 @@ typedef struct {
XHCI_NEXT_ACTION_CONFIGURE_ENDPOINT,
XHCI_NEXT_ACTION_SET_CONFIG,
XHCI_NEXT_ACTION_BOT_DATA_IN,
XHCI_NEXT_ACTION_BOT_DATA_OUT,
XHCI_NEXT_ACTION_BOT_CSW_RECEIVE,
XHCI_NEXT_ACTION_BOT_SEND_TUR,
XHCI_NEXT_ACTION_BOT_SEND_READ10,
XHCI_NEXT_ACTION_BOT_SEND_READ_CAPACITY10
XHCI_NEXT_ACTION_BOT_SEND_READ_CAPACITY10,
XHCI_NEXT_ACTION_BOT_SEND_WRITE10
} next_action;
uint32_t next_action_slot_id;
uint16_t next_action_length;
@@ -501,6 +512,45 @@ 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);
/*
* xhci_bot_send_write10 — build a Command Block Wrapper for a SCSI
* WRITE(10) and submit it on the bulk OUT
* Transfer Ring; the Data-Out stage and CSW
* receive follow automatically once this CBW's
* own completion arrives (see
* xhci_bot_write_data_out()/xhci_bot_receive_csw()
* below) -- direct mirror of
* xhci_bot_send_read10() above, same deferred-
* chaining pattern, opposite data direction.
*
* Unlike READ(10), the caller must have already placed the num_blocks*
* block_size bytes to be written into dev->bot_data_buf *before* calling
* this -- there is no separate "stage the payload" step, matching how
* xhci_bot_read_block()'s caller reads the result back out of
* bot_data_buf only *after* the whole chain completes. bmCBWFlags is 0
* (host -> device data stage), not USB_BOT_CBW_FLAG_DATA_IN -- the one
* CBW-level difference from xhci_bot_send_read10(). CDB layout (SBC-3
* section 5.32) is otherwise identical to READ(10)'s: opcode, then LBA
* and Transfer Length as the same big-endian fields.
*
* lba/num_blocks/block_size have the same meaning and the same
* num_blocks*block_size <= sizeof(dev->bot_data_buf) bound as
* xhci_bot_send_read10(). Requires the same bulk endpoint/ring
* prerequisites -- refuses if any are missing.
*
* Returns 0 if the CBW was posted, -1 if a prerequisite is missing or
* the requested transfer size exceeds dev->bot_data_buf.
*
* Low-level primitive -- sets dev->bot_cmd_kind = BOT_CMD_WRITE10 but
* does not run TEST UNIT READY first. Most callers want
* xhci_bot_write_block() below instead; this is called directly only by
* xhci_poll_events()'s own deferred dispatch
* (XHCI_NEXT_ACTION_BOT_SEND_WRITE10, once a prior TUR has reported
* PASS).
*/
int xhci_bot_send_write10(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
uint16_t num_blocks, uint32_t block_size);
/*
* xhci_bot_send_test_unit_ready — build a Command Block Wrapper for SCSI
* TEST UNIT READY (6-byte CDB, no data
@@ -557,6 +607,27 @@ int xhci_bot_send_test_unit_ready(xhci_dev_t *dev, uint32_t slot_id);
int xhci_bot_read_block(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
uint16_t num_blocks, uint32_t block_size);
/*
* xhci_bot_write_block — the real entry point for writing a block to the
* attached SCSI device. Direct mirror of
* xhci_bot_read_block() above: latches
* lba/num_blocks/block_size into
* dev->bot_write10_*, resets dev->bot_tur_retries
* to 0, and issues a TEST UNIT READY first rather
* than a bare WRITE(10), for the same first-command
* UNIT ATTENTION reason.
*
* As with xhci_bot_send_write10(), the caller must have already placed
* the payload bytes into dev->bot_data_buf before calling this.
*
* Returns 0 if TEST UNIT READY was posted, -1 if a prerequisite is
* missing or the requested transfer size exceeds dev->bot_data_buf (same
* check xhci_bot_send_write10() performs, done up front here so a bad
* request is rejected before spending a TUR round-trip on it).
*/
int xhci_bot_write_block(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
uint16_t num_blocks, uint32_t block_size);
/*
* xhci_bot_send_read_capacity10 — build a Command Block Wrapper for SCSI
* READ CAPACITY(10) (SBC-3 section 5.14,
@@ -656,6 +727,21 @@ int xhci_bot_wait_for_idle(xhci_dev_t *dev, uint32_t max_iters);
*/
int xhci_bot_read_data_in(xhci_dev_t *dev, uint32_t slot_id);
/*
* xhci_bot_write_data_out — submit a Normal TRB on the bulk OUT Transfer
* Ring to write dev->bot_expected_data_len
* bytes from dev->bot_data_buf. Direct mirror
* of xhci_bot_read_data_in() above, opposite
* ring/direction.
*
* Called from xhci_poll_events()'s deferred next_action dispatch once a
* WRITE(10) 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_out_ring isn't set up.
*/
int xhci_bot_write_data_out(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
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
File diff suppressed because it is too large Load Diff
+23 -8
View File
@@ -59,17 +59,29 @@ static int usb_blk_read(blkio_dev_t *dev, uint32_t fblock, void *dst) {
}
static int usb_blk_write(blkio_dev_t *dev, uint32_t fblock, const void *src) {
(void)dev;
(void)fblock;
(void)src;
/* No SCSI WRITE(10) in the xHCI driver yet — read-only this increment,
* see blkio_usb.h's own doc comment. */
return BLKIO_ENOSUP;
if (!dev || !src) return BLKIO_EINVAL;
BlkioUsbState *s = (BlkioUsbState *)dev->state;
if (fblock >= s->total_forth_blocks) return BLKIO_EINVAL;
memcpy(s->xdev->bot_data_buf, src, BLKIO_FORTH_BLOCK_SIZE);
uint32_t lba = fblock * s->scsi_blocks_per_fblock;
if (xhci_bot_write_block(s->xdev, s->slot_id, lba,
(uint16_t)s->scsi_blocks_per_fblock,
s->scsi_block_size) != 0) {
return BLKIO_EIO;
}
if (xhci_bot_wait_for_idle(s->xdev, 100000u) != BOT_STATUS_PASS) {
return BLKIO_EIO;
}
return BLKIO_OK;
}
static int usb_blk_flush(blkio_dev_t *dev) {
(void)dev;
return BLKIO_OK; /* nothing buffered to flush — read-only backend */
return BLKIO_OK; /* every write above is already synchronous -- see
* usb_blk_write()'s own xhci_bot_wait_for_idle()
* call -- nothing buffered here to flush. */
}
static int usb_blk_info(blkio_dev_t *dev, blkio_info_t *out) {
@@ -79,7 +91,10 @@ static int usb_blk_info(blkio_dev_t *dev, blkio_info_t *out) {
out->total_blocks = s->total_forth_blocks;
out->phys_sector_size = s->scsi_block_size;
out->phys_size_bytes = (uint64_t)s->total_forth_blocks * BLKIO_FORTH_BLOCK_SIZE;
out->read_only = 1;
out->read_only = 0; /* WRITE(10) verified live end-to-end on amd64,
* 2026-08-28 (FABRIC-3.md §F.1): BLK-CONFIRM-FORMAT's
* BAM/reloc writes and an explicit content write both
* survived a cold reboot and read back correctly. */
return BLKIO_OK;
}
+117 -5
View File
@@ -132,10 +132,13 @@ 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_send_write10(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
uint16_t num_blocks, uint32_t block_size);
int xhci_bot_send_test_unit_ready(xhci_dev_t *dev, uint32_t slot_id);
int xhci_bot_send_read_capacity10(xhci_dev_t *dev, uint32_t slot_id);
int xhci_bot_get_capacity(xhci_dev_t *dev, uint32_t slot_id);
int xhci_bot_read_data_in(xhci_dev_t *dev, uint32_t slot_id);
int xhci_bot_write_data_out(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);
@@ -737,6 +740,54 @@ int xhci_bot_send_read10(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
return 0;
}
int xhci_bot_send_write10(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 || !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;
dev->bot_cmd_kind = BOT_CMD_WRITE10;
usb_bot_cbw_t *cbw = &dev->bot_cbw;
cbw->dCBWSignature = USB_BOT_CBW_SIGNATURE;
cbw->dCBWTag = dev->bot_next_tag++;
dev->bot_last_tag = cbw->dCBWTag;
dev->bot_expected_data_len = data_len;
cbw->dCBWDataTransferLength = data_len;
cbw->bmCBWFlags = 0; /* WRITE(10): host -> device data stage, unlike
* READ10's USB_BOT_CBW_FLAG_DATA_IN above */
cbw->bCBWLUN = USB_BOT_CBW_LUN_DEFAULT;
cbw->bCBWCBLength = SCSI_CDB_LEN_WRITE10;
for (uint32_t i = 0; i < sizeof(cbw->CBWCB); i++) cbw->CBWCB[i] = 0;
/* SCSI WRITE(10) CDB (SBC-3 section 5.32) -- identical layout to
* READ(10)'s CDB above, only the opcode differs. Same big-endian
* field packing, see xhci_bot_send_read10()'s own comment for why. */
cbw->CBWCB[0] = SCSI_CMD_WRITE10;
cbw->CBWCB[1] = 0; /* flags: no FUA/DPO for this increment */
cbw->CBWCB[2] = (uint8_t)(lba >> 24);
cbw->CBWCB[3] = (uint8_t)(lba >> 16);
cbw->CBWCB[4] = (uint8_t)(lba >> 8);
cbw->CBWCB[5] = (uint8_t)(lba);
cbw->CBWCB[6] = 0; /* group number */
cbw->CBWCB[7] = (uint8_t)(num_blocks >> 8);
cbw->CBWCB[8] = (uint8_t)(num_blocks);
cbw->CBWCB[9] = 0; /* control */
dev->transfer_purpose = XHCI_XFER_CBW_SENT;
dev->pending_transfer_slot_id = slot_id;
xhci_bulk_out_enqueue_and_ring(dev, slot_id, (uint64_t)(uintptr_t)cbw,
USB_BOT_CBW_LENGTH,
(XHCI_TRB_TYPE_NORMAL << XHCI_TRB_CONTROL_TYPE_SHIFT) |
XHCI_TRB_CONTROL_IOC);
console_println("xhci: CBW (WRITE10) submitted");
return 0;
}
int xhci_bot_send_test_unit_ready(xhci_dev_t *dev, uint32_t slot_id)
{
if (!dev || !dev->bulk_out_ring || !dev->bulk_in_ring) return -1;
@@ -817,6 +868,22 @@ int xhci_bot_read_block(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
return xhci_bot_send_test_unit_ready(dev, slot_id);
}
int xhci_bot_write_block(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 || !dev->bulk_in_ring) return -1;
if (dev->bulk_out_ep_addr == 0 || dev->bulk_in_ep_addr == 0) return -1;
if ((uint32_t)num_blocks * block_size > sizeof(dev->bot_data_buf)) return -1;
dev->bot_write10_lba = lba;
dev->bot_write10_num_blocks = num_blocks;
dev->bot_write10_block_size = block_size;
dev->bot_tur_chain_target = BOT_TUR_CHAIN_WRITE10;
dev->bot_tur_retries = 0;
return xhci_bot_send_test_unit_ready(dev, slot_id);
}
int xhci_bot_get_capacity(xhci_dev_t *dev, uint32_t slot_id)
{
if (!dev || !dev->bulk_out_ring || !dev->bulk_in_ring) return -1;
@@ -879,6 +946,21 @@ int xhci_bot_read_data_in(xhci_dev_t *dev, uint32_t slot_id)
return 0;
}
int xhci_bot_write_data_out(xhci_dev_t *dev, uint32_t slot_id)
{
if (!dev || !dev->bulk_out_ring) return -1;
dev->transfer_purpose = XHCI_XFER_BOT_DATA_OUT;
dev->pending_transfer_slot_id = slot_id;
xhci_bulk_out_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-Out write submitted");
return 0;
}
int xhci_bot_receive_csw(xhci_dev_t *dev, uint32_t slot_id)
{
if (!dev || !dev->bulk_in_ring) return -1;
@@ -1432,11 +1514,17 @@ void xhci_poll_events(void)
* same doorbell-ordering hazard as every
* other chained request in this driver.
* dCBWDataTransferLength == 0 (TEST UNIT
* READY) means no Data-In stage exists --
* BOT spec section 6.3 -- so skip straight to
* CSW receive. */
* READY) means no data stage exists -- BOT
* spec section 6.3 -- so skip straight to CSW
* receive. Otherwise the direction depends on
* which command this CBW was for: WRITE(10)
* needs a Data-Out stage (bulk_out_ring),
* every other data-bearing command here
* (READ10, READ CAPACITY10) needs Data-In. */
if (dev->bot_expected_data_len == 0) {
dev->next_action = XHCI_NEXT_ACTION_BOT_CSW_RECEIVE;
} else if (dev->bot_cmd_kind == BOT_CMD_WRITE10) {
dev->next_action = XHCI_NEXT_ACTION_BOT_DATA_OUT;
} else {
dev->next_action = XHCI_NEXT_ACTION_BOT_DATA_IN;
}
@@ -1449,6 +1537,12 @@ void xhci_poll_events(void)
dev->next_action_slot_id = xfer_slot_id;
break;
}
case XHCI_XFER_BOT_DATA_OUT: {
console_println("xhci: BOT Data-Out write 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
@@ -1494,6 +1588,9 @@ void xhci_poll_events(void)
if (dev->bot_tur_chain_target == BOT_TUR_CHAIN_READ_CAPACITY10) {
console_println("xhci: unit ready -- issuing READ CAPACITY10");
dev->next_action = XHCI_NEXT_ACTION_BOT_SEND_READ_CAPACITY10;
} else if (dev->bot_tur_chain_target == BOT_TUR_CHAIN_WRITE10) {
console_println("xhci: unit ready -- issuing WRITE10");
dev->next_action = XHCI_NEXT_ACTION_BOT_SEND_WRITE10;
} else {
console_println("xhci: unit ready -- issuing READ10");
dev->next_action = XHCI_NEXT_ACTION_BOT_SEND_READ10;
@@ -1522,8 +1619,9 @@ void xhci_poll_events(void)
dev->bot_last_status = csw_pass ? BOT_STATUS_PASS : BOT_STATUS_FAILED;
dev->bot_cmd_kind = BOT_CMD_NONE;
} else {
/* BOT_CMD_READ10 (BOT_CMD_NONE shouldn't
* reach here) -- terminal either way. */
/* BOT_CMD_READ10 or BOT_CMD_WRITE10
* (BOT_CMD_NONE shouldn't reach here) --
* terminal either way. */
dev->bot_last_status = csw_pass ? BOT_STATUS_PASS : BOT_STATUS_FAILED;
dev->bot_cmd_kind = BOT_CMD_NONE;
}
@@ -1599,6 +1697,12 @@ void xhci_poll_events(void)
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_DATA_OUT) {
uint32_t next_slot_id = dev->next_action_slot_id;
dev->next_action = XHCI_NEXT_ACTION_NONE;
if (xhci_bot_write_data_out(dev, next_slot_id) != 0) {
console_println("xhci: deferred BOT Data-Out write 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;
@@ -1625,5 +1729,13 @@ void xhci_poll_events(void)
if (xhci_bot_send_read_capacity10(dev, next_slot_id) != 0) {
console_println("xhci: deferred READ CAPACITY10 setup failed");
}
} else if (dev->next_action == XHCI_NEXT_ACTION_BOT_SEND_WRITE10) {
uint32_t next_slot_id = dev->next_action_slot_id;
dev->next_action = XHCI_NEXT_ACTION_NONE;
if (xhci_bot_send_write10(dev, next_slot_id, dev->bot_write10_lba,
dev->bot_write10_num_blocks,
dev->bot_write10_block_size) != 0) {
console_println("xhci: deferred WRITE10 setup failed");
}
}
}