Adds Slot/Endpoint/Input Control Context structs (32-byte layout only -- HCCPARAMS1.CSZ checked live and confirmed 0 against this driver's QEMU target; 64-byte contexts refuse rather than silently mis-laying-out), xhci_cmd_address_device(), and a new dev->connect_state (idle/await-enable-slot/await-address-device) sequencing Enable Slot and Address Device per connect. Input Context (what the command TRB's parameter points at) and Device Context (what DCBAA[slot_id] points at) are separate 64-byte-aligned allocations, lazily created once and reused across every connect -- single-device driver scope, no free path needed. A new EP0 Transfer Ring uses the same fixed-ring-plus-Link-TRB pattern as the Command Ring. Two facts checked live before writing any context code, not assumed: HCCPARAMS1.CSZ (32-byte, confirmed) and PORTSC.PED at connect time (already set -- PORTSC=0x00021203, SuperSpeed -- the test device self-enables via USB3 link training, so no port-reset state machine was needed this increment; USB2 would need one, untested). Both diagnostics also added console_puts/println-based hex logging (xhci_log_hex32()) -- console_println() only takes string literals, no formatted print existed on this driver's console path before now. Verified live via QMP hotplug, all three architectures, succeeded on the first attempt with no debugging needed: "enable slot succeeded" -> "address device command submitted" -> "address device succeeded" on every boot. Also fixes a FABRIC-2.md dependency-direction error from the previous commit (Address Device is 2f's prerequisite, not the reverse). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HZ8kNoTuP63pbQtro4qvrm
185 lines
9.0 KiB
C
185 lines
9.0 KiB
C
/*
|
|
* xhci_driver.h — xHCI USB host controller driver public API for StarKernel
|
|
*
|
|
* Register-layout definitions live in xhci.h; this header is the driver's
|
|
* own state and public entry points, matching virtio_blk.h's split.
|
|
*/
|
|
|
|
#ifndef STARKERNEL_XHCI_DRIVER_H
|
|
#define STARKERNEL_XHCI_DRIVER_H
|
|
|
|
#include <stdint.h>
|
|
#include "starkernel/pci.h"
|
|
#include "starkernel/xhci.h"
|
|
|
|
/* Driver state for one xHCI controller instance. Only one controller is
|
|
* supported (matches virtio_blk's single-device precedent). */
|
|
typedef struct {
|
|
PciDevice pci;
|
|
uint64_t bar0_phys; /* physical MMIO base, BAR0 */
|
|
xhci_cap_regs_t *cap; /* BAR0 + 0 */
|
|
xhci_op_regs_t *op; /* BAR0 + cap->cap_length */
|
|
xhci_runtime_regs_t *runtime; /* BAR0 + cap->rts_off */
|
|
xhci_doorbell_t *doorbell; /* BAR0 + cap->db_off */
|
|
uint32_t max_slots;
|
|
uint32_t max_ports;
|
|
uint32_t max_intrs;
|
|
uint32_t max_scratchpad_bufs;
|
|
|
|
/* Set up by xhci_bringup(); NULL/0 until then. */
|
|
void *dcbaa; /* Device Context Base Address Array */
|
|
void *scratchpad_arr; /* array of scratchpad buffer pointers, if any */
|
|
xhci_trb_t *cmd_ring; /* Command Ring, XHCI_RING_TRB_COUNT TRBs;
|
|
* index XHCI_RING_TRB_COUNT-1 is a
|
|
* permanent Link TRB back to index 0 */
|
|
uint32_t cmd_ring_cycle; /* current Command Ring Cycle State (RCS) */
|
|
uint32_t cmd_ring_enq; /* next free Command Ring index (0..COUNT-2) */
|
|
xhci_trb_t *evt_ring; /* Event Ring, XHCI_RING_TRB_COUNT TRBs */
|
|
void *evt_ring_seg_table; /* Event Ring Segment Table (1 entry) */
|
|
uint32_t evt_ring_cycle; /* current Event Ring Cycle State */
|
|
uint32_t evt_ring_deq; /* current Event Ring dequeue index */
|
|
xhci_intr_regs_t *intr0; /* Interrupter 0 register set, cached
|
|
* by xhci_bringup() for
|
|
* xhci_poll_events() */
|
|
|
|
/* Milestone 2e: connect -> Enable Slot correlation. port_slot_id is
|
|
* indexed by port_id - 1 (1-based port IDs, matching PORTSC/Port
|
|
* Status Change Event numbering); 0 means no slot allocated for that
|
|
* port yet. Fixed-size, not heap-allocated -- XHCI_MAX_TRACKED_PORTS
|
|
* comfortably covers any real or emulated root hub's port count
|
|
* without adding a new kmalloc_aligned() call to xhci_bringup(); ports
|
|
* beyond this bound (checked against both this array and max_ports)
|
|
* are simply not tracked, matching this driver's existing preference
|
|
* for fixed allocations over dynamic growth (xhci.h's own ring-sizing
|
|
* rationale). Only one Enable Slot is ever in flight at a time (this
|
|
* driver issues commands synchronously with respect to connect events,
|
|
* not a queue) -- pending_connect_port_id is 0 when idle, or the
|
|
* port_id whose Command Completion Event is still outstanding. */
|
|
uint32_t port_slot_id[XHCI_MAX_TRACKED_PORTS];
|
|
uint32_t pending_connect_port_id;
|
|
uint32_t pending_connect_speed; /* PORTSC.Port Speed at connect time */
|
|
|
|
/* Milestone 2e: Address Device. This driver only ever addresses one
|
|
* device at a time (single-drive-at-a-time scope), so these are
|
|
* single, reused allocations rather than per-slot -- lazily allocated
|
|
* on the first connect that reaches xhci_cmd_address_device(), then
|
|
* reinitialised (not reallocated) on every subsequent connect. connect
|
|
* state tracks which command a still-outstanding completion event
|
|
* belongs to, since Enable Slot and Address Device are issued
|
|
* sequentially, not concurrently, for a given connect. */
|
|
enum {
|
|
XHCI_CONN_IDLE = 0,
|
|
XHCI_CONN_AWAIT_ENABLE_SLOT,
|
|
XHCI_CONN_AWAIT_ADDRESS_DEVICE
|
|
} connect_state;
|
|
uint32_t pending_connect_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 */
|
|
uint32_t ep0_ring_cycle;
|
|
uint32_t ep0_ring_enq;
|
|
} xhci_dev_t;
|
|
|
|
/*
|
|
* xhci_find_and_map — locate the xHCI controller on PCI bus 0, enable it
|
|
* (I/O+MEM+bus-master), map its BAR0 MMIO region, and
|
|
* fill in the four register-region pointers in *dev.
|
|
*
|
|
* dev must point to a zero-initialised xhci_dev_t.
|
|
*
|
|
* Returns 0 on success.
|
|
* Returns -1 if no xHCI device was found on the PCI bus.
|
|
* Returns -2 if the BAR0 mapping failed.
|
|
*/
|
|
int xhci_find_and_map(xhci_dev_t *dev);
|
|
|
|
/*
|
|
* xhci_bringup — reset the controller, allocate and program the DCBAA,
|
|
* Command Ring, and Event Ring (Interrupter 0), then start
|
|
* the controller (RUN/STOP=1) and confirm it left the
|
|
* halted state.
|
|
*
|
|
* Must be called after a successful xhci_find_and_map(). Does not enable
|
|
* interrupts (USBCMD.INTE / IMAN.IE) -- this driver is polled, not
|
|
* interrupt-driven (see xhci_poll_events()'s own doc comment for why).
|
|
*
|
|
* Returns 0 on success.
|
|
* Returns -1 on reset timeout.
|
|
* Returns -2 on allocation failure.
|
|
* Returns -3 if the controller failed to leave the halted state after RUN.
|
|
* On success, latches dev into the module-static pointer xhci_poll_events()
|
|
* reads -- only one controller is supported, matching virtio_blk's
|
|
* single-device precedent.
|
|
*/
|
|
int xhci_bringup(xhci_dev_t *dev);
|
|
|
|
/*
|
|
* xhci_poll_events — read Interrupter 0's Event Ring, dispatching each TRB
|
|
* by type: Port Status Change reads PORTSC to log
|
|
* connect/disconnect and acknowledges CSC; Command
|
|
* Completion and Transfer Event are logged only (slot
|
|
* allocation and BOT transfers are later increments).
|
|
* Advances the Event Ring dequeue pointer and clears
|
|
* ERDP.EHB when done.
|
|
*
|
|
* Polled, not interrupt-driven: an initial attempt at IRQ delivery
|
|
* (Milestone 2d's first draft) found the amd64 PCI INTx routing formula
|
|
* gives a demonstrably wrong GSI (checked live via QMP query-pci: xHCI at
|
|
* PCI slot 4 reports IRQ 10, the formula predicted 16), and the
|
|
* aarch64/riscv64 slot/pin-derived source IDs were unverified at the new
|
|
* slot this controller occupies. Rather than guess further at chipset
|
|
* PIRQ routing, this matches Section U item 6's own design intent
|
|
* (Captain Bob: "interrupt-driven, coarse cadence, cheap early-exit...
|
|
* quick check blocks... done") via sk_repl_idle()'s existing coarse-cadence
|
|
* hook instead of a per-arch IRQ path -- USB insertion is a human-timescale
|
|
* event, not a hot path, so polling costs nothing meaningful here.
|
|
*
|
|
* No arguments and no return value -- only one xHCI controller is
|
|
* supported, so the caller needs no device handle. A no-op if
|
|
* xhci_bringup() has not completed successfully (dev pointer not yet
|
|
* latched).
|
|
*/
|
|
void xhci_poll_events(void);
|
|
|
|
/*
|
|
* xhci_cmd_enable_slot — submit an Enable Slot command TRB to the Command
|
|
* Ring and ring doorbell 0. Does not wait for or
|
|
* read the resulting Command Completion Event -- it
|
|
* arrives asynchronously via xhci_poll_events(),
|
|
* which correlates the returned Slot ID back to
|
|
* dev->pending_connect_port_id and records it in
|
|
* dev->port_slot_id[].
|
|
*
|
|
* Called from xhci_poll_events()'s own Port Status Change handling on a
|
|
* real connect event -- not called directly by other code.
|
|
*
|
|
* Returns 0 if the command was posted, -1 if dev/dev->cmd_ring is not set
|
|
* up (xhci_bringup() has not completed).
|
|
*/
|
|
int xhci_cmd_enable_slot(xhci_dev_t *dev);
|
|
|
|
/*
|
|
* xhci_cmd_address_device — build the Input Context (Slot + EP0, add-only),
|
|
* program DCBAA[slot_id] with the Device Context,
|
|
* allocate the EP0 Transfer Ring, and submit an
|
|
* Address Device command TRB.
|
|
*
|
|
* speed is the PORTSC.Port Speed value read live at the connect this call
|
|
* is servicing (xHCI 1.2 spec table 7-13 speed IDs) -- used to pick EP0's
|
|
* default Max Packet Size before any device descriptor has been read.
|
|
*
|
|
* Refuses (-2) if HCCPARAMS1.CSZ indicates 64-byte contexts -- only
|
|
* 32-byte contexts are implemented (see xhci.h's own doc comment on
|
|
* xhci_slot_ctx32_t).
|
|
*
|
|
* Called from xhci_poll_events()'s Command Completion handling once Enable
|
|
* Slot succeeds -- not called directly by other code.
|
|
*
|
|
* Returns 0 if the command was posted, -1 on allocation failure, -2 if
|
|
* 64-byte contexts are required.
|
|
*/
|
|
int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
|
|
uint32_t port_id, uint32_t speed);
|
|
|
|
#endif /* STARKERNEL_XHCI_DRIVER_H */
|