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
+74
-6
@@ -3211,9 +3211,10 @@ Final three-arch acceptance (probe-free, `clean qemu`): all three boot to `ok>`
|
||||
the device (Address Device command) — **both done 2026-08-22**, see writeup below.
|
||||
Device descriptor read is 2f's job and depends on this (not the reverse — an earlier
|
||||
version of this note had the dependency backwards)
|
||||
- [ ] On disconnect: tear down the corresponding device slot and signal to whatever higher-
|
||||
level code (Section U's identity/VM logic) that the device is gone — **this is the
|
||||
first point where Section U/V's design actually gets a real trigger to hang off**
|
||||
- [x] On disconnect: tear down the corresponding device slot — **done 2026-08-25**, see
|
||||
writeup below. Signaling to Section U's higher-level code is still open — a real
|
||||
callback surface doesn't exist yet, this item only covers the xHCI-side teardown
|
||||
(Disable Slot command, DCBAA entry clear)
|
||||
- [ ] Decide and implement where the hotplug event surfaces to the rest of the kernel —
|
||||
likely a callback registered by whatever owns the home-blocks logic, not xHCI code
|
||||
calling into block_subsystem.c directly (matching the existing "kernel/Artemis
|
||||
@@ -3319,8 +3320,43 @@ no debugging needed. `logs/20260822-113852/amd64/`, `logs/20260822-114043/aarch6
|
||||
|
||||
Still open: correlating Command Completion Events via the real Command TRB Pointer (not needed
|
||||
yet, see above), device descriptor read (2f), USB2 port-reset handling (untested — this
|
||||
increment's test device was SuperSpeed and self-enabled), disconnect teardown (Disable Slot
|
||||
command, DCBAA entry clear), and the callback surface into Section U's higher-level code.
|
||||
increment's test device was SuperSpeed and self-enabled), and the callback surface into
|
||||
Section U's higher-level code.
|
||||
|
||||
**Disconnect teardown (Disable Slot command, DCBAA entry clear), done 2026-08-25.** New
|
||||
`XHCI_CONN_AWAIT_DISABLE_SLOT` state and `dev->pending_disable_slot_id`, same
|
||||
single-outstanding-command pattern as Enable Slot/Address Device above. On a real disconnect
|
||||
(`PORTSC.CCS` clear), `port_slot_id[port_id-1]` is captured into
|
||||
`pending_disable_slot_id` and cleared from the tracking array *immediately* — before the
|
||||
Disable Slot command's completion is known — so a fresh connect on the same port during the
|
||||
brief in-flight window isn't confused for one already in progress; the command's own
|
||||
completion handler is the only thing that later touches `DCBAA[slot_id]`, zeroing it on
|
||||
success only (a failure completion leaves it alone rather than zeroing a context pointer the
|
||||
controller might still consider live). If a disconnect arrives while another Command Ring
|
||||
command is already outstanding, the Disable Slot is dropped rather than queued — same
|
||||
documented limitation as Enable Slot's "already pending" case, not a new gap.
|
||||
|
||||
Verified live via QMP hotplug, all three architectures — a real connect→full-enumeration→
|
||||
disconnect cycle exercised the new teardown path directly, not a synthetic disconnect: `xhci:
|
||||
port status change -- device connected` through `xhci: device configured` (full 2f sequence)
|
||||
followed immediately by `xhci: port status change -- device disconnected`, `xhci: disable slot
|
||||
command submitted`, `xhci: disable slot succeeded`, no wedge. `logs/20260825-081058/amd64/`,
|
||||
`logs/20260825-081300/aarch64/`, `logs/20260825-081517/riscv64/`.
|
||||
|
||||
**A documentation near-miss caught before commit, worth recording.** The first draft of this
|
||||
writeup cited the probe-free three-arch acceptance boots (`logs/20260825-075759/amd64/` etc.)
|
||||
as the verification evidence for this feature — those boots never had a USB device attached at
|
||||
all (`grep xhci` on them shows only the four controller-bring-up lines, nothing past that), so
|
||||
the claim was false. The mixup came from an earlier monitoring mistake in-session: a
|
||||
leftover, still-running QEMU process from before a session crash left a *stale* log directory
|
||||
on disk, and a `ls -dt | head -1` glob picked that stale directory instead of the freshly
|
||||
launched run's — the stale log happened to already contain a full connect/enumerate/disconnect
|
||||
sequence from whatever testing produced it originally, which read as this session's own result
|
||||
until checked against the actual PID and the actual new log path. Caught by re-grepping the
|
||||
three acceptance logs directly before writing this paragraph and finding them empty of device
|
||||
activity; fixed by re-running the verification as a real, deliberate QMP hotplug (not relying
|
||||
on whatever happened to be attached at boot) against freshly launched, individually-tracked
|
||||
instances.
|
||||
|
||||
**2f. USB device enumeration (post-connect, before it's usable as storage)**
|
||||
- [x] Request and parse the device descriptor — **done 2026-08-22**, see writeup below.
|
||||
@@ -3415,7 +3451,11 @@ arch afterward, no wedge. `logs/20260825-073235/amd64/`, `logs/20260825-073417/a
|
||||
`logs/20260825-073728/riscv64/`.
|
||||
|
||||
**2g. Bulk-Only Transport (BOT) — the actual read/write path**
|
||||
- [ ] Identify and configure the device's bulk IN and bulk OUT endpoints
|
||||
- [~] Identify the device's bulk IN and bulk OUT endpoints — **identification done
|
||||
2026-08-25**, see writeup below; *configuring* them (a Configure Endpoint command,
|
||||
wiring their EP Contexts/DCIs so the controller will actually run transfers on them)
|
||||
is still open — this increment only reads the addresses out of the descriptor, it
|
||||
doesn't yet act on them
|
||||
- [ ] Implement CBW (Command Block Wrapper) construction and send, for a SCSI READ(10)
|
||||
- [ ] Implement CSW (Command Status Wrapper) receive and status check
|
||||
- [ ] Get one real SCSI READ(10) working end to end — first proof the whole stack works,
|
||||
@@ -3425,6 +3465,34 @@ arch afterward, no wedge. `logs/20260825-073235/amd64/`, `logs/20260825-073417/a
|
||||
- [ ] Implement basic error/stall recovery (CSW failure status, endpoint stall clear) — at
|
||||
minimum enough to not wedge the controller on a single bad transfer
|
||||
|
||||
**Bulk endpoint identification, done 2026-08-25.** Extends the Interface-descriptor walk
|
||||
already built in 2f: once a Mass Storage/SCSI/BOT interface is confirmed, a second, nested
|
||||
walk continues forward from that interface's own offset through the Endpoint descriptors that
|
||||
follow it (`bDescriptorType == 5`, USB 2.0 spec table 9-13), stopping at the next Interface
|
||||
descriptor or end of the concatenated descriptor stream — matching the same "found by type,
|
||||
not assumed at a fixed offset" discipline the outer walk already uses. Only endpoints whose
|
||||
`bmAttributes` bits 1:0 read Bulk (`0x02`) are kept; `bEndpointAddress` bit 7 picks IN vs. OUT.
|
||||
Four new `xhci_dev_t` fields (`bulk_in_ep_addr`/`bulk_in_max_packet`/`bulk_out_ep_addr`/
|
||||
`bulk_out_max_packet`) hold the result — address `0` doubles as the "not found yet" sentinel
|
||||
for both, since endpoint address 0 is always EP0 and can never legitimately be a bulk
|
||||
endpoint's own address. A device confirmed BOT but missing either direction logs a warning
|
||||
rather than failing silently.
|
||||
|
||||
This work was already scaffolded — the four struct fields, the descriptor-offset macros, and
|
||||
their doc comments were all written and uncommitted before an earlier session ended abruptly
|
||||
mid-edit — but the walk itself, the piece that actually populates them, hadn't been written
|
||||
yet; this increment is that missing piece (plus resetting the four fields in
|
||||
`xhci_bringup()`, which the scaffolding had also missed).
|
||||
|
||||
Verified live via QMP hotplug (deliberate `device_add`/`device_del` against a freshly booted,
|
||||
individually-tracked instance — see the disconnect-teardown writeup above for why this
|
||||
matters), all three architectures, byte-identical results everywhere: `bulk IN
|
||||
endpoint=0x81`, `bulk OUT endpoint=0x02`, immediately followed by the existing `set
|
||||
configuration submitted` → `device configured` chain, unaffected. `logs/20260825-081058/amd64/`,
|
||||
`logs/20260825-081300/aarch64/`, `logs/20260825-081517/riscv64/`. *Configuring* these
|
||||
endpoints (a Configure Endpoint command, EP Context setup) is the next open item in this
|
||||
milestone — this increment only identifies them.
|
||||
|
||||
**2h. Integration with the existing block subsystem**
|
||||
- [ ] Wire a working USB MSC device into `blk_subsys_attach_device()` (or
|
||||
`blk_subsys_add_raw_device()`, whichever fits — confirm which, since USB is
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-25T11:37:26Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-25T12:03:09Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
Binary file not shown.
@@ -192,6 +192,7 @@ typedef struct {
|
||||
/* TRB types used by this driver (subset — xHCI defines many more) */
|
||||
#define XHCI_TRB_TYPE_LINK 6 /* ring-wraparound marker, Command/Transfer Rings only */
|
||||
#define XHCI_TRB_TYPE_ENABLE_SLOT_CMD 9
|
||||
#define XHCI_TRB_TYPE_DISABLE_SLOT_CMD 10
|
||||
#define XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD 11
|
||||
#define XHCI_TRB_TYPE_SETUP_STAGE 2 /* Transfer Ring, control transfers only */
|
||||
#define XHCI_TRB_TYPE_DATA_STAGE 3
|
||||
@@ -260,6 +261,19 @@ typedef struct {
|
||||
#define USB_SUBCLASS_SCSI 0x06u /* SCSI transparent command set */
|
||||
#define USB_PROTOCOL_BOT 0x50u /* Bulk-Only Transport */
|
||||
|
||||
/* Standard USB Endpoint descriptor field offsets (7 bytes, USB 2.0 spec
|
||||
* table 9-13) -- found the same way as the Interface descriptor: walked
|
||||
* by bDescriptorType within the concatenated stream, not a fixed offset,
|
||||
* since the number of endpoints on the interface isn't known up front. */
|
||||
#define USB_DESC_TYPE_ENDPOINT 5u
|
||||
#define USB_EP_OFF_ADDRESS 2u /* bEndpointAddress: bit 7 = direction, bits 3:0 = number */
|
||||
#define USB_EP_OFF_ATTRIBUTES 3u /* bmAttributes: bits 1:0 = transfer type */
|
||||
#define USB_EP_OFF_MAX_PACKET_SIZE 4u /* wMaxPacketSize, 2 bytes */
|
||||
#define USB_EP_ADDR_DIR_MASK 0x80u
|
||||
#define USB_EP_ADDR_NUM_MASK 0x0Fu
|
||||
#define USB_EP_ATTR_TYPE_MASK 0x03u
|
||||
#define USB_EP_TYPE_BULK 0x02u
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -70,9 +70,21 @@ typedef struct {
|
||||
enum {
|
||||
XHCI_CONN_IDLE = 0,
|
||||
XHCI_CONN_AWAIT_ENABLE_SLOT,
|
||||
XHCI_CONN_AWAIT_ADDRESS_DEVICE
|
||||
XHCI_CONN_AWAIT_ADDRESS_DEVICE,
|
||||
XHCI_CONN_AWAIT_DISABLE_SLOT
|
||||
} connect_state;
|
||||
uint32_t pending_connect_slot_id;
|
||||
/* Milestone 2e/2g: disconnect teardown. Same single-outstanding-
|
||||
* command assumption as Enable Slot/Address Device above -- a
|
||||
* disconnect that arrives while another Command Ring command is
|
||||
* already outstanding is dropped rather than queued (matches the
|
||||
* existing "enable slot already pending -- dropped" precedent).
|
||||
* pending_disable_slot_id is captured at disconnect time, since the
|
||||
* port's own tracked slot ID (port_slot_id[]) is cleared immediately
|
||||
* on disconnect so a fresh connect on the same port isn't confused
|
||||
* for one already in progress -- by the time the Disable Slot
|
||||
* command's completion arrives, the port array no longer has it. */
|
||||
uint32_t pending_disable_slot_id;
|
||||
void *input_ctx; /* Input Control Ctx + Slot Ctx + EP0 Ctx (96 bytes, 32-byte contexts) */
|
||||
void *device_ctx; /* Slot Ctx + EP0 Ctx (64 bytes) -- DCBAA[slot_id] points here */
|
||||
xhci_trb_t *ep0_ring; /* EP0 Transfer Ring, XHCI_RING_TRB_COUNT TRBs */
|
||||
@@ -107,6 +119,19 @@ typedef struct {
|
||||
uint8_t config_descriptor[128];
|
||||
uint16_t config_total_length;
|
||||
|
||||
/* Milestone 2g: bulk endpoints, discovered by walking the Endpoint
|
||||
* descriptors that follow the confirmed Mass Storage/BOT Interface
|
||||
* descriptor in config_descriptor. bEndpointAddress in full (not just
|
||||
* the endpoint number) -- bit 7 is needed later to pick the right
|
||||
* Doorbell target / EP Context DCI, and callers that want direction
|
||||
* alone can just mask it. 0 means "not found yet" for both --
|
||||
* endpoint address 0 is always EP0 (control), never a valid bulk
|
||||
* endpoint address, so it's a safe not-found sentinel. */
|
||||
uint8_t bulk_in_ep_addr;
|
||||
uint16_t bulk_in_max_packet;
|
||||
uint8_t bulk_out_ep_addr;
|
||||
uint16_t bulk_out_max_packet;
|
||||
|
||||
/* 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
|
||||
@@ -209,6 +234,22 @@ void xhci_poll_events(void);
|
||||
*/
|
||||
int xhci_cmd_enable_slot(xhci_dev_t *dev);
|
||||
|
||||
/*
|
||||
* xhci_cmd_disable_slot — submit a Disable Slot command TRB for slot_id
|
||||
* and ring doorbell 0. Does not wait for or read
|
||||
* the resulting Command Completion Event -- it
|
||||
* arrives asynchronously via xhci_poll_events(),
|
||||
* which clears DCBAA[slot_id] on success.
|
||||
*
|
||||
* Called from xhci_poll_events()'s own Port Status Change handling on a
|
||||
* real disconnect event, for a slot that was actually addressed -- not
|
||||
* called directly by other code.
|
||||
*
|
||||
* Returns 0 if the command was posted, -1 if dev/dev->cmd_ring is not set
|
||||
* up.
|
||||
*/
|
||||
int xhci_cmd_disable_slot(xhci_dev_t *dev, uint32_t slot_id);
|
||||
|
||||
/*
|
||||
* xhci_cmd_address_device — build the Input Context (Slot + EP0, add-only),
|
||||
* program DCBAA[slot_id] with the Device Context,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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