Artemis Milestone 2h (foundational): sync wait bridge + SCSI READ CAPACITY(10)
Closes the gap block_subsystem.c needs before any of 2h's real work (blkio_usb.c, attach wiring, hot-detach) can start: this driver is fully async/polled with no way for a synchronous caller (blkio_read()/ blkio_info() etc.) to get a result back. xhci_bot_wait_for_idle() is a bounded busy-wait over xhci_poll_events() -- MUST be called only from outside xhci_poll_events()'s own call frame, never from within it or a next_action dispatch (recursion into live Event Ring/ERDP processing, same class of hazard already documented for doorbell rings in this driver). xhci_get_dev() exposes the module-static device handle to outside callers that didn't observe the original hotplug event. SCSI READ CAPACITY(10) (opcode 0x25) is the other half -- nothing could learn a device's block size/capacity before this. First attempt sent it bare and hit the classic first-command UNIT ATTENTION (CSW FAILED); fixed with the same TUR-guard pattern READ(10) already used, generalized via a new bot_tur_chain_target field so TEST UNIT READY's PASS handling can chain into either command. bot_data_buf grown 512->1024 bytes (one Forth block = two 512-byte SCSI blocks, per block_subsystem.c's own 1KiB-unit convention). Verified live via a temporary probe (hot-attached disk/usb-thumbdrive- test.img via QMP, reverted after capture): TUR-guarded READ CAPACITY10 correctly reported last LBA=0x1ffff, block size=0x200 -- exactly 64MiB, matching the test image byte for byte -- followed by a TUR-guarded 1024-byte/2-block READ10, both PASS. All three architectures re-verified clean, probe-free boot to ok> on the reverted tree. FABRIC-2.md Section X 2h updated with the writeup; the blkio_usb.c backend itself is next. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
65effbd1ba
commit
d686f28853
+70
@@ -3724,6 +3724,76 @@ function. Detaching should be safe without renumbering other devices' LBNs as lo
|
||||
the tail of the chain (true today; would need reconsidering if a second hot-pluggable device
|
||||
class is ever added), but the function itself does not exist yet.
|
||||
|
||||
**2h foundational piece — synchronous bridge + READ CAPACITY(10), done 2026-08-25.** Before any
|
||||
of the three items above can be built, something has to close the gap between this driver
|
||||
(fully async, single-outstanding-transaction, polled from `sk_repl_idle()`) and
|
||||
`block_subsystem.c` (ordinary synchronous function calls — `blkio_read()`/`blkio_info()` etc.
|
||||
have no way to "come back later" for a result). `xhci_bot_wait_for_idle(dev, max_iters)` is
|
||||
that bridge: a bounded busy-wait that calls `xhci_poll_events()` in a loop until
|
||||
`dev->bot_cmd_kind` returns to `BOT_CMD_NONE`, returning `dev->bot_last_status`
|
||||
(`BOT_STATUS_PASS`/`BOT_STATUS_FAILED`/`BOT_STATUS_TIMEOUT`). Every BOT command's CSW-received
|
||||
handling now resets `bot_cmd_kind`/sets `bot_last_status` on every terminal path — including the
|
||||
two CSW signature/tag-mismatch discard branches, which previously left `bot_cmd_kind` stuck
|
||||
forever on a garbled CSW (harmless before, since nothing read it back; would have hung this new
|
||||
wait loop for its full `max_iters` on the same condition).
|
||||
|
||||
**Load-bearing constraint, caught by advisor review before writing any code:** this wait
|
||||
function must never be called from inside `xhci_poll_events()` itself, or from anything it
|
||||
calls (a `next_action` dispatch, a Transfer Event handler) — the busy-wait's own repeated
|
||||
`xhci_poll_events()` calls would recurse into live Event Ring/ERDP processing, the same class of
|
||||
hazard this driver's `next_action` deferral mechanism exists to avoid for doorbell rings (see
|
||||
`xhci_dev_t`'s own doc comment on `next_action`, and the confirmed-live amd64 hang recorded
|
||||
there). A new `xhci_get_dev()` accessor (returns the module-static `xhci_dev_t*` this driver
|
||||
already latches internally, or NULL if not brought up) lets an outside caller — this increment's
|
||||
temp probe used `sk_repl_idle()`, immediately after its own `xhci_poll_events()` call had already
|
||||
returned — reach the device handle safely; every earlier caller of this driver's public API
|
||||
already had one in hand (`kernel_main.c`'s local `xhci_dev_t`), which doesn't help code that only
|
||||
runs later, on a hotplug event it wasn't the one to observe.
|
||||
|
||||
SCSI READ CAPACITY(10) (SBC-3 section 5.14, opcode `0x25`) is the other half: `blk_subsys_attach_device()`
|
||||
reads `info.total_blocks` (via `blkio_info()`) before it does anything else, and there was no way
|
||||
for this driver to learn a device's block size/capacity at all until now.
|
||||
`xhci_bot_send_read_capacity10()` mirrors `xhci_bot_send_read10()`'s CBW-build shape (opcode +
|
||||
all-zero CDB otherwise, 8-byte Data-In reply); on a PASS CSW the reply's big-endian last-LBA and
|
||||
block-length fields are parsed into `dev->bot_cap_last_lba`/`bot_cap_block_size`. First attempt
|
||||
sent it bare (no TEST UNIT READY guard) and it failed — CSW status FAILED, residue 8 — the exact
|
||||
first-command UNIT ATTENTION READ(10) used to eat before `xhci_bot_read_block()` existed, caught
|
||||
immediately by the live capture rather than assumed away. Fixed the same way READ(10) was: a new
|
||||
`bot_tur_chain_target` field (`BOT_TUR_CHAIN_READ10`/`BOT_TUR_CHAIN_READ_CAPACITY10`) lets TEST
|
||||
UNIT READY's own PASS handling tell which command to chain into next — `xhci_bot_get_capacity()`
|
||||
is the new TUR-guarded entry point (sets the chain target, resets the retry counter, issues TUR
|
||||
first), matching `xhci_bot_read_block()`'s own shape exactly.
|
||||
|
||||
`bot_data_buf` grew from 512 to 1024 bytes — `block_subsystem.c`'s own header comment says
|
||||
blkio backends operate on 1 KiB units, i.e. one Forth block is two consecutive 512-byte SCSI
|
||||
blocks, and 512 bytes could never hold that.
|
||||
|
||||
Verified live via a temporary probe (written, run once, log captured, then reverted per this
|
||||
project's own probe convention — hot-attached `disk/usb-thumbdrive-test.img` through the running
|
||||
instance's QMP socket, same as every earlier BOT capture), amd64 only (the mechanism is
|
||||
architecture-independent code already proven identical across all three arches by every earlier
|
||||
BOT increment, same reasoning as the TEST UNIT READY increment's own capture scope): full chain
|
||||
`CBW (TEST UNIT READY)` → `FAILED` → retry → `PASS` → `unit ready -- issuing READ CAPACITY10` →
|
||||
`CBW (READ CAPACITY10) submitted` → `CSW status = PASS` → `READ CAPACITY last LBA=0x0001ffff` /
|
||||
`block size=0x00000200` → `probe READ CAPACITY10 PASS`, immediately followed by a second
|
||||
TUR→READ(10) chain (`xhci_bot_read_block(dev, slot, 0, 2, 512)`, exercising the grown buffer)
|
||||
also `PASS`. `0x1ffff` (131071) × `0x200` (512) = exactly 64 MiB, matching
|
||||
`usb-thumbdrive-test.img`'s real size byte for byte — proof this isn't just a well-formed reply,
|
||||
but the *correct* one. `logs/20260825-120043/amd64/` (first attempt, READ CAPACITY10 bare,
|
||||
FAILED — kept as evidence of the bug this increment's own TUR-guard fix addresses, not deleted
|
||||
as a failed run), `logs/20260825-121339/amd64/` (fixed, full PASS chain). The probe (a
|
||||
`bot_probe_pending`/`bot_probe_slot_id` flag pair set by the SET_CONFIGURATION handler and
|
||||
consumed in `sk_repl_idle()`) was removed after capture; `xhci_bot_wait_for_idle()`,
|
||||
`xhci_get_dev()`, `xhci_bot_send_read_capacity10()`, and `xhci_bot_get_capacity()` remain as
|
||||
permanent, reusable substrate for the next 2h increment (the actual `blkio_usb.c` backend).
|
||||
Re-verified probe-free afterward, all three architectures, clean boots with no BOT activity:
|
||||
`logs/20260825-122016/amd64/`, `logs/20260825-122318/aarch64/`, `logs/20260825-122527/riscv64/`.
|
||||
|
||||
Still ahead for 2h: the `blkio_usb.c` backend itself (per `blk_format_or_load_disk()`'s own
|
||||
"NEVER writes to disk here" discipline at attach time, a read-only backend is sufficient to land
|
||||
first — WRITE(10) doesn't exist in this driver yet and isn't a prerequisite), the connect-time
|
||||
call site wiring a ready `blkio_dev*` into `blk_subsys_attach_device()`, and the hot-detach path.
|
||||
|
||||
### Milestone 3 — Block subsystem extensions (Section U items 3-6, Section V area A)
|
||||
|
||||
Depends on Milestone 2 existing (needs a real device to test against, though the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-25T15:42:49Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-25T16:24:57Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
@@ -320,6 +320,19 @@ typedef struct {
|
||||
* xhci_dev_t's bot_tur_retries doc comment. */
|
||||
#define XHCI_BOT_TUR_MAX_RETRIES 3u
|
||||
|
||||
/* SCSI READ CAPACITY(10) (SBC-3 section 5.14) -- 10-byte CDB, opcode 0x25,
|
||||
* every other CDB byte reserved/zero for the standard "report capacity"
|
||||
* form (LBA field left 0, PMI bit left clear). 8-byte Data-In reply:
|
||||
* bytes 0-3 = Returned Logical Block Address (the *last* valid LBA, not a
|
||||
* block count) big-endian, bytes 4-7 = Block Length in Bytes big-endian.
|
||||
* This is Milestone 2h's prerequisite for everything else -- there is no
|
||||
* other way for this driver to learn a device's block size or capacity,
|
||||
* and block_subsystem.c's blk_subsys_attach_device() needs exactly that
|
||||
* (via blkio_info()) before it can do anything with a device. */
|
||||
#define SCSI_CMD_READ_CAPACITY10 0x25u
|
||||
#define SCSI_CDB_LEN_READ_CAPACITY10 10u
|
||||
#define SCSI_READ_CAPACITY10_DATA_LEN 8u
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -158,17 +158,20 @@ typedef struct {
|
||||
* 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. */
|
||||
* fixed 1024-byte Data-In destination -- sized to cover exactly one
|
||||
* Forth block (BLKIO_FORTH_BLOCK_SIZE, block_subsystem.c's own unit)
|
||||
* as two consecutive 512-byte SCSI blocks, which is what
|
||||
* xhci_bot_read_block() actually requests once Milestone 2h's blkio
|
||||
* backend calls this path with real block-subsystem-driven sizes; grown
|
||||
* from the single-512-byte-block buffer of the increment that first
|
||||
* added it. 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];
|
||||
uint8_t bot_data_buf[1024];
|
||||
uint32_t bot_expected_data_len;
|
||||
|
||||
/* Milestone 2g follow-up: TEST UNIT READY unit-init sequence, ahead of
|
||||
@@ -183,16 +186,51 @@ typedef struct {
|
||||
* pending bot_read10_* fields latch a caller's requested READ(10) so
|
||||
* it can be issued once TUR reports PASS -- xhci_bot_read_block() is
|
||||
* the entry point that stages these and kicks off TUR first, rather
|
||||
* than callers driving xhci_bot_send_read10() directly. */
|
||||
* than callers driving xhci_bot_send_read10() directly.
|
||||
*
|
||||
* Milestone 2h adds BOT_CMD_READ_CAPACITY10 (see
|
||||
* xhci_bot_send_read_capacity10()) and bot_last_status: every command
|
||||
* kind now resets bot_cmd_kind to BOT_CMD_NONE and sets
|
||||
* bot_last_status once its own CSW is fully processed (TEST UNIT
|
||||
* READY is the one exception -- a PASS or an in-progress retry both
|
||||
* stay non-terminal, chaining into the next command instead). This is
|
||||
* what lets xhci_bot_wait_for_idle() -- a synchronous busy-wait,
|
||||
* called from OUTSIDE xhci_poll_events(), never from within it --
|
||||
* detect "this command's whole chain is finished" without needing to
|
||||
* know which specific command it was waiting on. */
|
||||
enum {
|
||||
BOT_CMD_NONE = 0,
|
||||
BOT_CMD_TEST_UNIT_READY,
|
||||
BOT_CMD_READ10
|
||||
BOT_CMD_READ10,
|
||||
BOT_CMD_READ_CAPACITY10
|
||||
} bot_cmd_kind;
|
||||
enum {
|
||||
BOT_STATUS_IDLE = 0,
|
||||
BOT_STATUS_PASS,
|
||||
BOT_STATUS_FAILED,
|
||||
BOT_STATUS_TIMEOUT
|
||||
} bot_last_status;
|
||||
/* Which command a TUR-PASS should chain into -- TEST UNIT READY's own
|
||||
* completion handling can't tell READ10 and READ CAPACITY10 apart
|
||||
* otherwise, since both now go through the identical TUR-first
|
||||
* sequencing xhci_bot_read_block()/xhci_bot_get_capacity() both use.
|
||||
* Set by whichever of those two entry points kicked off the TUR. */
|
||||
enum {
|
||||
BOT_TUR_CHAIN_NONE = 0,
|
||||
BOT_TUR_CHAIN_READ10,
|
||||
BOT_TUR_CHAIN_READ_CAPACITY10
|
||||
} 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;
|
||||
/* 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
|
||||
* must check xhci_bot_wait_for_idle()'s return value, not just read
|
||||
* these blindly. */
|
||||
uint32_t bot_cap_last_lba;
|
||||
uint32_t bot_cap_block_size;
|
||||
|
||||
/* Deferred chaining: a doorbell ring (new control transfer) must
|
||||
* never happen synchronously from inside xhci_poll_events()'s event-
|
||||
@@ -214,7 +252,8 @@ typedef struct {
|
||||
XHCI_NEXT_ACTION_BOT_DATA_IN,
|
||||
XHCI_NEXT_ACTION_BOT_CSW_RECEIVE,
|
||||
XHCI_NEXT_ACTION_BOT_SEND_TUR,
|
||||
XHCI_NEXT_ACTION_BOT_SEND_READ10
|
||||
XHCI_NEXT_ACTION_BOT_SEND_READ10,
|
||||
XHCI_NEXT_ACTION_BOT_SEND_READ_CAPACITY10
|
||||
} next_action;
|
||||
uint32_t next_action_slot_id;
|
||||
uint16_t next_action_length;
|
||||
@@ -256,6 +295,19 @@ int xhci_find_and_map(xhci_dev_t *dev);
|
||||
*/
|
||||
int xhci_bringup(xhci_dev_t *dev);
|
||||
|
||||
/*
|
||||
* xhci_get_dev — return the module-static xhci_dev_t* xhci_poll_events()
|
||||
* itself reads (only one controller is supported), or NULL
|
||||
* if xhci_bringup() has not completed successfully yet.
|
||||
*
|
||||
* Milestone 2h: callers outside this driver (sk_repl_idle(), eventually
|
||||
* the block-subsystem attach glue) have no other way to reach the device
|
||||
* handle -- every earlier caller of this driver's API already had one in
|
||||
* hand (kernel_main.c's own local xhci_dev_t), which doesn't help code
|
||||
* that only runs later, on a hotplug event it wasn't the one to observe.
|
||||
*/
|
||||
xhci_dev_t *xhci_get_dev(void);
|
||||
|
||||
/*
|
||||
* xhci_poll_events — read Interrupter 0's Event Ring, dispatching each TRB
|
||||
* by type: Port Status Change reads PORTSC to log
|
||||
@@ -473,6 +525,92 @@ 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_send_read_capacity10 — build a Command Block Wrapper for SCSI
|
||||
* READ CAPACITY(10) (SBC-3 section 5.14,
|
||||
* opcode 0x25, see xhci.h) and submit it
|
||||
* on the bulk OUT Transfer Ring. Sets
|
||||
* dev->bot_cmd_kind = BOT_CMD_READ_CAPACITY10
|
||||
* and dev->bot_expected_data_len =
|
||||
* SCSI_READ_CAPACITY10_DATA_LEN (8) so
|
||||
* the existing CBW-completion handler
|
||||
* runs the Data-In stage (unlike TEST
|
||||
* UNIT READY, this command does have a
|
||||
* short reply). On a PASS CSW,
|
||||
* dev->bot_cap_last_lba/bot_cap_block_size
|
||||
* are parsed from the 8-byte reply and
|
||||
* bot_cmd_kind resets to BOT_CMD_NONE --
|
||||
* this command is always terminal, it
|
||||
* never chains into anything else.
|
||||
*
|
||||
* Requires the same bulk endpoint/ring prerequisites as
|
||||
* xhci_bot_send_read10()/xhci_bot_send_test_unit_ready() -- refuses if any
|
||||
* are missing.
|
||||
*
|
||||
* Low-level primitive -- does not run TEST UNIT READY first. Sent to a
|
||||
* freshly attached device with no TUR ahead of it, this eats the same
|
||||
* first-command UNIT ATTENTION READ(10) used to before xhci_bot_read_block()
|
||||
* existed (confirmed live -- see this driver's own Milestone 2h capture
|
||||
* log). Most callers want xhci_bot_get_capacity() below instead; this is
|
||||
* called directly only by xhci_poll_events()'s own deferred dispatch
|
||||
* (XHCI_NEXT_ACTION_BOT_SEND_READ_CAPACITY10, once a prior TUR has
|
||||
* reported PASS).
|
||||
*
|
||||
* Returns 0 if the CBW was posted, -1 if a prerequisite is missing.
|
||||
*/
|
||||
int xhci_bot_send_read_capacity10(xhci_dev_t *dev, uint32_t slot_id);
|
||||
|
||||
/*
|
||||
* xhci_bot_get_capacity — the real entry point for learning a device's
|
||||
* block size/capacity. Sets
|
||||
* dev->bot_tur_chain_target =
|
||||
* BOT_TUR_CHAIN_READ_CAPACITY10, resets
|
||||
* dev->bot_tur_retries, and issues a TEST UNIT
|
||||
* READY first rather than a bare READ CAPACITY(10)
|
||||
* -- same reasoning as xhci_bot_read_block(),
|
||||
* and the same TUR retry budget
|
||||
* (XHCI_BOT_TUR_MAX_RETRIES).
|
||||
*
|
||||
* Returns 0 if TEST UNIT READY was posted, -1 if a prerequisite is
|
||||
* missing (same checks xhci_bot_send_read_capacity10() performs, done up
|
||||
* front here so a bad request is rejected before spending a TUR
|
||||
* round-trip on it).
|
||||
*/
|
||||
int xhci_bot_get_capacity(xhci_dev_t *dev, uint32_t slot_id);
|
||||
|
||||
/*
|
||||
* xhci_bot_wait_for_idle — busy-wait for the BOT command currently in
|
||||
* flight (bot_cmd_kind != BOT_CMD_NONE) to reach
|
||||
* a terminal state, by calling xhci_poll_events()
|
||||
* in a loop up to max_iters times.
|
||||
*
|
||||
* This is Milestone 2h's synchronous bridge over an otherwise fully
|
||||
* asynchronous, polled driver -- block_subsystem.c's blkio_read()/
|
||||
* blkio_info() etc. are ordinary synchronous function calls with no way
|
||||
* to "come back later" for a result, so something has to spin until the
|
||||
* driver's own event-driven state machine finishes.
|
||||
*
|
||||
* MUST NOT be called from inside xhci_poll_events() itself, or from any
|
||||
* function xhci_poll_events() calls (a next_action dispatch, a Transfer
|
||||
* Event handler) -- xhci_poll_events() is not reentrant, and this
|
||||
* function's own busy-wait loop calls it again on every iteration; doing
|
||||
* so from within an already-running call would recurse into live Event
|
||||
* Ring/ERDP processing (the same class of hazard this driver's
|
||||
* next_action deferral mechanism exists to avoid for doorbell rings, see
|
||||
* xhci_dev_t's own doc comment on next_action). Only call this from a
|
||||
* context that is definitely outside that call frame -- a caller in
|
||||
* sk_repl_idle() invoked strictly after its own xhci_poll_events() call
|
||||
* has already returned, for example.
|
||||
*
|
||||
* Returns BOT_STATUS_PASS/BOT_STATUS_FAILED (dev->bot_last_status, as left
|
||||
* by whichever command was in flight) once bot_cmd_kind returns to
|
||||
* BOT_CMD_NONE, or BOT_STATUS_TIMEOUT if max_iters is exhausted first
|
||||
* (the command may still complete later -- this driver has no cancel
|
||||
* operation, the caller just stops waiting). Returns BOT_STATUS_FAILED
|
||||
* immediately if dev is NULL.
|
||||
*/
|
||||
int xhci_bot_wait_for_idle(xhci_dev_t *dev, uint32_t max_iters);
|
||||
|
||||
/*
|
||||
* xhci_bot_read_data_in — submit a Normal TRB on the bulk IN Transfer
|
||||
* Ring to read dev->bot_expected_data_len bytes
|
||||
|
||||
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
+109
-6
@@ -133,6 +133,8 @@ 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_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_receive_csw(xhci_dev_t *dev, uint32_t slot_id);
|
||||
int xhci_ep0_get_device_descriptor(xhci_dev_t *dev, uint32_t slot_id);
|
||||
@@ -315,10 +317,14 @@ int xhci_bringup(xhci_dev_t *dev)
|
||||
dev->bot_last_tag = 0;
|
||||
dev->bot_expected_data_len = 0;
|
||||
dev->bot_cmd_kind = BOT_CMD_NONE;
|
||||
dev->bot_last_status = BOT_STATUS_IDLE;
|
||||
dev->bot_tur_chain_target = BOT_TUR_CHAIN_NONE;
|
||||
dev->bot_tur_retries = 0;
|
||||
dev->bot_read10_lba = 0;
|
||||
dev->bot_read10_num_blocks = 0;
|
||||
dev->bot_read10_block_size = 0;
|
||||
dev->bot_cap_last_lba = 0;
|
||||
dev->bot_cap_block_size = 0;
|
||||
dev->next_action = XHCI_NEXT_ACTION_NONE;
|
||||
dev->next_action_slot_id = 0;
|
||||
dev->next_action_length = 0;
|
||||
@@ -337,6 +343,11 @@ int xhci_bringup(xhci_dev_t *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
xhci_dev_t *xhci_get_dev(void)
|
||||
{
|
||||
return g_xhci_dev;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Command Ring submission -- Milestone 2e. Shared by Enable Slot now and
|
||||
* Address Device next; xhci_poll_events() above is the read side of this
|
||||
@@ -752,6 +763,40 @@ int xhci_bot_send_test_unit_ready(xhci_dev_t *dev, uint32_t slot_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xhci_bot_send_read_capacity10(xhci_dev_t *dev, uint32_t slot_id)
|
||||
{
|
||||
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;
|
||||
|
||||
dev->bot_cmd_kind = BOT_CMD_READ_CAPACITY10;
|
||||
|
||||
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 = SCSI_READ_CAPACITY10_DATA_LEN;
|
||||
cbw->dCBWDataTransferLength = SCSI_READ_CAPACITY10_DATA_LEN;
|
||||
cbw->bmCBWFlags = USB_BOT_CBW_FLAG_DATA_IN; /* READ CAPACITY(10): device -> host data stage */
|
||||
cbw->bCBWLUN = USB_BOT_CBW_LUN_DEFAULT;
|
||||
cbw->bCBWCBLength = SCSI_CDB_LEN_READ_CAPACITY10;
|
||||
for (uint32_t i = 0; i < sizeof(cbw->CBWCB); i++) cbw->CBWCB[i] = 0;
|
||||
cbw->CBWCB[0] = SCSI_CMD_READ_CAPACITY10; /* opcode 0x25, standard "report
|
||||
* capacity" form -- LBA field
|
||||
* and PMI bit left zero, see
|
||||
* this constant's own doc
|
||||
* comment in xhci.h */
|
||||
|
||||
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 (READ CAPACITY10) submitted");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xhci_bot_read_block(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
|
||||
uint16_t num_blocks, uint32_t block_size)
|
||||
{
|
||||
@@ -762,11 +807,33 @@ int xhci_bot_read_block(xhci_dev_t *dev, uint32_t slot_id, uint32_t lba,
|
||||
dev->bot_read10_lba = lba;
|
||||
dev->bot_read10_num_blocks = num_blocks;
|
||||
dev->bot_read10_block_size = block_size;
|
||||
dev->bot_tur_chain_target = BOT_TUR_CHAIN_READ10;
|
||||
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;
|
||||
if (dev->bulk_out_ep_addr == 0 || dev->bulk_in_ep_addr == 0) return -1;
|
||||
|
||||
dev->bot_tur_chain_target = BOT_TUR_CHAIN_READ_CAPACITY10;
|
||||
dev->bot_tur_retries = 0;
|
||||
|
||||
return xhci_bot_send_test_unit_ready(dev, slot_id);
|
||||
}
|
||||
|
||||
int xhci_bot_wait_for_idle(xhci_dev_t *dev, uint32_t max_iters)
|
||||
{
|
||||
if (!dev) return BOT_STATUS_FAILED;
|
||||
for (uint32_t i = 0; i < max_iters; i++) {
|
||||
if (dev->bot_cmd_kind == BOT_CMD_NONE) return (int)dev->bot_last_status;
|
||||
xhci_poll_events();
|
||||
}
|
||||
return BOT_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
/* 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
|
||||
@@ -1371,9 +1438,13 @@ void xhci_poll_events(void)
|
||||
* misread as a clean pass. */
|
||||
if (dev->bot_csw.dCSWSignature != USB_BOT_CSW_SIGNATURE) {
|
||||
console_println("xhci: CSW signature mismatch -- discarding");
|
||||
dev->bot_last_status = BOT_STATUS_FAILED;
|
||||
dev->bot_cmd_kind = BOT_CMD_NONE;
|
||||
break;
|
||||
} else if (dev->bot_csw.dCSWTag != dev->bot_last_tag) {
|
||||
console_println("xhci: CSW tag mismatch -- discarding");
|
||||
dev->bot_last_status = BOT_STATUS_FAILED;
|
||||
dev->bot_cmd_kind = BOT_CMD_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1393,13 +1464,20 @@ void xhci_poll_events(void)
|
||||
* target's standard first-command UNIT
|
||||
* ATTENTION behavior, not a driver defect (see
|
||||
* SCSI_CMD_TEST_UNIT_READY's doc comment in
|
||||
* xhci.h). READ10 completions are terminal --
|
||||
* this driver has no synchronous caller to
|
||||
* report back to yet (2h's problem). */
|
||||
* xhci.h). Every other command kind is
|
||||
* terminal here: bot_last_status/bot_cmd_kind
|
||||
* reset so a synchronous caller waiting in
|
||||
* xhci_bot_wait_for_idle() (Milestone 2h) sees
|
||||
* the command as finished. */
|
||||
if (dev->bot_cmd_kind == BOT_CMD_TEST_UNIT_READY) {
|
||||
if (csw_pass) {
|
||||
console_println("xhci: unit ready -- issuing READ10");
|
||||
dev->next_action = XHCI_NEXT_ACTION_BOT_SEND_READ10;
|
||||
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 {
|
||||
console_println("xhci: unit ready -- issuing READ10");
|
||||
dev->next_action = XHCI_NEXT_ACTION_BOT_SEND_READ10;
|
||||
}
|
||||
dev->next_action_slot_id = xfer_slot_id;
|
||||
} else if (dev->bot_tur_retries < XHCI_BOT_TUR_MAX_RETRIES) {
|
||||
dev->bot_tur_retries++;
|
||||
@@ -1407,8 +1485,27 @@ void xhci_poll_events(void)
|
||||
dev->next_action = XHCI_NEXT_ACTION_BOT_SEND_TUR;
|
||||
dev->next_action_slot_id = xfer_slot_id;
|
||||
} else {
|
||||
console_println("xhci: unit still not ready -- giving up on READ10");
|
||||
console_println("xhci: unit still not ready -- giving up");
|
||||
dev->bot_last_status = BOT_STATUS_FAILED;
|
||||
dev->bot_cmd_kind = BOT_CMD_NONE;
|
||||
}
|
||||
} else if (dev->bot_cmd_kind == BOT_CMD_READ_CAPACITY10) {
|
||||
if (csw_pass) {
|
||||
const uint8_t *d = dev->bot_data_buf;
|
||||
dev->bot_cap_last_lba = ((uint32_t)d[0] << 24) | ((uint32_t)d[1] << 16) |
|
||||
((uint32_t)d[2] << 8) | (uint32_t)d[3];
|
||||
dev->bot_cap_block_size = ((uint32_t)d[4] << 24) | ((uint32_t)d[5] << 16) |
|
||||
((uint32_t)d[6] << 8) | (uint32_t)d[7];
|
||||
xhci_log_hex32("xhci: READ CAPACITY last LBA=", dev->bot_cap_last_lba);
|
||||
xhci_log_hex32("xhci: READ CAPACITY block size=", dev->bot_cap_block_size);
|
||||
}
|
||||
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. */
|
||||
dev->bot_last_status = csw_pass ? BOT_STATUS_PASS : BOT_STATUS_FAILED;
|
||||
dev->bot_cmd_kind = BOT_CMD_NONE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1502,5 +1599,11 @@ void xhci_poll_events(void)
|
||||
dev->bot_read10_block_size) != 0) {
|
||||
console_println("xhci: deferred READ10 setup failed");
|
||||
}
|
||||
} else if (dev->next_action == XHCI_NEXT_ACTION_BOT_SEND_READ_CAPACITY10) {
|
||||
uint32_t next_slot_id = dev->next_action_slot_id;
|
||||
dev->next_action = XHCI_NEXT_ACTION_NONE;
|
||||
if (xhci_bot_send_read_capacity10(dev, next_slot_id) != 0) {
|
||||
console_println("xhci: deferred READ CAPACITY10 setup failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user