299 lines
8.9 KiB
D
299 lines
8.9 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;
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
struct PortRegisters
|
|
{
|
|
uint portsc;
|
|
uint portpmsc;
|
|
uint portli;
|
|
uint porthlpmc;
|
|
}
|
|
static assert(PortRegisters.sizeof == 0x10u);
|
|
|
|
struct RuntimeRegisters
|
|
{
|
|
uint mfindex;
|
|
uint[7] _reserved01;
|
|
uint ir;
|
|
}
|
|
|
|
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);
|
|
/* TODO: confirm if 0x400 offset is from m_operation_registers or base_address. */
|
|
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);
|
|
}
|
|
}
|
|
}
|