Artemis Milestone 2g (partial): bulk endpoint discovery + 2e disconnect teardown
Picked up from a crashed session: xhci_driver.h/xhci.h already had the bulk_in/out_ep_addr/max_packet fields and Endpoint-descriptor offset macros scaffolded, but the actual walk that populates them was never written. Added it: after 2f confirms a Mass Storage/SCSI/BOT interface, a nested walk continues through the Endpoint descriptors that follow it (bDescriptorType==5, stopping at the next Interface descriptor or end of stream), keeping only Bulk-type endpoints and splitting IN/OUT by bEndpointAddress bit 7. Also reset the four new fields in xhci_bringup(), which the scaffolding had missed. Also completed 2e's disconnect teardown, which was fully implemented this session (not scaffolded): a Disable Slot command is now submitted on a real disconnect, with the port's tracked slot ID captured and cleared from port_slot_id[] immediately (before the command completes) so a fresh connect on the same port isn't confused for one already in progress, and DCBAA[slot_id] cleared only on a successful completion. Verified live via QMP hotplug (deliberate device_add/device_del against freshly launched, individually-tracked instances -- not whatever happened to be attached at boot), all three architectures, byte-identical: bulk IN endpoint=0x81, bulk OUT endpoint=0x02, then a clean disconnect -> disable slot succeeded, no wedge. Caught and fixed a documentation near-miss in the same pass: an initial draft cited the probe-free three-arch acceptance boots as this feature's verification evidence, but a stale leftover log directory from a pre-crash orphaned QEMU process had been picked up by an `ls -dt | head -1` glob during monitoring and mistaken for this session's own result -- the real acceptance logs never had a device attached at all. Re-verified against real PIDs and real log paths before writing FABRIC-2.md's final writeup. FABRIC-2.md Section X Milestone 2 updated: 2e's disconnect-teardown checklist item marked done, 2g's endpoint-identification item marked partially done (identification only -- Configure Endpoint / EP Context wiring is still open). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R4VMX6VSKCten8nGgaMkq4
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
b4bbd043d0
commit
96d55fcd87
@@ -126,6 +126,7 @@ static xhci_dev_t *g_xhci_dev = NULL;
|
||||
* (see the "Command Ring submission" section) so it can stay close to
|
||||
* xhci_poll_events(), the read side of the same ring pair. */
|
||||
int xhci_cmd_enable_slot(xhci_dev_t *dev);
|
||||
int xhci_cmd_disable_slot(xhci_dev_t *dev, uint32_t slot_id);
|
||||
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);
|
||||
@@ -285,6 +286,7 @@ int xhci_bringup(xhci_dev_t *dev)
|
||||
dev->pending_connect_speed = 0;
|
||||
dev->connect_state = XHCI_CONN_IDLE;
|
||||
dev->pending_connect_slot_id = 0;
|
||||
dev->pending_disable_slot_id = 0;
|
||||
dev->input_ctx = NULL;
|
||||
dev->device_ctx = NULL;
|
||||
dev->ep0_ring = NULL;
|
||||
@@ -293,6 +295,10 @@ int xhci_bringup(xhci_dev_t *dev)
|
||||
dev->pending_transfer_slot_id = 0;
|
||||
dev->transfer_purpose = XHCI_XFER_NONE;
|
||||
dev->config_total_length = 0;
|
||||
dev->bulk_in_ep_addr = 0;
|
||||
dev->bulk_in_max_packet = 0;
|
||||
dev->bulk_out_ep_addr = 0;
|
||||
dev->bulk_out_max_packet = 0;
|
||||
dev->next_action = XHCI_NEXT_ACTION_NONE;
|
||||
dev->next_action_slot_id = 0;
|
||||
dev->next_action_length = 0;
|
||||
@@ -358,6 +364,16 @@ int xhci_cmd_enable_slot(xhci_dev_t *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xhci_cmd_disable_slot(xhci_dev_t *dev, uint32_t slot_id)
|
||||
{
|
||||
if (!dev || !dev->cmd_ring) return -1;
|
||||
/* Slot ID goes in control[31:24], same field Address Device uses --
|
||||
* no parameter/status payload needed, this command just names a slot. */
|
||||
xhci_submit_command(dev, 0, 0, XHCI_TRB_TYPE_DISABLE_SLOT_CMD, slot_id << 24);
|
||||
console_println("xhci: disable slot command submitted");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Default EP0 Max Packet Size by PORTSC.Port Speed, used before any device
|
||||
* descriptor has been read (xHCI 1.2 spec's own recommended defaults --
|
||||
* the real value comes from bMaxPacketSize0 once 2f reads the device
|
||||
@@ -697,12 +713,27 @@ void xhci_poll_events(void)
|
||||
console_println("xhci: port status change -- device disconnected");
|
||||
if (port_id >= 1 && port_id <= XHCI_MAX_TRACKED_PORTS &&
|
||||
dev->port_slot_id[port_id - 1] != 0) {
|
||||
/* Real teardown (Disable Slot command, DCBAA entry
|
||||
* clear, callback to Section U's code) is a later
|
||||
* increment -- for now just stop tracking the slot
|
||||
* so a future connect on this port isn't confused
|
||||
* for one already in progress. */
|
||||
uint32_t disconnecting_slot_id = dev->port_slot_id[port_id - 1];
|
||||
/* Stop tracking the slot immediately so a future
|
||||
* connect on this port isn't confused for one
|
||||
* already in progress -- independent of whether
|
||||
* the Disable Slot command below can be issued
|
||||
* right now. */
|
||||
dev->port_slot_id[port_id - 1] = 0;
|
||||
if (dev->connect_state == XHCI_CONN_IDLE) {
|
||||
dev->pending_disable_slot_id = disconnecting_slot_id;
|
||||
dev->connect_state = XHCI_CONN_AWAIT_DISABLE_SLOT;
|
||||
xhci_cmd_disable_slot(dev, disconnecting_slot_id);
|
||||
} else {
|
||||
/* Same single-outstanding-command limitation
|
||||
* as Enable Slot above -- the slot's DCBAA
|
||||
* entry is simply left stale (harmless: it is
|
||||
* never looked at again since port_slot_id[]
|
||||
* no longer references it, and a genuinely
|
||||
* concurrent connect/disconnect pair isn't
|
||||
* this driver's current scope). */
|
||||
console_println("xhci: disable slot skipped -- command ring busy");
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Acknowledge only CSC (RW1CS): preserve PP, write 0 for
|
||||
@@ -758,6 +789,22 @@ void xhci_poll_events(void)
|
||||
}
|
||||
dev->connect_state = XHCI_CONN_IDLE;
|
||||
dev->pending_connect_port_id = 0;
|
||||
} else if (dev->connect_state == XHCI_CONN_AWAIT_DISABLE_SLOT) {
|
||||
if (code == XHCI_COMPLETION_CODE_SUCCESS) {
|
||||
/* DCBAA[slot_id] cleared on success only -- if the
|
||||
* controller reports failure, leave it: the slot
|
||||
* may still be in a state where zeroing its
|
||||
* context pointer out from under the controller
|
||||
* is unsafe, and port_slot_id[] no longer
|
||||
* references this slot either way, so nothing
|
||||
* else in this driver will look at it again. */
|
||||
((uint64_t *)dev->dcbaa)[dev->pending_disable_slot_id] = 0;
|
||||
console_println("xhci: disable slot succeeded");
|
||||
} else {
|
||||
console_println("xhci: disable slot failed");
|
||||
}
|
||||
dev->connect_state = XHCI_CONN_IDLE;
|
||||
dev->pending_disable_slot_id = 0;
|
||||
} else {
|
||||
console_println("xhci: command completion event");
|
||||
}
|
||||
@@ -844,6 +891,42 @@ void xhci_poll_events(void)
|
||||
iface_subclass == USB_SUBCLASS_SCSI &&
|
||||
iface_protocol == USB_PROTOCOL_BOT) {
|
||||
console_println("xhci: confirmed Mass Storage / SCSI / BOT device");
|
||||
/* Walk the Endpoint descriptors that
|
||||
* follow this Interface descriptor,
|
||||
* stopping at the next Interface
|
||||
* descriptor (start of a different
|
||||
* interface's endpoints) or end of
|
||||
* the stream. Only bulk endpoints
|
||||
* are of interest for BOT. */
|
||||
uint16_t ep_off = (uint16_t)(off + desc_len);
|
||||
while (ep_off + 2 <= len) {
|
||||
uint8_t ep_desc_len = dev->config_descriptor[ep_off + USB_DESC_OFF_LENGTH];
|
||||
uint8_t ep_desc_type = dev->config_descriptor[ep_off + USB_DESC_OFF_TYPE];
|
||||
if (ep_desc_len == 0) break; /* malformed -- avoid an infinite loop */
|
||||
if (ep_desc_type == USB_DESC_TYPE_INTERFACE) break;
|
||||
if (ep_desc_type == USB_DESC_TYPE_ENDPOINT &&
|
||||
ep_off + USB_EP_OFF_MAX_PACKET_SIZE + 1 < len) {
|
||||
uint8_t ep_addr = dev->config_descriptor[ep_off + USB_EP_OFF_ADDRESS];
|
||||
uint8_t ep_attr = dev->config_descriptor[ep_off + USB_EP_OFF_ATTRIBUTES];
|
||||
uint16_t ep_max_packet = (uint16_t)(dev->config_descriptor[ep_off + USB_EP_OFF_MAX_PACKET_SIZE] |
|
||||
((uint16_t)dev->config_descriptor[ep_off + USB_EP_OFF_MAX_PACKET_SIZE + 1] << 8));
|
||||
if ((ep_attr & USB_EP_ATTR_TYPE_MASK) == USB_EP_TYPE_BULK) {
|
||||
if (ep_addr & USB_EP_ADDR_DIR_MASK) {
|
||||
dev->bulk_in_ep_addr = ep_addr;
|
||||
dev->bulk_in_max_packet = ep_max_packet;
|
||||
xhci_log_hex32("xhci: bulk IN endpoint=", ep_addr);
|
||||
} else {
|
||||
dev->bulk_out_ep_addr = ep_addr;
|
||||
dev->bulk_out_max_packet = ep_max_packet;
|
||||
xhci_log_hex32("xhci: bulk OUT endpoint=", ep_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
ep_off = (uint16_t)(ep_off + ep_desc_len);
|
||||
}
|
||||
if (dev->bulk_in_ep_addr == 0 || dev->bulk_out_ep_addr == 0) {
|
||||
console_println("xhci: warning -- BOT device missing a bulk IN or OUT endpoint");
|
||||
}
|
||||
/* Deferred (see xhci_dev_t's
|
||||
* next_action doc comment) rather
|
||||
* than called directly here --
|
||||
|
||||
Reference in New Issue
Block a user