/* * 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 #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; /* Milestone 2f: EP0 control transfers. Like connect_state, this * driver only ever has one control transfer outstanding at a time -- * pending_transfer_slot_id is 0 when idle, else the slot ID whose * Transfer Event (posted only by the Status Stage TRB, which alone * has IOC set) is still outstanding. device_descriptor is the * (reused, not per-slot) buffer control transfers read into; 18 * bytes is the full standard USB device descriptor size. */ uint32_t pending_transfer_slot_id; uint8_t device_descriptor[18]; } 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); /* * xhci_ep0_get_device_descriptor — issue a standard GET_DESCRIPTOR * (Device) control transfer (Setup + * Data-IN + Status-OUT stages) on * slot_id's EP0, reading the 18-byte * result into dev->device_descriptor. * Does not wait for completion -- the * result arrives asynchronously via * xhci_poll_events()'s Transfer Event * handling, which currently only logs * success/failure (parsing the fields * is the next increment). * * Called once Address Device succeeds -- not called directly by other * code yet (Milestone 2f is still in progress). * * Returns 0 if the transfer was posted, -1 if dev/dev->ep0_ring is not * set up. */ int xhci_ep0_get_device_descriptor(xhci_dev_t *dev, uint32_t slot_id); #endif /* STARKERNEL_XHCI_DRIVER_H */