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:
co-authored by
Claude Sonnet 5
parent
2c34e45d05
commit
b9c540a78b
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user