/* * Xhci.hpp * xHCI (USB 3.x) Host Controller driver * Copyright (c) 2025 Daniel Hammer */ #pragma once #include #include namespace Drivers::USB::Xhci { // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- constexpr uint32_t MAX_SLOTS = 16; constexpr uint32_t MAX_PORTS = 16; constexpr uint32_t CMD_RING_SIZE = 64; constexpr uint32_t EVT_RING_SIZE = 64; constexpr uint32_t XFER_RING_SIZE = 32; // MSI configuration (E1000E uses IRQ 24/vector 56, we use 25/57) constexpr uint8_t MSI_IRQ = 25; constexpr uint32_t MSI_VECTOR = 57; constexpr uint32_t MSI_ADDR_BASE = 0xFEE00000; // PCI class/subclass/progif for xHCI constexpr uint8_t PCI_CLASS_SERIAL = 0x0C; constexpr uint8_t PCI_SUBCLASS_USB = 0x03; constexpr uint8_t PCI_PROGIF_XHCI = 0x30; // --------------------------------------------------------------------------- // xHCI Capability Register offsets (from BAR0) // --------------------------------------------------------------------------- constexpr uint32_t CAP_CAPLENGTH = 0x00; // 1 byte constexpr uint32_t CAP_HCIVERSION = 0x02; // 2 bytes constexpr uint32_t CAP_HCSPARAMS1 = 0x04; // 4 bytes constexpr uint32_t CAP_HCSPARAMS2 = 0x08; // 4 bytes constexpr uint32_t CAP_HCSPARAMS3 = 0x0C; // 4 bytes constexpr uint32_t CAP_HCCPARAMS1 = 0x10; // 4 bytes constexpr uint32_t CAP_DBOFF = 0x14; // 4 bytes constexpr uint32_t CAP_RTSOFF = 0x18; // 4 bytes // --------------------------------------------------------------------------- // xHCI Operational Register offsets (from BAR0 + CAPLENGTH) // --------------------------------------------------------------------------- constexpr uint32_t OP_USBCMD = 0x00; constexpr uint32_t OP_USBSTS = 0x04; constexpr uint32_t OP_PAGESIZE = 0x08; constexpr uint32_t OP_DNCTRL = 0x14; constexpr uint32_t OP_CRCR = 0x18; // 8 bytes constexpr uint32_t OP_DCBAAP = 0x30; // 8 bytes constexpr uint32_t OP_CONFIG = 0x38; constexpr uint32_t OP_PORTSC_BASE = 0x400; constexpr uint32_t OP_PORTSC_STRIDE = 0x10; // USBCMD bits constexpr uint32_t USBCMD_RS = (1 << 0); // Run/Stop constexpr uint32_t USBCMD_HCRST = (1 << 1); // Host Controller Reset constexpr uint32_t USBCMD_INTE = (1 << 2); // Interrupter Enable constexpr uint32_t USBCMD_HSEE = (1 << 3); // Host System Error Enable // USBSTS bits constexpr uint32_t USBSTS_HCH = (1 << 0); // HC Halted constexpr uint32_t USBSTS_HSE = (1 << 2); // Host System Error constexpr uint32_t USBSTS_EINT = (1 << 3); // Event Interrupt constexpr uint32_t USBSTS_PCD = (1 << 4); // Port Change Detect constexpr uint32_t USBSTS_CNR = (1 << 11); // Controller Not Ready // PORTSC bits constexpr uint32_t PORTSC_CCS = (1 << 0); // Current Connect Status constexpr uint32_t PORTSC_PED = (1 << 1); // Port Enabled/Disabled constexpr uint32_t PORTSC_PR = (1 << 4); // Port Reset constexpr uint32_t PORTSC_PLS_MASK = (0xF << 5); // Port Link State constexpr uint32_t PORTSC_PP = (1 << 9); // Port Power constexpr uint32_t PORTSC_SPEED_MASK = (0xF << 10); // Port Speed constexpr uint32_t PORTSC_PRC = (1 << 21); // Port Reset Change constexpr uint32_t PORTSC_CSC = (1 << 17); // Connect Status Change constexpr uint32_t PORTSC_PEC = (1 << 18); // Port Enabled/Disabled Change constexpr uint32_t PORTSC_WRC = (1 << 19); // Warm Port Reset Change constexpr uint32_t PORTSC_OCC = (1 << 20); // Over-current Change constexpr uint32_t PORTSC_PLC = (1 << 22); // Port Link State Change constexpr uint32_t PORTSC_CEC = (1 << 23); // Port Config Error Change // Write-1-to-clear change bits constexpr uint32_t PORTSC_CHANGE_BITS = PORTSC_CSC | PORTSC_PEC | PORTSC_WRC | PORTSC_OCC | PORTSC_PRC | PORTSC_PLC | PORTSC_CEC; // Bits that must be preserved when writing PORTSC (RW1S/RW1CS excluded) constexpr uint32_t PORTSC_PRESERVE = PORTSC_PP; // Port speed values (from PORTSC bits 13:10) constexpr uint32_t SPEED_FULL = 1; constexpr uint32_t SPEED_LOW = 2; constexpr uint32_t SPEED_HIGH = 3; constexpr uint32_t SPEED_SUPER = 4; // --------------------------------------------------------------------------- // Runtime Register offsets (from BAR0 + RTSOFF) // --------------------------------------------------------------------------- // Interrupter 0 registers constexpr uint32_t IR0_IMAN = 0x20; // Interrupter Management constexpr uint32_t IR0_IMOD = 0x24; // Interrupter Moderation constexpr uint32_t IR0_ERSTSZ = 0x28; // Event Ring Segment Table Size constexpr uint32_t IR0_ERSTBA = 0x30; // Event Ring Segment Table Base Address (8 bytes) constexpr uint32_t IR0_ERDP = 0x38; // Event Ring Dequeue Pointer (8 bytes) // IMAN bits constexpr uint32_t IMAN_IP = (1 << 0); // Interrupt Pending constexpr uint32_t IMAN_IE = (1 << 1); // Interrupt Enable constexpr uint32_t IMOD_INTERVAL_100US = 400; // 100 us in 250 ns units // --------------------------------------------------------------------------- // TRB (Transfer Request Block) - 16 bytes // --------------------------------------------------------------------------- struct TRB { uint32_t Parameter0; uint32_t Parameter1; uint32_t Status; uint32_t Control; } __attribute__((packed)); // TRB type field (bits 15:10 of Control) constexpr uint32_t TRB_TYPE_SHIFT = 10; constexpr uint32_t TRB_TYPE_MASK = (0x3F << TRB_TYPE_SHIFT); // TRB types constexpr uint32_t TRB_NORMAL = 1; constexpr uint32_t TRB_SETUP_STAGE = 2; constexpr uint32_t TRB_DATA_STAGE = 3; constexpr uint32_t TRB_STATUS_STAGE = 4; constexpr uint32_t TRB_LINK = 6; constexpr uint32_t TRB_ENABLE_SLOT = 9; constexpr uint32_t TRB_DISABLE_SLOT = 10; constexpr uint32_t TRB_ADDRESS_DEVICE = 11; constexpr uint32_t TRB_CONFIGURE_ENDPOINT = 12; constexpr uint32_t TRB_EVALUATE_CONTEXT = 13; constexpr uint32_t TRB_RESET_ENDPOINT = 14; constexpr uint32_t TRB_STOP_ENDPOINT = 15; constexpr uint32_t TRB_SET_TR_DEQUEUE = 16; constexpr uint32_t TRB_NOOP_CMD = 23; constexpr uint32_t TRB_TRANSFER_EVENT = 32; constexpr uint32_t TRB_COMMAND_COMPLETION = 33; constexpr uint32_t TRB_PORT_STATUS_CHANGE = 34; // TRB control field bits constexpr uint32_t TRB_CYCLE_BIT = (1 << 0); constexpr uint32_t TRB_IOC = (1 << 5); // Interrupt On Completion constexpr uint32_t TRB_IDT = (1 << 6); // Immediate Data constexpr uint32_t TRB_BSR = (1 << 9); // Block Set Address Request constexpr uint32_t TRB_DIR_IN = (1 << 16); // Direction: 1=IN (device to host) constexpr uint32_t TRB_TRT_IN = (3 << 16); // Transfer Type: IN constexpr uint32_t TRB_TRT_OUT = (2 << 16); // Transfer Type: OUT constexpr uint32_t TRB_TRT_NODATA = (0 << 16); // Transfer Type: No Data Stage constexpr uint32_t TRB_ISP = (1 << 2); // Interrupt on Short Packet constexpr uint32_t TRB_CHAIN = (1 << 4); // Chain bit constexpr uint32_t TRB_ENT = (1 << 1); // Evaluate Next TRB // Completion codes (from Status field bits 31:24) constexpr uint32_t CC_SUCCESS = 1; constexpr uint32_t CC_SHORT_PACKET = 13; // --------------------------------------------------------------------------- // Event Ring Segment Table Entry // --------------------------------------------------------------------------- struct ERSTEntry { uint64_t RingSegmentBase; uint32_t RingSegmentSize; uint32_t Reserved; } __attribute__((packed)); // --------------------------------------------------------------------------- // Device Context structures (xHCI spec section 6.2) // --------------------------------------------------------------------------- struct SlotContext { uint32_t Field0; // Route String, Speed, MTT, Hub, Context Entries uint32_t Field1; // Max Exit Latency, Root Hub Port Number, Num Ports uint32_t Field2; // TT Hub Slot ID, TT Port Number, Interrupter Target uint32_t Field3; // Device Address, Slot State uint32_t Reserved[4]; } __attribute__((packed)); struct EndpointContext { uint32_t Field0; // EP State, Mult, MaxPStreams, Interval, LSA uint32_t Field1; // CErr, EP Type, HID, Max Burst Size, Max Packet Size uint64_t TRDequeuePtr; // TR Dequeue Pointer (with DCS at bit 0) uint32_t Field2; // Average TRB Length, Max ESIT Payload Lo uint32_t Reserved[3]; } __attribute__((packed)); // Endpoint types (bits 5:3 of EP Field1) constexpr uint32_t EP_TYPE_ISOCH_OUT = 1; constexpr uint32_t EP_TYPE_BULK_OUT = 2; constexpr uint32_t EP_TYPE_INTERRUPT_OUT = 3; constexpr uint32_t EP_TYPE_CONTROL = 4; constexpr uint32_t EP_TYPE_ISOCH_IN = 5; constexpr uint32_t EP_TYPE_BULK_IN = 6; constexpr uint32_t EP_TYPE_INTERRUPT_IN = 7; struct InputControlContext { uint32_t DropFlags; uint32_t AddFlags; uint32_t Reserved[5]; uint8_t ConfigValue; uint8_t InterfaceNumber; uint8_t AlternateSetting; uint8_t Reserved2; } __attribute__((packed)); // Full InputContext: InputControlContext + SlotContext + 31 EndpointContexts // (but we only use EP0 + a few endpoints) struct InputContext { InputControlContext ICC; SlotContext Slot; EndpointContext EP[31]; } __attribute__((packed)); struct DeviceContext { SlotContext Slot; EndpointContext EP[31]; } __attribute__((packed)); // --------------------------------------------------------------------------- // Per-device tracking // --------------------------------------------------------------------------- struct UsbDeviceInfo { bool Active; uint8_t PortId; uint32_t Speed; uint16_t VendorId; uint16_t ProductId; uint8_t InterfaceClass; uint8_t InterfaceSubClass; uint8_t InterfaceProtocol; uint8_t DeviceClass; // bDeviceClass from device descriptor // Interrupt IN endpoint uint8_t InterruptEpNum; // Endpoint number (1-15) uint16_t InterruptMaxPacket; uint8_t InterruptInterval; // Transfer ring for Interrupt IN endpoint TRB* InterruptRing; uint64_t InterruptRingPhys; uint32_t InterruptRingEnqueue; bool InterruptRingCCS; // Current Cycle State // Bulk IN endpoint uint8_t BulkInEpNum; uint16_t BulkInMaxPacket; TRB* BulkInRing; uint64_t BulkInRingPhys; uint32_t BulkInRingEnqueue; bool BulkInRingCCS; // Bulk OUT endpoint uint8_t BulkOutEpNum; uint16_t BulkOutMaxPacket; TRB* BulkOutRing; uint64_t BulkOutRingPhys; uint32_t BulkOutRingEnqueue; bool BulkOutRingCCS; // EP0 transfer ring TRB* EP0Ring; uint64_t EP0RingPhys; uint32_t EP0RingEnqueue; bool EP0RingCCS; // Device context (output) DeviceContext* OutputContext; uint64_t OutputContextPhys; }; // --------------------------------------------------------------------------- // Transfer callback for non-HID class drivers (Bluetooth, etc.) // --------------------------------------------------------------------------- using TransferCallback = void (*)(uint8_t slotId, uint8_t epDci, const uint8_t* data, uint32_t length, uint32_t completionCode); // --------------------------------------------------------------------------- // Public API // --------------------------------------------------------------------------- void Initialize(); bool Probe(const Pci::PciDevice& dev); bool IsInitialized(); bool HasDeferredWork(); // Deferred hot-plug processing (call from timer tick, not interrupt context) void ProcessDeferredWork(); // Send a command on the command ring, wait for completion. // Returns completion code. uint32_t SendCommand(const TRB& trb); // Perform a control transfer on slot's EP0. // setup: 8 bytes of USB setup packet (packed into TRB params) // data: optional data buffer (virtual address), dataLen: length // dirIn: true = device-to-host // Returns completion code. uint32_t ControlTransfer(uint8_t slotId, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, void* data, bool dirIn); // Queue an interrupt IN transfer on a device's interrupt endpoint void QueueInterruptTransfer(uint8_t slotId); // Queue a bulk transfer on a device's bulk IN or OUT endpoint void QueueBulkInTransfer(uint8_t slotId, uint8_t* data, uint64_t dataPhys, uint32_t length); void QueueBulkOutTransfer(uint8_t slotId, uint8_t* data, uint64_t dataPhys, uint32_t length); // Register a transfer callback for a specific slot (used by non-HID class drivers) void RegisterTransferCallback(uint8_t slotId, TransferCallback cb); // Ring a doorbell void RingDoorbell(uint8_t slotId, uint8_t target); // Access device info UsbDeviceInfo* GetDevice(uint8_t slotId); // Poll event ring (called from interrupt handler or during init) void PollEvents(); };