/** * 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); } } }