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
+64
-1
@@ -3453,7 +3453,8 @@ arch afterward, no wedge. `logs/20260825-073235/amd64/`, `logs/20260825-073417/a
|
||||
**2g. Bulk-Only Transport (BOT) — the actual read/write path**
|
||||
- [x] Identify and configure the device's bulk IN and bulk OUT endpoints — **identification
|
||||
done 2026-08-25, configuration done 2026-08-25**, see writeups below
|
||||
- [ ] Implement CBW (Command Block Wrapper) construction and send, for a SCSI READ(10)
|
||||
- [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
|
||||
- [ ] Get one real SCSI READ(10) working end to end — first proof the whole stack works,
|
||||
before write
|
||||
@@ -3535,6 +3536,68 @@ every other ring in this driver) are allocated and wired into the EP Contexts by
|
||||
increment but not yet used for an actual transfer — CBW/CSW submission (2g's next items) is
|
||||
what exercises them for the first time.
|
||||
|
||||
**CBW construction and send, done 2026-08-25.** 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) into a new `usb_bot_cbw_t`
|
||||
buffer, then submits it as a single Normal TRB (`XHCI_TRB_TYPE_NORMAL`, new in `xhci.h`) on
|
||||
the bulk OUT ring via a new `xhci_bulk_out_enqueue_and_ring()` helper — a CBW is always
|
||||
exactly one TRB (no Setup/Data/Status split, that's a control-transfer-only concept), so
|
||||
unlike the EP0 helper this one rings the doorbell itself rather than leaving that to a caller
|
||||
assembling a group. Doorbell target is the bulk OUT endpoint's own DCI (`XHCI_EP_ADDR_TO_DCI()`,
|
||||
already built for Configure Endpoint), not target 1 (EP0) — distinct rings need distinct
|
||||
doorbell targets.
|
||||
|
||||
`usb_bot_cbw_t` is a real C struct (unlike the Interface/Endpoint descriptor fields, which are
|
||||
read via explicit offset macros rather than a struct) since every field up to the trailing
|
||||
16-byte CDB array is already naturally aligned and this driver's targets are all little-endian
|
||||
already assumed everywhere else (no `htole32`-style conversions anywhere in this codebase) —
|
||||
but its DMA length is the explicit constant `USB_BOT_CBW_LENGTH` (31), never `sizeof(*cbw)`,
|
||||
since the compiler may pad the struct to 32 bytes to satisfy its `uint32_t` members' alignment.
|
||||
The SCSI READ(10) CDB itself (SBC-3 section 5.13) is written byte-by-byte instead, since its
|
||||
multi-byte fields (LBA, Transfer Length) are big-endian on the wire regardless of USB's own
|
||||
little-endian convention — the one place in this function two different byte orders are both
|
||||
live at once, flagged in a comment so a future edit doesn't "fix" one to match the other.
|
||||
|
||||
Completion is correlated the same way every other transfer in this driver is: a new
|
||||
`XHCI_XFER_CBW_SENT` `transfer_purpose`, read back via the existing `pending_transfer_slot_id`
|
||||
gate in `xhci_poll_events()`'s Transfer Event handling — no ring-specific dispatch needed,
|
||||
since this driver's single-outstanding-transfer scope already covers "which ring generated
|
||||
this event" implicitly (there is only ever one transfer in flight, control or bulk, at a
|
||||
time). `bot_next_tag` (a new `xhci_dev_t` field) is forward-looking plumbing for a future
|
||||
dCSWTag match check, not read back by anything in this increment — CSW receive/validation is
|
||||
2g's next item.
|
||||
|
||||
This covers construction and send only, one third of a full READ(10) (CBW → Data-In stage →
|
||||
CSW) — the Data-In stage and CSW receive are explicitly separate, not-yet-implemented items on
|
||||
this milestone's own punch list.
|
||||
|
||||
Verified live via a temporary probe (written, run once, log captured, then reverted per this
|
||||
project's own probe convention — see `feedback_revert_probes_after_capture` in memory): a
|
||||
`XHCI_NEXT_ACTION_BOT_TEST_READ` deferred action, triggered from the existing SET_CONFIGURATION
|
||||
completion handler, issued a 1-block READ(10) at LBA 0 immediately once a device reached
|
||||
`device configured`. All three architectures, byte-identical: `xhci: CBW (READ10) submitted` →
|
||||
`xhci: CBW send completed`, then a clean disconnect (`disable slot succeeded`) afterward even
|
||||
with the READ10's Data-In stage never drained — confirms the driver doesn't wedge on a
|
||||
dangling BOT transaction, a useful bonus check beyond this increment's own scope.
|
||||
`logs/20260825-084942/amd64/`, `logs/20260825-085140/aarch64/`, `logs/20260825-085414/riscv64/`.
|
||||
The probe itself (the `XHCI_NEXT_ACTION_BOT_TEST_READ` trigger and its enum value) was removed
|
||||
after capture, per convention — only `xhci_bot_send_read10()` and its Transfer Event handling
|
||||
remain as reusable substrate. Re-verified probe-free afterward, all three architectures, clean
|
||||
boots with no BOT activity (nothing calls the function yet with the probe gone — expected):
|
||||
`logs/20260825-090124/amd64/`, `logs/20260825-090238/aarch64/`, `logs/20260825-090426/riscv64/`.
|
||||
|
||||
**A monitoring mistake caught mid-session, same class as the one recorded in 2e's
|
||||
disconnect-teardown writeup above.** While verifying the final probe-free boots, two separate
|
||||
`ls -t logs/*/<arch>/*.log | head -1` globs each picked a *stale* leftover log from this same
|
||||
session's own earlier hotplug-verification runs (their file mtimes apparently updated during
|
||||
QEMU's own shutdown/log-flush, after the new run's log had already been created but before it
|
||||
had accumulated enough content to look "newer" by content, only by directory-creation time) —
|
||||
both times caught by cross-checking the glob's answer against the actual running QEMU
|
||||
process's real command line (`ps aux | grep qemu-system-<arch>`, which names its own log path
|
||||
directly on the command line) before trusting the result. `ls -t` on this log tree is now a
|
||||
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.
|
||||
|
||||
**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
|
||||
|
||||
Reference in New Issue
Block a user