hos/src/hulk/usb/xhci.d

405 lines
12 KiB
D

/**
* eXtensible Host Controller Interface for Universal Serial Bus
*
* Documentation:
* - https://wiki.osdev.org/EXtensible_Host_Controller_Interface
* - https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf
*/
module hulk.usb.xhci;
import hulk.pci;
import hulk.hurl.a1;
import hulk.klog;
struct XHCI
{
/**
* Located at PCI base address.
*
* All are read-only.
*/
struct CapabilityRegisters
{
/**
* Offset from beginning of capability registers to beginning of
* operational registers.
*/
ubyte capability_length;
ubyte _reserved01;
/**
* Interface version number supported (psuedo-BSD).
*/
ushort hci_version;
/**
* HCSPARAMS1.
* 7:0 number of device slots (MaxSlots)
* 18:8 number of interrupters (MaxIntrs)
* 23:19 reserved
* 31:24 number of ports (MaxPorts)
*/
uint hcs_params1;
/**
* 3:0 isochronous scheduling threshold (IST)
* 7:4 event ring segment table max (ERST Max)
* 20:8 reserved
* 25:21 max scratchpad buffers (high 5 bits; xHCI v1.1+)
* 26 scratchpad restore (SPR)
* 31:27 max scratchpad buffers (low 5 bits)
*/
uint hcs_params2;
/**
* 7:0 U1 device exit latency
* 15:8 reserved
* 31:16 U2 device exit latency
*/
uint hcs_params3;
/**
* 0 64-bit addressing capability (AC64)
* 1 BW negotiation capability (BNC)
* 2 context size (CSZ)
* 3 port power control (PPC)
* 4 port indicators (PIND)
* 5 light HC reset capability (LHRC)
* 6 latency tolerance messaging capability (LTC)
* 7 no secondary SID support (NSS)
* 8 parse all event data (PAE; xHCI v1.1+)
* 9 stopped - short packet capability (SPC; xHCI v1.1+)
* 10 stopped EDTLA capability (SEC; xHCI v1.1+)
* 11 continuous frame ID capability (CFC; xHCI v1.1+)
* 15:12 maximum primary stream array size (MaxPSASize)
* 31:16 xHCI extended capabilities pointer (xECP)
* offset in 32-bit words from base of capability registers to base
* of capabilities list
*/
uint hcc_params1;
/**
* 1:0 reserved
* 31:2 doorbell array offset in 32-bit words
* offset in 32-bit words from base of capability registers to the
* doorbell array
*/
uint doorbell_offset;
/**
* 4:0 reserved
* 31:5 runtime register space offset
*/
uint rts_offset;
/**
* 0 U3 entry capability (U3C)
* 1 ConfigEP command max exit latency too large (CMC)
* 2 force save context capability (FSC)
* 3 compliance transition capability (CTC)
* 4 large ESIT payload capability (LEC)
* 5 configuration information capability (CIC)
* 6 extended TBC capability (ETC)
* 7 extended TBC TRB status capability (ETC_TSC)
* 8 get/set extended property capability (GSC; xHCI v1.1+)
* 9 virtualization based trusted I/O capability (VTC; xHCI v1.2+)
* 31:10 reserved
*/
uint hcc_params2;
/**
* 11:0 reserved
* 31:12 VTIO register space offset (xHCI v1.2+)
*/
uint vtios_offset;
}
static assert(CapabilityRegisters.sizeof == 0x24u);
/**
* Located at offset capability_length from PCI base address.
*/
struct OperationalRegisters
{
/**
* 0 run/stop (RS)
* 1 host controller reset (HCRST)
* 2 interrupter enable (INTE)
* 3 host system error enable (HSEE)
* 6:4 reserved
* 7 light host controller reset (LHCRST)
* 8 controller save state (CSS)
* 9 controller restore state (CRS)
* 10 enable wrap event (EWE)
* 11 enable U3 MFINDEX stop (UE3S)
* 12 stopped short packet enable (SPE; xHCI v1.1+)
* 13 CEM enable (CME; xHCI v1.1+)
* 14 extended TBC enable (ETE; xHCI v1.2+)
* 15 extended TBC TRB status enable (TSC_EN; xHCI v1.2+)
* 16 VTIO enable (VTIOE; xHCI v1.2+)
* 31:17 reserved
*/
uint usb_command;
/**
* 0 HCHalted
* 1 reserved
* 2 Host System Error (HSE)
* 3 Event Interrupt (EINT)
* 4 Port Change Detected (PCD)
* 7:5 reserved
* 8 Save State Status (SSS)
* 9 Restore State Status (RSS)
* 10 Save/Restore Error (SRE)
* 11 Controller Not Ready (CNR)
* 12 Host Controller Error (HCE)
* 31:13 reserved
*/
uint usb_status;
/**
* 15:0 Page Size (actual page size is 2^(x+12))
* 31:16 reserved
*/
uint page_size;
uint[2] _reserved01;
/**
* 15:0 Notification Enable
* 31:16 reserved
*/
uint dn_ctrl;
/**
* 0 Ring Cycle State (RCS)
* 1 Command Stop (CS)
* 2 Command Abort (CA)
* 3 Command Ring Running (CRR)
* 5:4 reserved
* 63:6 Command Ring Pointer
*/
ulong cr_ctrl;
uint[4] _reserved02;
/**
* 5:0 reserved
* 63:6 Device Context Base Address Array Pointer
*/
ulong dcbaap;
/**
* 7:0 Max Device Slots Enabled (MaxSlotsEn)
* 8 U3 Entry Enable (U3E; xHCI v1.1+)
* 9 Configuration Information Enable (CIE; xHCI v1.1+)
* 31:10 reserved
*/
uint config;
uint _reserved03;
}
static assert(OperationalRegisters.sizeof == 0x40u);
/**
* Located at offset 0x400 from start of Operational Registers.
*
* There is one set of these registers for each port of the root hub.
*/
struct PortRegisters
{
/**
* Port Status and Control
*
* 0 Current Connect Status
* 1 Port Enabled/Disabled
* 2 reserved
* 3 Over-current active
* 4 Port Reset
* 8:5 Port Link State
* 9 Port Power
* 13:10 Port Speed
* 15:14 Port Indicator Control
* 16 Port Link State Write Strobe
* 17 Connect Status Change
* 18 Port Enable/Disable Change
* 19 Warm Port Reset Change
* 20 Over-current Change
* 21 Port Reset Change
* 22 Port Link State Change
* 23 Port Config Error Change
* 24 Cold Attach Status
* 25 Wake on Connect Enable
* 26 Wake on Disconnect Enable
* 27 Wake on Over-current Enable
* 29:28 reserved
* 30 Device Removable
* 31 Warm Port Reset
*/
uint portsc;
/**
* Port Power Management Status and Control
*/
uint portpmsc;
/**
* Port Link Info
*/
uint portli;
/**
* Port Hardware LPM Control (xHCI v1.1+)
*/
uint porthlpmc;
}
static assert(PortRegisters.sizeof == 0x10u);
/**
* Located at offset rts_offset from PCI base address.
*/
struct RuntimeRegisters
{
/**
* Microframe index. Incremented by controller each microframe.
*/
uint mfindex;
uint[7] _reserved01;
/**
* Interrupter register sets.
*/
InterrupterRegister[0] ir;
}
/**
* Interrupter register set.
*
* A copy of this register set is present for each interrupter.
*/
struct InterrupterRegister
{
/**
* Interrupter Management Register.
* 0 Interrupt Pending (IP)
* 1 Interrupt Enable
* 31:2 reserved
*/
uint imr;
/**
* Interrupter Moderation.
* 15:0 Interrupt Moderation Interval
* 31:16 Interrupt Moderation Counter
*/
uint im;
/**
* Event Ring Segment Table Size.
* 15:0 Event Ring Segment Table Size
* 31:16 reserved
*/
uint ersts;
uint reserved;
/**
* Event Ring Segment Table Base Address.
* 5:0 reserved
* 63:6 Event Ring Segment Table Base Address
*/
ulong erstba;
/**
* Event Ring Dequeue Pointer.
* 2:0 Dequeue ERST Segment Index
* 3 Event Handler Busy
* 63:4 Event Ring Dequeue Pointer
*/
ulong erdp;
}
/**
* Located at offset doorbell_offset from PCI base address.
*
* The number of these is given by HCSPARAMS1.MaxSlots + 1 (for the command
* ring).
*/
struct DoorbellRegister
{
ubyte target;
ubyte _reserved01;
ushort task_id;
}
static assert(DoorbellRegister.sizeof == 4u);
private CapabilityRegisters * m_capability_registers;
private OperationalRegisters * m_operational_registers;
private PortRegisters * m_port_registers;
private RuntimeRegisters * m_runtime_registers;
private DoorbellRegister * m_doorbell_registers;
void initialize(Pci.Device * pci_device)
{
void * base_address = pci_device.memory_ranges[0].address;
Klog.writefln("Found XHCI controller at address %p", base_address);
m_capability_registers = cast(CapabilityRegisters *)base_address;
m_operational_registers = cast(OperationalRegisters *)(base_address + m_capability_registers.capability_length);
m_port_registers = cast(PortRegisters *)(cast(void *)m_operational_registers + 0x400);
m_runtime_registers = cast(RuntimeRegisters *)(base_address + m_capability_registers.rts_offset);
m_doorbell_registers = cast(DoorbellRegister *)(base_address + m_capability_registers.doorbell_offset);
dump_extended_capabilities();
}
private void dump_extended_capabilities()
{
size_t ext_cap_off = (m_capability_registers.hcc_params1 >> 16u);
if (ext_cap_off != 0u)
{
Klog.writefln("Extended capabilities:");
uint * extended_capabilities = cast(uint *)m_capability_registers + ext_cap_off;
for (;;)
{
uint ec0 = extended_capabilities[0];
size_t next_offset = (ec0 >> 8u) & 0xFFu;
size_t len = next_offset;
if (len < 1u)
{
len = 1u;
}
ubyte type = ec0 & 0xFFu;
if (type == 2u)
{
len = 4u;
}
for (size_t i = 0u; i < len; i++)
{
uint ecv = extended_capabilities[i];
Klog.writef(" %02X %02X %02X %02X",
ecv & 0xFFu,
(ecv >> 8u) & 0xFFu,
(ecv >> 16u) & 0xFFu,
(ecv >> 24u) & 0xFFu);
}
Klog.writef("\n");
if (next_offset == 0u)
{
break;
}
extended_capabilities += next_offset;
}
}
}
static void build(Pci.Device * pci_device)
{
if (pci_device.memory_ranges[0].address != null)
{
XHCI * xhci = A1.allocate!XHCI();
xhci.initialize(pci_device);
}
}
}