405 lines
12 KiB
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);
|
|
}
|
|
}
|
|
}
|