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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-25T12:37:41Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-25T13:03:55Z -->
|
||||
<!-- 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.
@@ -190,6 +190,7 @@ typedef struct {
|
||||
#define XHCI_TRB_TYPE(ctrl) (((ctrl) & XHCI_TRB_CONTROL_TYPE_MASK) >> XHCI_TRB_CONTROL_TYPE_SHIFT)
|
||||
|
||||
/* TRB types used by this driver (subset — xHCI defines many more) */
|
||||
#define XHCI_TRB_TYPE_NORMAL 1 /* Transfer Ring, bulk/interrupt/isoch -- not EP0 */
|
||||
#define XHCI_TRB_TYPE_LINK 6 /* ring-wraparound marker, Command/Transfer Rings only */
|
||||
#define XHCI_TRB_TYPE_ENABLE_SLOT_CMD 9
|
||||
#define XHCI_TRB_TYPE_DISABLE_SLOT_CMD 10
|
||||
@@ -275,6 +276,36 @@ typedef struct {
|
||||
#define USB_EP_ATTR_TYPE_MASK 0x03u
|
||||
#define USB_EP_TYPE_BULK 0x02u
|
||||
|
||||
/* Bulk-Only Transport Command Block Wrapper (USB Mass Storage Class Bulk-
|
||||
* Only Transport spec, section 5.1) -- sent host-to-device on the bulk OUT
|
||||
* endpoint ahead of every SCSI command's data phase. Fixed 31-byte wire
|
||||
* layout; every multi-byte field is little-endian, which this driver's
|
||||
* targets (amd64/aarch64/riscv64, all little-endian) already assume
|
||||
* throughout (no htole32-style conversions anywhere in this codebase) --
|
||||
* direct field assignment is wire-correct as-is. sizeof() is NOT used as
|
||||
* this struct's DMA length anywhere (may be padded to 32 by the compiler
|
||||
* to satisfy the uint32_t members' alignment) -- USB_BOT_CBW_LENGTH (31)
|
||||
* is the correct, explicit wire length, matching the same
|
||||
* offset-constant-not-sizeof discipline already used for the USB
|
||||
* descriptor field offsets above. */
|
||||
typedef struct {
|
||||
uint32_t dCBWSignature;
|
||||
uint32_t dCBWTag;
|
||||
uint32_t dCBWDataTransferLength;
|
||||
uint8_t bmCBWFlags;
|
||||
uint8_t bCBWLUN;
|
||||
uint8_t bCBWCBLength;
|
||||
uint8_t CBWCB[16];
|
||||
} usb_bot_cbw_t;
|
||||
|
||||
#define USB_BOT_CBW_SIGNATURE 0x43425355u /* "USBC", wire byte order U,S,B,C as an LE dword */
|
||||
#define USB_BOT_CBW_LENGTH 31u
|
||||
#define USB_BOT_CBW_FLAG_DATA_IN 0x80u /* bmCBWFlags: device-to-host data stage */
|
||||
#define USB_BOT_CBW_LUN_DEFAULT 0u /* no multi-LUN support -- single-LUN devices only */
|
||||
|
||||
#define SCSI_CMD_READ10 0x28u
|
||||
#define SCSI_CDB_LEN_READ10 10u
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -113,7 +113,8 @@ typedef struct {
|
||||
XHCI_XFER_DEVICE_DESC,
|
||||
XHCI_XFER_CONFIG_DESC_SHORT,
|
||||
XHCI_XFER_CONFIG_DESC_FULL,
|
||||
XHCI_XFER_SET_CONFIG
|
||||
XHCI_XFER_SET_CONFIG,
|
||||
XHCI_XFER_CBW_SENT
|
||||
} transfer_purpose;
|
||||
uint32_t pending_transfer_slot_id;
|
||||
uint8_t device_descriptor[18];
|
||||
@@ -148,6 +149,18 @@ 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. */
|
||||
usb_bot_cbw_t bot_cbw;
|
||||
uint32_t bot_next_tag;
|
||||
|
||||
/* Deferred chaining: a doorbell ring (new control transfer) must
|
||||
* never happen synchronously from inside xhci_poll_events()'s event-
|
||||
* processing loop, before ERDP has been updated for the event
|
||||
@@ -323,6 +336,34 @@ 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);
|
||||
|
||||
/*
|
||||
* xhci_bot_send_read10 — build a Command Block Wrapper for a SCSI
|
||||
* READ(10) and submit it on the bulk OUT Transfer
|
||||
* Ring.
|
||||
*
|
||||
* 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).
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Returns 0 if the CBW was posted, -1 if a prerequisite is missing.
|
||||
*/
|
||||
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_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
@@ -130,6 +130,8 @@ int xhci_cmd_disable_slot(xhci_dev_t *dev, uint32_t slot_id);
|
||||
int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
|
||||
uint32_t port_id, uint32_t speed);
|
||||
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_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);
|
||||
@@ -306,6 +308,7 @@ int xhci_bringup(xhci_dev_t *dev)
|
||||
dev->bulk_out_ring = NULL;
|
||||
dev->bulk_out_ring_cycle = 1;
|
||||
dev->bulk_out_ring_enq = 0;
|
||||
dev->bot_next_tag = 1;
|
||||
dev->next_action = XHCI_NEXT_ACTION_NONE;
|
||||
dev->next_action_slot_id = 0;
|
||||
dev->next_action_length = 0;
|
||||
@@ -618,6 +621,90 @@ int xhci_cmd_configure_endpoint(xhci_dev_t *dev, uint32_t slot_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Enqueue one Normal TRB to the bulk OUT Transfer Ring -- same fixed-
|
||||
* ring-plus-Link-TRB wraparound pattern as xhci_ep0_enqueue_trb(),
|
||||
* operating on bulk_out_ring/bulk_out_ring_enq/bulk_out_ring_cycle
|
||||
* instead of the EP0 ring's fields. A CBW is always exactly one TRB
|
||||
* (no Setup/Data/Status split -- that's a control-transfer-only
|
||||
* concept), so unlike xhci_ep0_enqueue_trb() this rings the doorbell
|
||||
* itself rather than leaving that to a caller assembling a group. */
|
||||
static void xhci_bulk_out_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_out_ring[dev->bulk_out_ring_enq];
|
||||
trb->parameter = parameter;
|
||||
trb->status = status;
|
||||
trb->control = control_bits | (dev->bulk_out_ring_cycle ? XHCI_TRB_CONTROL_CYCLE : 0);
|
||||
|
||||
dev->bulk_out_ring_enq++;
|
||||
if (dev->bulk_out_ring_enq == XHCI_RING_TRB_COUNT - 1) {
|
||||
dev->bulk_out_ring[XHCI_RING_TRB_COUNT - 1].control =
|
||||
(XHCI_TRB_TYPE_LINK << XHCI_TRB_CONTROL_TYPE_SHIFT) |
|
||||
XHCI_TRB_CONTROL_TC |
|
||||
(dev->bulk_out_ring_cycle ? XHCI_TRB_CONTROL_CYCLE : 0);
|
||||
dev->bulk_out_ring_enq = 0;
|
||||
dev->bulk_out_ring_cycle ^= 1u;
|
||||
}
|
||||
|
||||
/* Doorbell target is the bulk OUT endpoint's own DCI, not target 1
|
||||
* (EP0) -- distinct rings need distinct doorbell targets so the
|
||||
* controller knows which Transfer Ring just gained a new TRB. */
|
||||
dev->doorbell[slot_id] = XHCI_DB_TARGET(XHCI_EP_ADDR_TO_DCI(dev->bulk_out_ep_addr));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
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;
|
||||
for (uint32_t i = 0; i < sizeof(cbw->CBWCB); i++) cbw->CBWCB[i] = 0;
|
||||
|
||||
/* SCSI READ(10) CDB (SBC-3 section 5.13): opcode, then LBA and
|
||||
* Transfer Length as big-endian fields -- SCSI multi-byte fields are
|
||||
* big-endian on the wire regardless of host or USB byte order, unlike
|
||||
* every other multi-byte value in this driver (TRBs, contexts, CBW
|
||||
* itself), which are all little-endian. Written byte-by-byte rather
|
||||
* than via a struct + byte-swap helper, matching this codebase's
|
||||
* existing preference for explicit field layout over struct-based
|
||||
* binary formats wherever the layout isn't naturally what a C struct
|
||||
* would produce (see usb_bot_cbw_t's own doc comment, and the
|
||||
* Interface/Endpoint descriptor offset macros). */
|
||||
cbw->CBWCB[0] = SCSI_CMD_READ10;
|
||||
cbw->CBWCB[1] = 0; /* flags: no FUA/DPO/RDPROTECT 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;
|
||||
|
||||
/* IOC set -- the CBW is always exactly one TRB, so it alone signals
|
||||
* "this transfer is done" (matching the EP0 control-read pattern of
|
||||
* IOC on the one TRB whose completion means something). Length is
|
||||
* USB_BOT_CBW_LENGTH (31), not sizeof(*cbw) -- see usb_bot_cbw_t's
|
||||
* own doc comment on why. */
|
||||
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 (READ10) 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
|
||||
@@ -1131,6 +1218,15 @@ void xhci_poll_events(void)
|
||||
console_println("xhci: device configured");
|
||||
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");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console_println("xhci: transfer event");
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user