Files
MontaukOS/kernel/src/Drivers/USB/Xhci.hpp
T

334 lines
14 KiB
C++

/*
* Xhci.hpp
* xHCI (USB 3.x) Host Controller driver
* Copyright (c) 2025 Daniel Hammer
*/
#pragma once
#include <cstdint>
#include <Pci/Pci.hpp>
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();
};