Artemis Milestone 2f: SET_CONFIGURATION -- 2f complete

Chains off a confirmed Mass Storage/SCSI/BOT interface match via the
existing next_action deferral mechanism: device descriptor -> config
descriptor -> SET_CONFIGURATION is now a single automatic sequence.
bConfigurationValue is read directly out of the already-fetched
config_descriptor buffer, no extra transfer needed.

First write control transfer this driver has issued (every prior one was
a read), so it needed its own submission helper,
xhci_ep0_control_write_nodata() -- SET_CONFIGURATION has no Data Stage
(wLength=0), and per USB 2.0 spec 8.5.3 a no-data control transfer's
Status Stage is always IN, the reverse of an OUT-data request's status
stage. XHCI_SETUP_TRT_NO_DATA already existed in xhci.h, unused until now.

Verified live via QMP hotplug, all three architectures, worked first try,
byte-identical: "set configuration submitted" -> "device configured",
guest stays running throughout (checked via QMP query-status). Disconnect
confirmed clean on every arch afterward, no wedge. FABRIC-2.md Section X
Milestone 2f updated -- 2f is now fully complete, 2g (Bulk-Only Transport)
can start.

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:40:16 -04:00
co-authored by Claude Sonnet 5
parent b9c540a78b
commit b4bbd043d0
12 changed files with 27309 additions and 9 deletions
+66
View File
@@ -130,6 +130,7 @@ 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_ep0_set_configuration(xhci_dev_t *dev, uint32_t slot_id, uint8_t config_value);
int xhci_bringup(xhci_dev_t *dev)
{
@@ -295,6 +296,7 @@ int xhci_bringup(xhci_dev_t *dev)
dev->next_action = XHCI_NEXT_ACTION_NONE;
dev->next_action_slot_id = 0;
dev->next_action_length = 0;
dev->next_action_config_value = 0;
console_println("xhci: controller running");
/* Milestone 2e prep: HCCPARAMS1.CSZ decides 32- vs 64-byte Slot/
@@ -572,6 +574,49 @@ int xhci_ep0_get_config_descriptor(xhci_dev_t *dev, uint32_t slot_id, uint16_t l
return 0;
}
/* Shared submission for a "no Data Stage" control transfer -- Setup Stage
* only (TRT = XHCI_SETUP_TRT_NO_DATA), then Status Stage. Per USB 2.0
* spec section 8.5.3, a control transfer with no Data Stage always uses
* an IN Status Stage (the reverse of a normal OUT request's OUT status),
* so DIR_IN is set unconditionally here -- this helper isn't generic
* across OUT-data and no-data requests, only the latter. */
static void xhci_ep0_control_write_nodata(xhci_dev_t *dev, uint8_t bRequest,
uint16_t wValue, uint16_t wIndex)
{
usb_setup_packet_t setup = {
.bmRequestType = USB_DIR_HOST_TO_DEVICE,
.bRequest = bRequest,
.wValue = wValue,
.wIndex = wIndex,
.wLength = 0
};
uint64_t setup_bits;
memcpy(&setup_bits, &setup, sizeof(setup_bits));
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_NO_DATA << XHCI_TRB_CONTROL_TRT_SHIFT));
xhci_ep0_enqueue_trb(dev, 0, 0,
(XHCI_TRB_TYPE_STATUS_STAGE << XHCI_TRB_CONTROL_TYPE_SHIFT) |
XHCI_TRB_CONTROL_IOC |
XHCI_TRB_CONTROL_DIR_IN);
}
int xhci_ep0_set_configuration(xhci_dev_t *dev, uint32_t slot_id, uint8_t config_value)
{
if (!dev || !dev->ep0_ring) return -1;
dev->transfer_purpose = XHCI_XFER_SET_CONFIG;
xhci_ep0_control_write_nodata(dev, USB_REQ_SET_CONFIGURATION, config_value, 0);
dev->pending_transfer_slot_id = slot_id;
dev->doorbell[slot_id] = XHCI_DB_TARGET(1);
console_println("xhci: set configuration submitted");
return 0;
}
/* -------------------------------------------------------------------------
* Milestone 2d: Event Ring servicing, polled from sk_repl_idle().
*
@@ -799,6 +844,16 @@ void xhci_poll_events(void)
iface_subclass == USB_SUBCLASS_SCSI &&
iface_protocol == USB_PROTOCOL_BOT) {
console_println("xhci: confirmed Mass Storage / SCSI / BOT device");
/* Deferred (see xhci_dev_t's
* next_action doc comment) rather
* than called directly here --
* same doorbell-ordering hazard
* as the device/config descriptor
* chaining above. */
dev->next_action = XHCI_NEXT_ACTION_SET_CONFIG;
dev->next_action_slot_id = xfer_slot_id;
dev->next_action_config_value =
dev->config_descriptor[USB_CONFIG_OFF_CONFIG_VALUE];
} else {
console_println("xhci: not a Mass Storage/SCSI/BOT device -- not usable as a drive");
}
@@ -812,6 +867,10 @@ void xhci_poll_events(void)
}
break;
}
case XHCI_XFER_SET_CONFIG: {
console_println("xhci: device configured");
break;
}
default:
console_println("xhci: transfer event");
break;
@@ -861,5 +920,12 @@ void xhci_poll_events(void)
if (xhci_ep0_get_config_descriptor(dev, next_slot_id, next_length) != 0) {
console_println("xhci: deferred config descriptor request setup failed");
}
} else if (dev->next_action == XHCI_NEXT_ACTION_SET_CONFIG) {
uint32_t next_slot_id = dev->next_action_slot_id;
uint8_t next_config_value = dev->next_action_config_value;
dev->next_action = XHCI_NEXT_ACTION_NONE;
if (xhci_ep0_set_configuration(dev, next_slot_id, next_config_value) != 0) {
console_println("xhci: deferred set configuration request setup failed");
}
}
}