Implements Event Ring TRB parsing and ERDP dequeue-pointer update
(xhci_poll_events(), src/starkernel/usb/xhci.c), called from
sk_repl_idle()'s existing ~1s idle cadence rather than a per-arch
interrupt handler.
A first attempt wired real interrupt delivery (PCI->IOAPIC GSI routing,
a dedicated isr_stub34/vector 0x22, GIC/PLIC routing mirroring
virtio_input.c). Checked live via QMP query-pci before trusting it: the
amd64 PIRQ swizzle formula predicted GSI 16 for the xHCI controller at
PCI slot 4; the real QEMU-assigned IRQ was 10, and embedded ICH9
functions contradicted the same formula too. Reverted all of it back to
the exact committed baseline rather than chasing chipset PIRQ routing
further, and reframed around Section U item 6's own design intent
("interrupt-driven, coarse cadence, cheap early-exit... quick check
blocks... done") via sk_repl_idle() instead -- USB insertion is a
human-timescale event, not a hot path.
Added -device qemu-xhci to all three QEMU launch targets (required for
any of this to be testable). Verified end to end via genuine post-boot
hotplug (QMP device_add/device_del usb-storage): all three architectures
detect a live attach within seconds. A false-alarm heartbeat "freeze"
found mid-verification traced to querying the wrong counter
(vm->heartbeat.tick_count, which only advances during word execution,
not the kernel's real ISR-driven heartbeat_ticks()) -- confirmed via a
temporary diagnostic word, captured and reverted.
Full writeup, including the discarded interrupt-routing attempt and the
false-alarm investigation, in FABRIC-2.md's Milestone 2c/2d entries.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HZ8kNoTuP63pbQtro4qvrm
103 lines
4.6 KiB
C
103 lines
4.6 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 */
|
|
uint32_t cmd_ring_cycle; /* current Command Ring Cycle State (RCS) */
|
|
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() */
|
|
} 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, Command Completion;
|
|
* other types logged and skipped -- Milestone 2e/2g
|
|
* consume them), then advance the Event Ring dequeue
|
|
* pointer and clear ERDP.EHB.
|
|
*
|
|
* 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);
|
|
|
|
#endif /* STARKERNEL_XHCI_DRIVER_H */
|