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