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:
co-authored by
Claude Sonnet 5
parent
2e7e957680
commit
2c34e45d05
+114
-3
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user