Artemis Milestone 2f: Configuration descriptor read + Mass Storage/BOT class confirmation

Chains off the device descriptor request via a new deferred-action mechanism
on xhci_dev_t (next_action/next_action_slot_id/next_action_length): a short
9-byte Configuration descriptor read learns wTotalLength, then a full read
retrieves Config+Interface+Endpoint descriptors, walked for the Interface
descriptor to confirm bInterfaceClass/SubClass/Protocol == Mass Storage/
SCSI/Bulk-Only Transport.

The deferral exists because ringing the next doorbell synchronously inside
xhci_poll_events()'s event-processing loop -- before the current event's
ERDP write -- hung the guest outright (confirmed live via checkpoint
logging, amd64). Fixed by moving the actual control-transfer submission to
a small dispatch at the end of xhci_poll_events(), after ERDP is updated.

A debug hack that shipped mid-session (forcing a repeated 9-byte read
instead of chaining into the real 44-byte length, to isolate whether the
hang was doorbell-ordering or length-specific) has been reverted: restored
the real length and re-verified live. The doorbell-ordering fix was the
whole story -- the 44-byte read completes cleanly.

Verified live via QMP hotplug, all three architectures, byte-identical
results: wTotalLength=0x2c, bInterfaceClass=0x08, bInterfaceSubClass=0x06,
bInterfaceProtocol=0x50 -- confirmed Mass Storage/SCSI/BOT. Disconnect
confirmed clean on every arch, no wedge. FABRIC-2.md Section X Milestone 2f
updated with the full writeup.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPfdtaXs9ay1nbwuMnrscu
This commit is contained in:
Robert Allan James
2026-08-25 07:27:07 -04:00
co-authored by Claude Sonnet 5
parent 2c34e45d05
commit b9c540a78b
22 changed files with 101974 additions and 52 deletions
+43 -4
View File
@@ -3328,12 +3328,14 @@ command, DCBAA entry clear), and the callback surface into Section U's higher-le
`usb-storage`), meaning class is defined at the interface level, not device level, for
standard Mass Storage devices — confirms the "is this a USB drive" check has to read the
Configuration/Interface descriptor (next item below), not the device descriptor
- [ ] Request and parse the configuration descriptor
- [ ] Confirm the device reports the Mass Storage class / Bulk-Only Transport subclass/
- [x] Request and parse the configuration descriptor**done 2026-08-25**, see writeup below
- [x] Confirm the device reports the Mass Storage class / Bulk-Only Transport subclass/
protocol (this is the actual "is this a USB drive" check — separate from, and prior
to, Section U item 7's foreign-drive/home-blocks-signature check, which happens one
layer up, after the drive is already known to be USB mass storage)
- [ ] Set the device configuration (SET_CONFIGURATION control transfer)
layer up, after the drive is already known to be USB mass storage) — **done
2026-08-25**, see writeup below
- [ ] Set the device configuration (SET_CONFIGURATION control transfer) — still open, last
item before 2g's bulk transfers are possible
**Device descriptor request, done 2026-08-22.** Extends the EP0 Transfer Ring built in 2e's
Address Device work with real control-transfer submission: a new `xhci_ep0_enqueue_trb()`
@@ -3352,6 +3354,43 @@ results everywhere: `idVendor=0x46f4` (QEMU's own USB vendor ID), `idProduct=0x0
`bDeviceClass=0x00`. `logs/20260822-125515/amd64/`, `logs/20260822-125624/aarch64/`,
`logs/20260822-125800/riscv64/`.
**Configuration descriptor request/parse + Mass Storage/BOT class confirmation, done
2026-08-25.** Chains off the device descriptor success via a new `dev->next_action`/
`next_action_slot_id`/`next_action_length` deferral mechanism on `xhci_dev_t`, rather than
issuing the next control transfer synchronously from inside the Transfer Event handling in
`xhci_poll_events()`. That deferral is load-bearing, not stylistic: an earlier same-session
attempt that rang the next doorbell synchronously, still inside the event-processing loop and
before the current event's ERDP write, hung the guest outright — confirmed live on amd64 via
checkpoint logging (execution stopped exactly at the doorbell MMIO write). The fix moves the
actual `xhci_ep0_get_config_descriptor()` call to a small dispatch at the end of
`xhci_poll_events()`, after the main event loop and the ERDP update.
Sequencing is two control transfers, chained: a short 9-byte read (just the Configuration
descriptor header) to learn `wTotalLength`, then a second read for the full length —
`xhci_ep0_get_config_descriptor()` shares its control-read submission plumbing with the device
descriptor request via a new static `xhci_ep0_control_read()` helper. The full read (Config +
Interface + Endpoint descriptors, concatenated per USB spec) is then walked byte-by-byte
looking for `bDescriptorType == USB_DESC_TYPE_INTERFACE` — not read at a fixed offset, since an
Interface descriptor's position within the stream depends on the device's own interface/
endpoint layout — and `bInterfaceClass`/`bInterfaceSubClass`/`bInterfaceProtocol` checked
against Mass Storage (`0x08`) / SCSI transparent (`0x06`) / Bulk-Only Transport (`0x50`).
**A real debugging false lead, caught and corrected before commit.** While chasing the
doorbell-ordering hang above, a `/* DEBUG: force short length to isolate 3rd-transfer-vs-44-
byte */` hack shipped in an uncommitted, mid-session state: instead of chaining into the real
`wTotalLength` (0x2c = 44 bytes) once learned, it kept re-requesting the 9-byte short read
indefinitely, to test whether *any* 3rd control transfer would hang, independent of length.
That test never got run against the real path before the session ended. Restored the real
`total_len` and re-verified live: the full 44-byte read completes cleanly, no hang, on all
three architectures — the doorbell-ordering fix above was the whole story; the 44-byte length
was never actually implicated.
Verified live via QMP hotplug, all three architectures, byte-identical results everywhere:
`wTotalLength=0x2c`, `bInterfaceClass=0x08`, `bInterfaceSubClass=0x06`,
`bInterfaceProtocol=0x50``confirmed Mass Storage / SCSI / BOT device`. Disconnect (device_del)
also confirmed clean on every arch — no wedge, matches 2e's existing disconnect discipline.
`logs/20260825-071717/amd64/`, `logs/20260825-072116/aarch64/`, `logs/20260825-072329/riscv64/`.
**2g. Bulk-Only Transport (BOT) — the actual read/write path**
- [ ] Identify and configure the device's bulk IN and bulk OUT endpoints
- [ ] Implement CBW (Command Block Wrapper) construction and send, for a SCSI READ(10)
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-08-22T16:57:57Z -->
<!-- Generated by mkcapsule --manifest 2026-08-25T11:23:25Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
BIN
View File
Binary file not shown.
+24
View File
@@ -231,8 +231,32 @@ typedef struct {
#define USB_REQ_GET_DESCRIPTOR 6u
#define USB_DESC_TYPE_DEVICE 1u
#define USB_DESC_TYPE_CONFIG 2u
#define USB_DIR_DEVICE_TO_HOST 0x80u
/* Standard USB Interface descriptor field offsets (9 bytes, USB 2.0 spec
* table 9-12) -- Mass Storage class detection reads these three fields.
* Not decoded via a struct like usb_setup_packet_t: the Interface
* descriptor's exact position within a Configuration descriptor's byte
* stream isn't fixed (depends on the device's actual interface/endpoint
* layout), so it's found by walking the byte stream looking for
* bDescriptorType == USB_DESC_TYPE_INTERFACE, not by a fixed struct
* offset into the whole buffer. */
/* Every standard USB descriptor starts with these two bytes (bLength,
* bDescriptorType) -- used to walk the concatenated descriptor stream a
* full Configuration descriptor read returns (Config + Interface +
* Endpoint descriptors back to back), not just the Interface one. */
#define USB_DESC_OFF_LENGTH 0u
#define USB_DESC_OFF_TYPE 1u
#define USB_CONFIG_OFF_TOTAL_LENGTH 2u /* wTotalLength, 2 bytes, Configuration descriptor only */
#define USB_DESC_TYPE_INTERFACE 4u
#define USB_IFACE_OFF_CLASS 5u
#define USB_IFACE_OFF_SUBCLASS 6u
#define USB_IFACE_OFF_PROTOCOL 7u
#define USB_CLASS_MASS_STORAGE 0x08u
#define USB_SUBCLASS_SCSI 0x06u /* SCSI transparent command set */
#define USB_PROTOCOL_BOT 0x50u /* Bulk-Only Transport */
/* 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
+65 -10
View File
@@ -83,11 +83,47 @@ typedef struct {
* driver only ever has one control transfer outstanding at a time --
* pending_transfer_slot_id is 0 when idle, else the slot ID whose
* Transfer Event (posted only by the Status Stage TRB, which alone
* has IOC set) is still outstanding. device_descriptor is the
* (reused, not per-slot) buffer control transfers read into; 18
* bytes is the full standard USB device descriptor size. */
* has IOC set) is still outstanding. transfer_purpose says which
* request that is, since xhci_poll_events() needs to know which
* buffer to interpret and what (if anything) to chain next on
* success -- e.g. a successful short Configuration descriptor read
* chains into a full-length read once wTotalLength is known.
* device_descriptor is the full 18-byte standard USB device
* descriptor; config_descriptor holds the Configuration descriptor
* and everything after it in the same read (Interface + Endpoint
* descriptors, concatenated, per USB spec) -- fixed 128 bytes,
* comfortably covers a single-interface Mass Storage device's full
* descriptor set without a dynamic allocation. All reused (not
* per-slot), matching this driver's single-device scope. */
enum {
XHCI_XFER_NONE = 0,
XHCI_XFER_DEVICE_DESC,
XHCI_XFER_CONFIG_DESC_SHORT,
XHCI_XFER_CONFIG_DESC_FULL
} transfer_purpose;
uint32_t pending_transfer_slot_id;
uint8_t device_descriptor[18];
uint8_t config_descriptor[128];
uint16_t config_total_length;
/* 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
* currently being handled -- confirmed live (amd64 QEMU) to hang the
* guest outright when tried (a doorbell rung mid-acknowledgment of
* the previous event, evidenced by checkpoint logging showing
* execution stop exactly at the doorbell MMIO write). Chained
* requests (device descriptor -> short config read -> full config
* read) instead set these fields during event processing; the actual
* doorbell ring happens once, after the main loop and the ERDP
* write, from a small dispatch at the end of xhci_poll_events(). */
enum {
XHCI_NEXT_ACTION_NONE = 0,
XHCI_NEXT_ACTION_GET_DEVICE_DESC,
XHCI_NEXT_ACTION_GET_CONFIG_DESC
} next_action;
uint32_t next_action_slot_id;
uint16_t next_action_length;
} xhci_dev_t;
/*
@@ -197,19 +233,38 @@ int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
* Data-IN + Status-OUT stages) on
* slot_id's EP0, reading the 18-byte
* result into dev->device_descriptor.
* Does not wait for completion -- the
* result arrives asynchronously via
* xhci_poll_events()'s Transfer Event
* handling, which currently only logs
* success/failure (parsing the fields
* is the next increment).
* Sets dev->transfer_purpose so
* xhci_poll_events() knows how to
* interpret the completion.
*
* Called once Address Device succeeds -- not called directly by other
* code yet (Milestone 2f is still in progress).
* code.
*
* Returns 0 if the transfer was posted, -1 if dev/dev->ep0_ring is not
* set up.
*/
int xhci_ep0_get_device_descriptor(xhci_dev_t *dev, uint32_t slot_id);
/*
* xhci_ep0_get_config_descriptor — issue a GET_DESCRIPTOR (Configuration)
* control transfer for `length` bytes,
* reading into dev->config_descriptor
* (capped to its fixed size). Used
* twice per device: once for a short
* 9-byte read (just the Configuration
* descriptor header, to learn
* wTotalLength) and once for the full
* read once that length is known --
* xhci_poll_events() chains the second
* call automatically on the first
* read's success.
*
* Called once the device descriptor read succeeds -- not called directly
* by other code.
*
* Returns 0 if the transfer was posted, -1 if dev/dev->ep0_ring is not
* set up.
*/
int xhci_ep0_get_config_descriptor(xhci_dev_t *dev, uint32_t slot_id, uint16_t length);
#endif /* STARKERNEL_XHCI_DRIVER_H */
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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+176 -37
View File
@@ -129,6 +129,7 @@ int xhci_cmd_enable_slot(xhci_dev_t *dev);
int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
uint32_t port_id, uint32_t speed);
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_bringup(xhci_dev_t *dev)
{
@@ -289,6 +290,11 @@ int xhci_bringup(xhci_dev_t *dev)
dev->ep0_ring_cycle = 1;
dev->ep0_ring_enq = 0;
dev->pending_transfer_slot_id = 0;
dev->transfer_purpose = XHCI_XFER_NONE;
dev->config_total_length = 0;
dev->next_action = XHCI_NEXT_ACTION_NONE;
dev->next_action_slot_id = 0;
dev->next_action_length = 0;
console_println("xhci: controller running");
/* Milestone 2e prep: HCCPARAMS1.CSZ decides 32- vs 64-byte Slot/
@@ -476,21 +482,25 @@ static void xhci_ep0_enqueue_trb(xhci_dev_t *dev, uint64_t parameter,
}
}
int xhci_ep0_get_device_descriptor(xhci_dev_t *dev, uint32_t slot_id)
/* Shared submission for any "device-to-host, standard, device recipient,
* IN data stage" control read -- both GET_DESCRIPTOR(Device) and
* GET_DESCRIPTOR(Configuration) are this same shape, differing only in
* wValue/wLength/destination buffer. Does not set dev->transfer_purpose
* or dev->pending_transfer_slot_id -- callers do that themselves so the
* purpose is set before the doorbell rings (avoids a window where a
* stray Transfer Event could be misread against a not-yet-set purpose,
* even though this driver is polled and that window can't actually be
* hit by anything external in practice). */
static void xhci_ep0_control_read(xhci_dev_t *dev, uint8_t bRequest,
uint16_t wValue, uint16_t wIndex,
uint8_t *buf, uint16_t len)
{
if (!dev || !dev->ep0_ring) return -1;
/* Standard GET_DESCRIPTOR(Device) request (USB 2.0 spec section
* 9.4.3): device-to-host, standard, device recipient; wValue high
* byte selects descriptor type, low byte the index (0 for the one
* Device descriptor); wLength 18 = the full standard Device
* descriptor size. */
usb_setup_packet_t setup = {
.bmRequestType = USB_DIR_DEVICE_TO_HOST,
.bRequest = USB_REQ_GET_DESCRIPTOR,
.wValue = (uint16_t)(USB_DESC_TYPE_DEVICE << 8),
.wIndex = 0,
.wLength = sizeof(dev->device_descriptor)
.bRequest = bRequest,
.wValue = wValue,
.wIndex = wIndex,
.wLength = len
};
uint64_t setup_bits;
memcpy(&setup_bits, &setup, sizeof(setup_bits));
@@ -503,10 +513,9 @@ int xhci_ep0_get_device_descriptor(xhci_dev_t *dev, uint32_t slot_id)
(XHCI_SETUP_TRT_IN_DATA << XHCI_TRB_CONTROL_TRT_SHIFT));
/* Data Stage: parameter is a real pointer here (not immediate) --
* points at the reused device_descriptor buffer. DIR=IN matches the
* Setup Stage's TRT. */
xhci_ep0_enqueue_trb(dev, (uint64_t)(uintptr_t)dev->device_descriptor,
(uint32_t)sizeof(dev->device_descriptor),
* points at the caller's buffer. DIR=IN matches the Setup Stage's
* TRT. */
xhci_ep0_enqueue_trb(dev, (uint64_t)(uintptr_t)buf, len,
(XHCI_TRB_TYPE_DATA_STAGE << XHCI_TRB_CONTROL_TYPE_SHIFT) |
XHCI_TRB_CONTROL_DIR_IN);
@@ -517,6 +526,16 @@ int xhci_ep0_get_device_descriptor(xhci_dev_t *dev, uint32_t slot_id)
xhci_ep0_enqueue_trb(dev, 0, 0,
(XHCI_TRB_TYPE_STATUS_STAGE << XHCI_TRB_CONTROL_TYPE_SHIFT) |
XHCI_TRB_CONTROL_IOC);
}
int xhci_ep0_get_device_descriptor(xhci_dev_t *dev, uint32_t slot_id)
{
if (!dev || !dev->ep0_ring) return -1;
dev->transfer_purpose = XHCI_XFER_DEVICE_DESC;
xhci_ep0_control_read(dev, USB_REQ_GET_DESCRIPTOR,
(uint16_t)(USB_DESC_TYPE_DEVICE << 8), 0,
dev->device_descriptor, sizeof(dev->device_descriptor));
dev->pending_transfer_slot_id = slot_id;
/* Doorbell Array is indexed by slot ID; target 1 = Default Control
@@ -528,6 +547,31 @@ int xhci_ep0_get_device_descriptor(xhci_dev_t *dev, uint32_t slot_id)
return 0;
}
int xhci_ep0_get_config_descriptor(xhci_dev_t *dev, uint32_t slot_id, uint16_t length)
{
if (!dev || !dev->ep0_ring) return -1;
/* Cap to the fixed buffer size -- a device whose real Configuration
* descriptor set exceeds this would be truncated, not overflowed;
* 128 bytes comfortably covers a single-interface Mass Storage
* device (Config 9 + Interface 9 + 2 Endpoints * 7 = 32 bytes
* typical), so this is a defensive cap, not an expected path. */
if (length > sizeof(dev->config_descriptor)) {
length = (uint16_t)sizeof(dev->config_descriptor);
}
dev->transfer_purpose = (length <= 9) ? XHCI_XFER_CONFIG_DESC_SHORT
: XHCI_XFER_CONFIG_DESC_FULL;
xhci_ep0_control_read(dev, USB_REQ_GET_DESCRIPTOR,
(uint16_t)(USB_DESC_TYPE_CONFIG << 8), 0,
dev->config_descriptor, length);
dev->pending_transfer_slot_id = slot_id;
dev->doorbell[slot_id] = XHCI_DB_TARGET(1);
console_println("xhci: get config descriptor submitted");
return 0;
}
/* -------------------------------------------------------------------------
* Milestone 2d: Event Ring servicing, polled from sk_repl_idle().
*
@@ -659,10 +703,11 @@ void xhci_poll_events(void)
console_println("xhci: address device succeeded");
/* Milestone 2f: enumeration starts here -- the
* device now has a USB address and EP0 is
* usable for control transfers. */
if (xhci_ep0_get_device_descriptor(dev, dev->pending_connect_slot_id) != 0) {
console_println("xhci: device descriptor request setup failed");
}
* usable for control transfers. Deferred (see
* xhci_dev_t's doc comment on next_action) rather
* than called directly here. */
dev->next_action = XHCI_NEXT_ACTION_GET_DEVICE_DESC;
dev->next_action_slot_id = dev->pending_connect_slot_id;
} else {
console_println("xhci: address device failed");
}
@@ -676,24 +721,100 @@ void xhci_poll_events(void)
case XHCI_TRB_TYPE_TRANSFER_EVENT: {
uint32_t code = XHCI_EVT_COMPLETION_CODE(trb->status);
if (dev->pending_transfer_slot_id != 0) {
uint32_t xfer_slot_id = dev->pending_transfer_slot_id;
uint32_t purpose = dev->transfer_purpose;
dev->pending_transfer_slot_id = 0;
if (code == XHCI_COMPLETION_CODE_SUCCESS) {
console_println("xhci: device descriptor received");
/* USB 2.0 spec table 9-8 layout. Logged, not yet
* acted on -- 2f's own punch list asks whether
* vendor/product IDs are even needed for this
* project, or class-only detection suffices;
* this surfaces the real values to help decide,
* doesn't decide it here. */
uint32_t id_vendor = dev->device_descriptor[8] |
((uint32_t)dev->device_descriptor[9] << 8);
uint32_t id_product = dev->device_descriptor[10] |
((uint32_t)dev->device_descriptor[11] << 8);
xhci_log_hex32("xhci: idVendor=", id_vendor);
xhci_log_hex32("xhci: idProduct=", id_product);
xhci_log_hex32("xhci: bDeviceClass=", dev->device_descriptor[4]);
} else {
console_println("xhci: device descriptor request failed");
dev->transfer_purpose = XHCI_XFER_NONE;
if (code != XHCI_COMPLETION_CODE_SUCCESS) {
console_println("xhci: control transfer failed");
break;
}
switch (purpose) {
case XHCI_XFER_DEVICE_DESC: {
console_println("xhci: device descriptor received");
/* USB 2.0 spec table 9-8 layout. Logged --
* 2f's own punch list asked whether vendor/
* product IDs are even needed, or class-only
* detection suffices; this surfaces the real
* values, doesn't decide it. */
uint32_t id_vendor = dev->device_descriptor[8] |
((uint32_t)dev->device_descriptor[9] << 8);
uint32_t id_product = dev->device_descriptor[10] |
((uint32_t)dev->device_descriptor[11] << 8);
xhci_log_hex32("xhci: idVendor=", id_vendor);
xhci_log_hex32("xhci: idProduct=", id_product);
xhci_log_hex32("xhci: bDeviceClass=", dev->device_descriptor[4]);
/* Chain: request just the Configuration
* descriptor's 9-byte header first, to learn
* wTotalLength before requesting everything.
* Deferred (see xhci_dev_t's doc comment on
* next_action) rather than called directly --
* a doorbell rung synchronously here, still
* inside this event-processing loop and
* before ERDP is updated, hung the guest
* outright (confirmed live via checkpoint
* logging, amd64 QEMU, 2026-08-22). */
dev->next_action = XHCI_NEXT_ACTION_GET_CONFIG_DESC;
dev->next_action_slot_id = xfer_slot_id;
dev->next_action_length = 9;
break;
}
case XHCI_XFER_CONFIG_DESC_SHORT: {
uint16_t total_len = (uint16_t)(dev->config_descriptor[USB_CONFIG_OFF_TOTAL_LENGTH] |
((uint16_t)dev->config_descriptor[USB_CONFIG_OFF_TOTAL_LENGTH + 1] << 8));
dev->config_total_length = total_len;
xhci_log_hex32("xhci: config wTotalLength=", total_len);
dev->next_action = XHCI_NEXT_ACTION_GET_CONFIG_DESC;
dev->next_action_slot_id = xfer_slot_id;
dev->next_action_length = total_len;
break;
}
case XHCI_XFER_CONFIG_DESC_FULL: {
console_println("xhci: full config descriptor received");
/* Walk the concatenated descriptor stream
* (Config + Interface + Endpoint descriptors
* back to back) looking for the Interface
* descriptor -- its fixed offset within the
* stream isn't guaranteed, has to be found by
* bDescriptorType, not assumed. */
uint16_t len = dev->config_total_length;
if (len > sizeof(dev->config_descriptor)) len = (uint16_t)sizeof(dev->config_descriptor);
uint16_t off = 0;
int found = 0;
while (off + 2 <= len) {
uint8_t desc_len = dev->config_descriptor[off + USB_DESC_OFF_LENGTH];
uint8_t desc_type = dev->config_descriptor[off + USB_DESC_OFF_TYPE];
if (desc_len == 0) break; /* malformed -- avoid an infinite loop */
if (desc_type == USB_DESC_TYPE_INTERFACE &&
off + USB_IFACE_OFF_PROTOCOL < len) {
uint8_t iface_class = dev->config_descriptor[off + USB_IFACE_OFF_CLASS];
uint8_t iface_subclass = dev->config_descriptor[off + USB_IFACE_OFF_SUBCLASS];
uint8_t iface_protocol = dev->config_descriptor[off + USB_IFACE_OFF_PROTOCOL];
xhci_log_hex32("xhci: bInterfaceClass=", iface_class);
xhci_log_hex32("xhci: bInterfaceSubClass=", iface_subclass);
xhci_log_hex32("xhci: bInterfaceProtocol=", iface_protocol);
if (iface_class == USB_CLASS_MASS_STORAGE &&
iface_subclass == USB_SUBCLASS_SCSI &&
iface_protocol == USB_PROTOCOL_BOT) {
console_println("xhci: confirmed Mass Storage / SCSI / BOT device");
} else {
console_println("xhci: not a Mass Storage/SCSI/BOT device -- not usable as a drive");
}
found = 1;
break;
}
off = (uint16_t)(off + desc_len);
}
if (!found) {
console_println("xhci: no Interface descriptor found in config set");
}
break;
}
default:
console_println("xhci: transfer event");
break;
}
} else {
console_println("xhci: transfer event");
@@ -723,4 +844,22 @@ void xhci_poll_events(void)
* have nothing to clear. */
dev->intr0->erdp = ((uint64_t)(uintptr_t)&dev->evt_ring[dev->evt_ring_deq]
& XHCI_ERDP_PTR_MASK) | XHCI_ERDP_EHB;
/* Deferred chained request, if event processing above set one --
* see xhci_dev_t's own doc comment on why this must happen here,
* after ERDP is updated, not synchronously inside the loop above. */
if (dev->next_action == XHCI_NEXT_ACTION_GET_DEVICE_DESC) {
uint32_t next_slot_id = dev->next_action_slot_id;
dev->next_action = XHCI_NEXT_ACTION_NONE;
if (xhci_ep0_get_device_descriptor(dev, next_slot_id) != 0) {
console_println("xhci: deferred device descriptor request setup failed");
}
} else if (dev->next_action == XHCI_NEXT_ACTION_GET_CONFIG_DESC) {
uint32_t next_slot_id = dev->next_action_slot_id;
uint16_t next_length = dev->next_action_length;
dev->next_action = XHCI_NEXT_ACTION_NONE;
if (xhci_ep0_get_config_descriptor(dev, next_slot_id, next_length) != 0) {
console_println("xhci: deferred config descriptor request setup failed");
}
}
}