Artemis Milestone 2f: EP0 control transfer, device descriptor request

Adds Setup/Data/Status stage TRB types and control bits (IDT, TRT, DIR)
to xhci.h, and xhci_ep0_enqueue_trb()/xhci_ep0_get_device_descriptor() to
xhci.c -- the first real control transfer this driver has issued.
Follows the same enqueue-then-doorbell-once pattern as the Command Ring,
operating on the EP0 Transfer Ring built during 2e's Address Device work.
Setup Stage uses Immediate Data (parameter IS the 8-byte setup packet);
Data Stage reads into a reused 18-byte device_descriptor buffer; Status
Stage alone carries IOC, so exactly one Transfer Event signals transfer
completion, correlated via a new pending_transfer_slot_id (same
single-outstanding-operation pattern as connect/Enable Slot/Address
Device).

Automatically triggered once Address Device succeeds. Verified live via
QMP hotplug, all three architectures, worked first try with identical
results everywhere: idVendor=0x46f4, idProduct=0x0001, bDeviceClass=0x00
-- the class=0 confirms Mass Storage class detection needs the
Configuration/Interface descriptor (2f's next item), not the device
descriptor.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HZ8kNoTuP63pbQtro4qvrm
This commit is contained in:
Robert Allan James
2026-08-22 12:59:49 -04:00
co-authored by Claude Sonnet 5
parent 2e7e957680
commit 2c34e45d05
13 changed files with 36397 additions and 6 deletions
+114 -3
View File
@@ -17,6 +17,7 @@
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "starkernel/pci.h"
#include "starkernel/xhci.h"
@@ -127,6 +128,7 @@ static xhci_dev_t *g_xhci_dev = NULL;
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_bringup(xhci_dev_t *dev)
{
@@ -286,6 +288,7 @@ int xhci_bringup(xhci_dev_t *dev)
dev->ep0_ring = NULL;
dev->ep0_ring_cycle = 1;
dev->ep0_ring_enq = 0;
dev->pending_transfer_slot_id = 0;
console_println("xhci: controller running");
/* Milestone 2e prep: HCCPARAMS1.CSZ decides 32- vs 64-byte Slot/
@@ -446,6 +449,85 @@ int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
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
* xHCI control transfer is submitted (the controller processes queued
* TRBs as a unit once notified, not one doorbell ring per TRB). Same
* fixed-ring-plus-Link-TRB wraparound pattern as xhci_submit_command(),
* operating on ep0_ring/ep0_ring_enq/ep0_ring_cycle instead of the
* Command Ring's fields. */
static void xhci_ep0_enqueue_trb(xhci_dev_t *dev, uint64_t parameter,
uint32_t status, uint32_t control_bits)
{
xhci_trb_t *trb = &dev->ep0_ring[dev->ep0_ring_enq];
trb->parameter = parameter;
trb->status = status;
trb->control = control_bits | (dev->ep0_ring_cycle ? XHCI_TRB_CONTROL_CYCLE : 0);
dev->ep0_ring_enq++;
if (dev->ep0_ring_enq == XHCI_RING_TRB_COUNT - 1) {
dev->ep0_ring[XHCI_RING_TRB_COUNT - 1].control =
(XHCI_TRB_TYPE_LINK << XHCI_TRB_CONTROL_TYPE_SHIFT) |
XHCI_TRB_CONTROL_TC |
(dev->ep0_ring_cycle ? XHCI_TRB_CONTROL_CYCLE : 0);
dev->ep0_ring_enq = 0;
dev->ep0_ring_cycle ^= 1u;
}
}
int xhci_ep0_get_device_descriptor(xhci_dev_t *dev, uint32_t slot_id)
{
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)
};
uint64_t setup_bits;
memcpy(&setup_bits, &setup, sizeof(setup_bits));
/* Setup Stage: IDT set (parameter IS the 8-byte packet, not a
* pointer), TRT = IN Data Stage since this request reads data back. */
xhci_ep0_enqueue_trb(dev, setup_bits, 8u,
(XHCI_TRB_TYPE_SETUP_STAGE << XHCI_TRB_CONTROL_TYPE_SHIFT) |
XHCI_TRB_CONTROL_IDT |
(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),
(XHCI_TRB_TYPE_DATA_STAGE << XHCI_TRB_CONTROL_TYPE_SHIFT) |
XHCI_TRB_CONTROL_DIR_IN);
/* Status Stage: DIR=OUT (opposite of the Data Stage's IN) -- the
* status handshake always runs the reverse direction. IOC set here
* only: this is the sole TRB of the three whose completion signals
* "the whole control transfer is done" to xhci_poll_events(). */
xhci_ep0_enqueue_trb(dev, 0, 0,
(XHCI_TRB_TYPE_STATUS_STAGE << XHCI_TRB_CONTROL_TYPE_SHIFT) |
XHCI_TRB_CONTROL_IOC);
dev->pending_transfer_slot_id = slot_id;
/* Doorbell Array is indexed by slot ID; target 1 = Default Control
* Endpoint (EP0)'s Device Context Index, per xHCI 1.2 spec table
* 6-25 -- distinct from doorbell[0], which is always the Command
* Ring regardless of slot. */
dev->doorbell[slot_id] = XHCI_DB_TARGET(1);
console_println("xhci: get device descriptor submitted");
return 0;
}
/* -------------------------------------------------------------------------
* Milestone 2d: Event Ring servicing, polled from sk_repl_idle().
*
@@ -575,6 +657,12 @@ void xhci_poll_events(void)
} else if (dev->connect_state == XHCI_CONN_AWAIT_ADDRESS_DEVICE) {
if (code == XHCI_COMPLETION_CODE_SUCCESS) {
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");
}
} else {
console_println("xhci: address device failed");
}
@@ -585,10 +673,33 @@ void xhci_poll_events(void)
}
break;
}
case XHCI_TRB_TYPE_TRANSFER_EVENT:
/* No transfer rings exist yet (Milestone 2g) -- logged. */
console_println("xhci: transfer event");
case XHCI_TRB_TYPE_TRANSFER_EVENT: {
uint32_t code = XHCI_EVT_COMPLETION_CODE(trb->status);
if (dev->pending_transfer_slot_id != 0) {
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");
}
} else {
console_println("xhci: transfer event");
}
break;
}
default:
break;
}