/*
* Copyright (c) 2024, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2025, Sönke Holz <sholz8530@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/StdLibExtras.h>
#include <Kernel/Bus/USB/USBController.h>
#include <Kernel/Bus/USB/xHCI/xHCIRootHub.h>
#include <Kernel/Interrupts/GenericInterruptHandler.h>
#include <Kernel/Memory/TypedMapping.h>
namespace Kernel::USB {
class xHCIPCIInterrupter;
class xHCIDeviceTreeInterrupter;
class xHCIController
: public USBController {
friend class xHCIPCIInterrupter;
friend class xHCIDeviceTreeInterrupter;
public:
virtual ~xHCIController() override;
virtual ErrorOr<void> initialize() override;
virtual ErrorOr<void> reset() override;
virtual ErrorOr<void> stop() override;
virtual ErrorOr<void> start() override;
virtual void cancel_async_transfer(NonnullLockRefPtr<Transfer> transfer) override;
virtual ErrorOr<size_t> submit_control_transfer(Transfer& transfer) override;
virtual ErrorOr<size_t> submit_bulk_transfer(Transfer& transfer) override;
virtual ErrorOr<void> submit_async_interrupt_transfer(NonnullLockRefPtr<Transfer> transfer, u16 ms_interval) override;
virtual ErrorOr<void> reset_pipe(USB::Device&, USB::Pipe&) override;
virtual ErrorOr<void> initialize_device(USB::Device&) override;
ErrorOr<void> initialize_endpoint_if_needed(Pipe const&);
u8 ports() const { return m_ports; }
ErrorOr<HubStatus> get_port_status(Badge<xHCIRootHub>, u8 port);
ErrorOr<void> set_port_feature(Badge<xHCIRootHub>, u8 port, HubFeatureSelector);
ErrorOr<void> clear_port_feature(Badge<xHCIRootHub>, u8 port, HubFeatureSelector);
protected:
xHCIController(Memory::TypedMapping<u8> registers_mapping);
virtual bool using_message_signalled_interrupts() const = 0;
virtual ErrorOr<OwnPtr<GenericInterruptHandler>> create_interrupter(u16 interrupter_id) = 0;
virtual ErrorOr<void> write_dmesgln_prefix(StringBuilder&) const = 0;
private:
void handle_interrupt(u16 interrupter_id);
void take_exclusive_control_from_bios();
ErrorOr<void> find_port_max_speeds();
void event_handling_thread();
void hot_plug_thread();
void poll_thread();
// Arbitrarily chosen to decrease allocation sizes, can be increased up to 256 if we reach this limit
static constexpr size_t max_devices = 64;
// 5.3 Host Controller Capability Registers
struct CapabilityRegisters {
u32 capability_register_length : 8; // CAPLENGTH
u32 reserved0 : 8; // Rsvd
u32 host_controller_interface_version_number : 16; // HCIVERSION
struct {
// HCSPARAMS1
u32 number_of_device_slots : 8; // MaxSlots
u32 number_of_interrupters : 11; // MaxIntrs
u32 reserved0 : 5; // Rsvd
u32 number_of_ports : 8; // MaxPorts
// HCSPARAMS2
u32 isochronous_scheduling_threshold : 4; // IST
u32 event_ring_segment_table_max : 4; // ERST Max
u32 reserved1 : 13; // Rsvd
u32 max_scratchpad_buffers_high : 5; // Max Scratchpad Bufs Hi
u32 scratchpad_restore : 1; // SPR
u32 max_scratchpad_buffers_low : 5; // Max Scratchpad Bufs Lo
// HCSPARAMS3
u32 U1_device_exit_latency : 8; // U1 Device Exit Latency
u32 reserved2 : 8; // Rsvd
u32 U2_device_exit_latency : 16; // U2 Device Exit Latency
} structural_parameters;
struct {
u32 _64bit_addressing_capability : 1; // AC64
u32 bandwidth_negotiation_capability : 1; // BNC
u32 context_size : 1; // CSZ
u32 port_power_control : 1; // PPC
u32 port_indicators : 1; // PIND
u32 light_host_controller_reset_capability : 1; // LHRC
u32 latency_tolerance_messaging_capability : 1; // LTC
u32 no_secondary_stream_id_support : 1; // NSS
u32 parse_all_event_data : 1; // PAE
u32 stopped_short_packet_capability : 1; // SPC
u32 stopped_EDTLA_capability : 1; // SEC
u32 contiguous_frame_id_capability : 1; // CFC
u32 maximum_primary_stream_array_size : 4; // MaxPSASize
u32 xHCI_extended_capabilities_pointer : 16; // xECP
} capability_parameters_1; // HCCPARAMS1
u32 doorbell_offset; // DBOFF
u32 runtime_register_space_offset; // RTSOFF
struct {
u32 U3_entry_capability : 1; // U3C
u32 configure_endpoint_command_max_exit_latency_too_large_capability : 1; // CMC
u32 force_save_context_capability : 1; // FSC
u32 compliance_transition_capability : 1; // CTC
u32 large_ESIT_payload_capability : 1; // LEC
u32 configuration_information_capability : 1; // CIC
u32 extended_TBC_capability : 1; // ETC
u32 extended_TBC_TRB_status_capability : 1; // ETC_TSC
u32 get_set_extended_property_capability : 1; // GSC
u32 virtualization_based_trusted_io_capability : 1; // VTC
u32 reserved0 : 22; // Reserved
} capability_parameters_2; // HCCPARAMS2
};
static_assert(AssertSize<CapabilityRegisters, 0x20>());
union PortStatusAndControl {
struct {
u32 current_connect_status : 1; // CCS
u32 port_enabled_disabled : 1; // PED
u32 reserved0 : 1; // RsvdZ
u32 over_current_active : 1; // OCA
u32 port_reset : 1; // PR
u32 port_link_state : 4; // PLS
u32 port_power : 1; // PP
u32 port_speed : 4; // Port Speed
u32 port_indicator_control : 2; // PIC
u32 port_link_state_write_strobe : 1; // LWS
u32 connect_status_change : 1; // CSC
u32 port_enabled_disabled_change : 1; // PEC
u32 warm_port_reset_change : 1; // WRC
u32 over_current_change : 1; // OCC
u32 port_reset_change : 1; // PRC
u32 port_link_state_change : 1; // PLC
u32 port_config_error_change : 1; // CEC
u32 cold_attach_status : 1; // CAS
u32 wake_on_connect_enable : 1; // WCE
u32 wake_on_disconnect_enable : 1; // WDE
u32 wake_on_over_current_enable : 1; // WOE
u32 reserved1 : 2; // RsvdZ
u32 device_removable : 1; // DR
u32 warm_port_reset : 1; // WPR
};
u32 raw { 0 }; // RW1CS fields
};
static_assert(AssertSize<PortStatusAndControl, 0x4>());
struct PortRegisters {
PortStatusAndControl port_status_and_control; // PORTSC
union {
struct {
u32 U1_timeout : 8; // U1 Timeout
u32 U2_timeout : 8; // U2 Timeout
u32 force_link_power_management_accept : 1; // FLA
u32 reserved0 : 15; // RsvdP
} usb3;
struct {
u32 l1_status : 3; // L1S
u32 remote_wake_enable : 1; // RWE
u32 best_effort_service_latency : 4; // BESL
u32 l1_device_slot : 8; // L1 Device Slot
u32 hardware_link_power_management_enable : 1; // HLE
u32 reserved0 : 11; // RsvdP
u32 port_test_control : 4; // Test Mode
} usb2;
} port_power_management_status_and_control; // PORTPMSC
union {
struct {
u32 link_error_count : 16; // Link Error Count
u32 rx_lane_count : 4; // RLC
u32 tx_lane_count : 4; // TLC
u32 reserved0 : 8; // RsvdP
} usb3;
struct {
u32 reserved0;
} usb2;
} port_link_info; // PORTLI
union {
struct {
u32 reserved0;
} usb3;
struct {
u32 host_initiated_resume_duration_mode : 2; // HIRDM
u32 l1_timeout : 8; // L1 Timeout
u32 best_effort_service_latency_deep : 4; // BESLD
u32 reserved0 : 18; // RsvdP
} usb2;
} port_hardware_link_power_management_control; // PORTHLPMC
};
static_assert(AssertSize<PortRegisters, 0x10>());
union USBStatus {
struct {
u32 host_controller_halted : 1; // HCH
u32 reserved0 : 1; // RsvdZ
u32 host_system_error : 1; // HSE
u32 event_interrupt : 1; // EINT
u32 port_change_detect : 1; // PCD
u32 reserved1 : 3; // RsvdZ
u32 save_state_status : 1; // SSS
u32 restore_status_status : 1; // RSS
u32 save_restore_error : 1; // SRE
u32 controller_not_ready : 1; // CNR
u32 host_controller_error : 1; // HCE
u32 reserved2 : 19; // RsvdZ
};
u32 raw { 0 }; // RW1CS fields
};
static_assert(AssertSize<USBStatus, 0x4>());
union CommandRingControlRegister {
struct {
u32 ring_cycle_state : 1; // RCS
u32 command_stop : 1; // CS
u32 command_abort : 1; // CA
u32 command_ring_running : 1; // CRR
u32 reserved0 : 2; // RsvdP
u32 command_ring_pointer_low : 26; // Command Ring Pointer Lo
u32 command_ring_pointer_high : 32; // Command Ring Pointer Hi
};
struct {
u32 raw0 { 0 };
u32 raw1 { 0 };
}; // RW1CS fields
};
static_assert(AssertSize<CommandRingControlRegister, 0x8>());
// 5.4 Host Controller Operational Registers
struct OperationalRegisters {
struct {
u32 run_stop : 1; // R/S
u32 host_controller_reset : 1; // HCRST
u32 interrupter_enable : 1; // INTE
u32 host_system_error_enable : 1; // HSEE
u32 reserved0 : 3; // RsvdP
u32 light_host_controller_reset : 1; // LHCRST
u32 controller_save_state : 1; // CSS
u32 controller_restore_state : 1; // CRS
u32 enable_wrap_event : 1; // EWE
u32 enable_U3_microframe_index_stop : 1; // EU3S
u32 reserved1 : 1; // RsvdP
u32 CEM_enable : 1; // CME
u32 extended_transfer_burst_count_enable : 1; // ETE
u32 extended_TBC_TRB_status_enable : 1; // TSB_EN
u32 VTIO_enable : 1; // VTIOE
u32 reserved2 : 15; // RsvdP
} usb_command; // USBCMD
USBStatus usb_status; // USBSTS
u32 page_size; // PAGESIZE
u32 reserved0[2]; // RsvdZ
struct {
u32 notification_enable_0 : 1; // N0
u32 notification_enable_1 : 1; // N1
u32 notification_enable_2 : 1; // N2
u32 notification_enable_3 : 1; // N3
u32 notification_enable_4 : 1; // N4
u32 notification_enable_5 : 1; // N5
u32 notification_enable_6 : 1; // N6
u32 notification_enable_7 : 1; // N7
u32 notification_enable_8 : 1; // N8
u32 notification_enable_9 : 1; // N9
u32 notification_enable_10 : 1; // N10
u32 notification_enable_11 : 1; // N11
u32 notification_enable_12 : 1; // N12
u32 notification_enable_13 : 1; // N13
u32 notification_enable_14 : 1; // N14
u32 notification_enable_15 : 1; // N15
u32 reserved0 : 16; // RsvdP
} device_notification_control; // DNCTRL
CommandRingControlRegister command_ring_control; // CRCR
u32 reserved1[4]; // RsvdZ
struct {
u32 low;
u32 high;
} device_context_base_address_array_pointer; // DCBAAP
struct {
u32 max_device_slots_enabled : 8; // MaxSlotsEn
u32 U3_entry_enable : 1; // U3E
u32 configuration_information_enable : 1; // CIE
u32 reserved0 : 22; // RsvdP
} configure; // CONFIG
u32 reserved2[241]; // RsvdZ
PortRegisters port_registers[0x100];
};
static_assert(AssertSize<OperationalRegisters, 0x1400>());
struct InterrupterRegisters {
struct {
u32 interrupt_pending : 1; // IP
u32 interrupt_enabled : 1; // IE
u32 reserved0 : 30; // RsvdP
} interrupter_management; // IMAN
struct {
u32 interrupt_moderation_interval : 16; // IMODI
u32 interrupt_moderation_counter : 16; // IMODC
} interrupter_moderation; // IMOD
u32 even_ring_segment_table_size; // ERSTSZ
u32 reserved0; // RsvdP
struct {
u32 low;
u32 high;
} event_ring_segment_table_base_address; // ERSTBA
struct {
u32 dequeue_ERST_segment_index : 3; // DESI
u32 event_handler_busy : 1; // EHB
u32 event_ring_dequeue_pointer_low : 28; // ERDP Lo
u32 event_ring_dequeue_pointer_high : 32; // ERDP Hi
} event_ring_dequeue_pointer; // ERDP
};
static_assert(AssertSize<InterrupterRegisters, 0x20>());
// 5.5 Host Controller Runtime Registers
struct RuntimeRegisters {
u32 microframe_index; // MFINDEX
u32 reserved0[7]; // RsvdZ
InterrupterRegisters interrupter_registers[0x400];
};
static_assert(AssertSize<RuntimeRegisters, 0x8020>());
// 5.6 Doorbell Registers
union DoorbellRegister {
struct {
u32 doorbell_target : 8; // DB Target
u32 reserved0 : 8; // RsvdZ
u32 doorbell_stream_id : 16; // DB Stream ID
};
u32 raw; // All fields must be modified at once
};
static_assert(AssertSize<DoorbellRegister, 0x4>());
struct DoorbellRegisters {
u32 doorbells[256];
};
static_assert(AssertSize<DoorbellRegisters, 0x400>());
struct ExtendedCapability {
enum class CapabilityID : u32 {
USB_Legacy_Support = 1,
Supported_Protocols = 2,
Extended_Power_Management = 3,
IO_Virtualization = 4,
Message_Interrupt = 5,
USB_Debug_Capability = 10,
Extended_Message_Interrupt = 17,
};
CapabilityID capability_id : 8;
u32 next_xHCI_extended_capability_pointer : 8;
u32 capability_specific : 16;
};
static_assert(AssertSize<ExtendedCapability, 0x4>());
struct USBLegacySupportExtendedCapability {
struct {
ExtendedCapability::CapabilityID capability_id : 8;
u32 next_xHCI_extended_capability_pointer : 8;
u32 host_controller_bios_owned_semaphore : 1;
u32 reserved0 : 7;
u32 host_controller_os_owned_semaphore : 1;
u32 reserved1 : 7;
} usb_legacy_support_capability; // USBLEGSUP
struct {
u32 usb_smi_enable : 1;
u32 reserved0 : 3;
u32 smi_on_host_system_error_enable : 1;
u32 reserved1 : 8;
u32 smi_on_os_ownership_enable : 1;
u32 smi_on_pci_command_enable : 1;
u32 smi_on_bar_enable : 1;
u32 smi_on_event_interrupt : 1;
u32 reserved2 : 3;
u32 smi_on_host_system_error : 1;
u32 reserved3 : 8;
u32 smi_on_os_ownership_change : 1;
u32 smi_on_pci_command : 1;
u32 smi_on_bar : 1;
} usb_legacy_support_control_status; // USBLEGCTLSTS
};
static_assert(AssertSize<USBLegacySupportExtendedCapability, 0x8>());
struct ProtocolSpeedID {
u32 protocol_speed_id_value : 4; // PSIV
u32 protocol_speed_id_exponent : 2; // PSIE
u32 protocol_speed_id_type : 2; // PLT
u32 protocol_speed_id_full_duplex : 1; // PFD
u32 reserved0 : 5; // RsvdP
u32 link_protocol : 2; // LP
u32 protocol_speed_id_mantissa : 16; // PSIM
};
static_assert(AssertSize<ProtocolSpeedID, 0x4>());
struct SupportedProtocolExtendedCapability {
static constexpr u32 usb_name_string = AK::convert_between_host_and_little_endian(0x20425355);
ExtendedCapability::CapabilityID capability_id : 8;
u32 next_xHCI_extended_capability_pointer : 8;
u32 minor_revision : 8;
u32 major_revision : 8;
u32 name_string : 32;
u32 compatible_port_offset : 8;
u32 compatible_port_count : 8;
u32 protocol_defined : 12;
u32 protocol_speed_id_count : 4; // PSIC
u32 protocol_slot_type : 5;
u32 reserved0 : 27;
ProtocolSpeedID protocol_speed_ids[16];
};
static_assert(AssertSize<SupportedProtocolExtendedCapability, 0x50>());
struct EventRingSegmentTableEntry {
u32 ring_segment_base_address_low : 32;
u32 ring_segment_base_address_high : 32;
u32 ring_segment_size : 16;
u32 reserved0 : 16;
u32 reserved1 : 32;
};
static_assert(AssertSize<EventRingSegmentTableEntry, 0x10>());
union TransferRequestBlock {
enum class TRBType : u32 {
Normal = 1,
Setup_Stage = 2,
Data_Stage = 3,
Status_Stage = 4,
Isoch = 5,
Link = 6,
Event_Data = 7,
No_Op = 8,
Enable_Slot_Command = 9,
Disable_Slot_Command = 10,
Address_Device_Command = 11,
Configure_Endpoint_Command = 12,
Evaluate_Context_Command = 13,
Reset_Endpoint_Command = 14,
Stop_Endpoint_Command = 15,
Set_TR_Dequeue_Pointer_Command = 16,
Reset_Device_Command = 17,
Force_Event_Command = 18,
Negotiate_Bandwidth_Command = 19,
Set_Latency_Tolerance_Value_Command = 20,
Get_Port_Bandwidth_Command = 21,
Force_Header_Command = 22,
No_Op_Command = 23,
Get_Extended_Property_Command = 24,
Set_Extended_Property_Command = 25,
Transfer_Event = 32,
Command_Completion_Event = 33,
Port_Status_Change_Event = 34,
Bandwidth_Request_Event = 35,
Doorbell_Event = 36,
Host_Controller_Event = 37,
Device_Notification_Event = 38,
Microframe_Index_Wrap_Event = 39,
};
enum class CompletionCode : u32 {
Invalid = 0,
Success = 1,
Data_Buffer_Error = 2,
Babble_Detected_Error = 3,
USB_Transaction_Error = 4,
TRB_Error = 5,
Stall_Error = 6,
Resource_Error = 7,
Bandwidth_Error = 8,
No_Slots_Available_Error = 9,
Invalid_Stream_Type_Error = 10,
Slot_Not_Enabled_Error = 11,
Endpoint_Not_Enabled_Error = 12,
Short_Packet = 13,
Ring_Underrun = 14,
Ring_Overrun = 15,
VF_Event_Ring_Full_Error = 16,
Parameter_Error = 17,
Bandwidth_Overrun_Error = 18,
Context_State_Error = 19,
No_Ping_Response_Error = 20,
Event_Ring_Full_Error = 21,
Incompatible_Device_Error = 22,
Missed_Service_Error = 23,
Command_Ring_Stopped = 24,
Command_Aborted = 25,
Stopped = 26,
Stopped_Length_Invalid = 27,
Stopped_Short_Packet = 28,
Max_Exit_Latency_Too_Large_Error = 29,
Isoch_Buffer_Overrun = 31,
Event_Lost_Error = 32,
Undefined_Error = 33,
Invalid_Stream_ID_Error = 34,
Secondary_Bandwidth_Error = 35,
Split_Transaction_Error = 36,
};
enum class TransferType : u32 {
No_Data_Stage = 0,
Reserved = 1,
OUT_Data_Stage = 2,
IN_Data_Stage = 3,
};
struct {
u32 parameter0 : 32;
u32 parameter1 : 32;
u32 status : 32;
u32 cycle_bit : 1; // C
u32 evaluate_next_transfer_request_block : 1; // ENT
u32 reserved0 : 2;
u32 chain_bit : 1; // CH
u32 reserved1 : 5;
TRBType transfer_request_block_type : 6; // TRB Type
u32 control : 16;
} generic;
struct {
u32 data_buffer_pointer_low : 32;
u32 data_buffer_pointer_high : 32;
u32 transfer_request_block_transfer_length : 17;
u32 transfer_descriptor_size : 5;
u32 interrupter_target : 10;
u32 cycle_bit : 1; // C
u32 evaluate_next_transfer_request_block : 1; // ENT
u32 interrupt_on_short_packet : 1; // ISP
u32 no_snoop : 1; // NS
u32 chain_bit : 1; // CH
u32 interrupt_on_completion : 1; // IOC
u32 immediate_data : 1; // IDT
u32 reserved0 : 2; // RsvdZ
u32 block_event_interrupt : 1; // BEI
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved1 : 16;
} normal;
struct {
u32 request_type : 8; // bmRequestType
u32 request : 8; // bRequest
u32 value : 16; // wValue
u32 index : 16; // wIndex
u32 length : 16; // wLength
u32 transfer_request_block_transfer_length : 17; // TRB Transfer Length
u32 reserved0 : 5; // RsvdZ
u32 interrupter_target : 10; // Interrupter Target
u32 cycle_bit : 1; // C
u32 reserved1 : 4; // RsvdZ
u32 interrupt_on_completion : 1; // IOC
u32 immediate_data : 1; // IDT
u32 reserved2 : 3; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
TransferType transfer_type : 2; // TRT
u32 reserved3 : 14; // RsvdZ
} setup_stage;
struct {
u32 data_buffer_low : 32;
u32 data_buffer_high : 32;
u32 transfer_request_block_transfer_length : 17; // TRB Transfer Length
u32 transfer_descriptor_size : 5;
u32 interrupter_target : 10;
u32 cycle_bit : 1; // C
u32 evaluate_next_transfer_request_block : 1; // ENT
u32 interrupt_on_short_packet : 1; // ISP
u32 no_snoop : 1; // NS
u32 chain_bit : 1; // CH
u32 interrupt_on_completion : 1; // IOC
u32 immediate_data : 1; // IDT
u32 reserved0 : 3; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 direction : 1; // DIR
u32 reserved1 : 15; // RsvdZ
} data_stage;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 reserved2 : 22;
u32 interrupter_target : 10;
u32 cycle_bit : 1; // C
u32 evaluate_next_transfer_request_block : 1; // ENT
u32 reserved3 : 2; // RsvdZ
u32 chain_bit : 1; // CH
u32 interrupt_on_completion : 1; // IOC
u32 reserved4 : 4;
TRBType transfer_request_block_type : 6; // TRB Type
u32 direction : 1; // DIR
u32 reserved5 : 15; // RsvdZ
} status_stage;
struct {
u32 data_buffer_pointer_low : 32;
u32 data_buffer_pointer_high : 32;
u32 transfer_request_block_transfer_length : 17;
u32 transfer_descriptor_size_OR_transfer_burst_count : 5; // TD Size/TBC
u32 interrupter_target : 10;
u32 cycle_bit : 1; // C
u32 evaluate_next_transfer_request_block : 1; // ENT
u32 interrupt_on_short_packet : 1; // ISP
u32 no_snoop : 1; // NS
u32 chain_bit : 1; // CH
u32 interrupt_on_completion : 1; // IOC
u32 immediate_data : 1; // IDT
u32 transfer_burst_count_OR_transfer_request_block_status_OR_reserved0 : 2; // TBC/TRBSts/RsvdZ
u32 block_event_interrupt : 1; // BEI
TRBType transfer_request_block_type : 6; // TRB Type
u32 transfer_last_burst_packet_count : 4; // TLBPC
u32 frame_id : 11;
u32 start_isoch_as_soon_as_possible : 1; // SIA
} isoch;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 reserved2 : 22;
u32 interrupter_target : 10;
u32 cycle_bit : 1; // C
u32 evaluate_next_transfer_request_block : 1; // ENT
u32 reserved3 : 2; // RsvdZ
u32 chain_bit : 1; // CH
u32 interrupt_on_completion : 1; // IOC
u32 reserved4 : 4; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved5 : 16; // RsvdZ
} no_op;
struct {
u32 transfer_request_block_pointer_low : 32;
u32 transfer_request_block_pointer_high : 32;
u32 transfer_request_block_transfer_length : 24;
CompletionCode completion_code : 8;
u32 cycle_bit : 1; // C
u32 reserved0 : 1; // RsvdZ
u32 event_data : 1; // ED
u32 reserved1 : 7;
TRBType transfer_request_block_type : 6; // TRB Type
u32 endpoint_id : 5;
u32 reserved2 : 3;
u32 slot_id : 8;
} transfer_event;
struct {
u32 command_transfer_request_block_pointer_low : 32;
u32 command_transfer_request_block_pointer_high : 32;
u32 command_completion_parameter : 24;
CompletionCode completion_code : 8;
u32 cycle_bit : 1; // C
u32 reserved0 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 vf_id : 8;
u32 slot_id : 8;
} command_completion_event;
struct {
u32 reserved0 : 24;
u32 port_id : 8;
u32 reserved1 : 32;
u32 reserved2 : 24;
CompletionCode completion_code : 8;
u32 cycle_bit : 1; // C
u32 reserved3 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved4 : 16;
} port_status_change_event;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 reserved2 : 24;
CompletionCode completion_code : 8;
u32 cycle_bit : 1; // C
u32 reserved3 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved4 : 8;
u32 slot_id : 8;
} bandwidth_request_event;
struct {
u32 doorbell_reason : 5;
u32 reserved0 : 27;
u32 reserved1 : 32;
u32 reserved2 : 24;
CompletionCode completion_code : 8;
u32 cycle_bit : 1; // C
u32 reserved3 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 vf_id : 8;
u32 slot_id : 8;
} doorbell_event;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 reserved2 : 24;
CompletionCode completion_code : 8;
u32 cycle_bit : 1; // C
u32 reserved3 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved4 : 16;
} host_controller_event;
struct {
u32 reserved0 : 4;
u32 notification_type : 4;
u32 device_notification_data_low : 24;
u32 device_notification_data_high : 32;
u32 reserved1 : 24;
CompletionCode completion_code : 8;
u32 cycle_bit : 1; // C
u32 reserved2 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved3 : 8;
u32 slot_id : 8;
} device_notification_event;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 reserved2 : 24;
CompletionCode completion_code : 8;
u32 cycle_bit : 1; // C
u32 reserved3 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved4 : 16;
} microframe_index_wrap_event;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 reserved2 : 32;
u32 cycle_bit : 1; // C
u32 reserved3 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved4 : 16;
} no_op_command;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 reserved2 : 32;
u32 cycle_bit : 1; // C
u32 reserved3 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 slot_type : 5;
u32 reserved4 : 11;
} enable_slot_command;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 reserved2 : 32;
u32 cycle_bit : 1; // C
u32 reserved3 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved4 : 8;
u32 slot_id : 8;
} disable_slot_command;
struct {
u32 input_context_pointer_low : 32;
u32 input_context_pointer_high : 32;
u32 reserved0 : 32;
u32 cycle_bit : 1; // C
u32 reserved1 : 8; // RsvdZ
u32 block_set_address_request : 1; // BSR
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved2 : 8;
u32 slot_id : 8;
} address_device_command;
struct {
u32 input_context_pointer_low : 32;
u32 input_context_pointer_high : 32;
u32 reserved0 : 32;
u32 cycle_bit : 1; // C
u32 reserved1 : 8; // RsvdZ
u32 deconfigure : 1; // DC
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved2 : 8;
u32 slot_id : 8;
} configure_endpoint_command;
struct {
u32 input_context_pointer_low : 32;
u32 input_context_pointer_high : 32;
u32 reserved0 : 32;
u32 cycle_bit : 1; // C
u32 reserved1 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved2 : 8;
u32 slot_id : 8;
} evaluate_context_command;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 reserved2 : 32;
u32 cycle_bit : 1; // C
u32 reserved3 : 8; // RsvdZ
u32 transfer_state_preserve : 1; // TSP
TRBType transfer_request_block_type : 6; // TRB Type
u32 endpoint_id : 5;
u32 reserved4 : 3;
u32 slot_id : 8;
} reset_endpoint_command;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 reserved2 : 32;
u32 cycle_bit : 1; // C
u32 reserved3 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 endpoint_id : 5;
u32 reserved4 : 2;
u32 suspend : 1; // SP
u32 slot_id : 8;
} stop_endpoint_command;
struct {
u32 dequeue_cycle_state : 1; // DCS
u32 stream_context_type : 3; // SCT
u32 new_tr_dequeue_pointer_low : 28;
u32 new_tr_dequeue_pointer_high : 32;
u32 reserved0 : 16;
u32 stream_id : 16;
u32 cycle_bit : 1; // C
u32 reserved1 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 endpoint_id : 5;
u32 reserved2 : 3;
u32 slot_id : 8;
} set_tr_dequeue_pointer_command;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 reserved2 : 32;
u32 cycle_bit : 1; // C
u32 reserved3 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved4 : 8;
u32 slot_id : 8;
} reset_device_command;
struct {
u32 event_transfer_request_block_pointer_low : 32;
u32 event_transfer_request_block_pointer_high : 32;
u32 reserved0 : 22;
u32 vf_interrupter_target : 10;
u32 cycle_bit : 1; // C
u32 reserved1 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 vf_id : 8;
u32 reserved4 : 8;
} force_event_command;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 reserved2 : 32;
u32 cycle_bit : 1; // C
u32 reserved3 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved4 : 8;
u32 slot_id : 8;
} negotiate_bandwidth_command;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 reserved2 : 32;
u32 cycle_bit : 1; // C
u32 reserved3 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 best_effort_latency_tolerance_value : 12;
u32 reserved4 : 4;
} set_latency_tolerance_value_command;
struct {
u32 port_bandwidth_context_pointer_low : 32;
u32 port_bandwidth_context_pointer_high : 32;
u32 reserved0 : 32;
u32 cycle_bit : 1; // C
u32 reserved1 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 device_speed : 4;
u32 reserved2 : 4;
u32 hub_slot_id : 8;
} get_port_bandwidth_command;
struct {
u32 packet_type : 5;
u32 header_info_low : 27;
u32 header_info_middle : 32;
u32 header_info_high : 32;
u32 cycle_bit : 1; // C
u32 reserved0 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved1 : 8;
u32 root_hub_port_number : 8;
} force_header_command;
struct {
u32 extended_property_context_pointer_low : 32;
u32 extended_property_context_pointer_high : 32;
u32 extended_capability_identifier : 16; // ECI
u32 reserved0 : 16;
u32 cycle_bit : 1; // C
u32 reserved1 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 command_sub_type : 3;
u32 endpoint_id : 5;
u32 slot_id : 8;
} get_extended_property_command;
struct {
u32 reserved0 : 32;
u32 reserved1 : 32;
u32 extended_capability_identifier : 16; // ECI
u32 capability_parameter : 8;
u32 reserved2 : 8;
u32 cycle_bit : 1; // C
u32 reserved3 : 9; // RsvdZ
TRBType transfer_request_block_type : 6; // TRB Type
u32 command_sub_type : 3;
u32 endpoint_id : 5;
u32 slot_id : 8;
} set_extended_property_command;
struct {
u32 ring_segment_pointer_low : 32;
u32 ring_segment_pointer_high : 32;
u32 reserved0 : 22;
u32 interrupter_target : 10;
u32 cycle_bit : 1; // C
u32 toggle_cycle : 1; // TC
u32 reserved1 : 2; // RsvdZ
u32 chain_bit : 1; // CH
u32 interrupt_on_completion : 1; // IOC
u32 reserved2 : 4;
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved3 : 16;
} link;
struct {
u32 event_data_low : 32;
u32 event_data_high : 32;
u32 reserved0 : 22;
u32 interrupter_target : 10;
u32 cycle_bit : 1; // C
u32 evaluate_next_transfer_request_block : 1; // ENT
u32 reserved1 : 2;
u32 chain_bit : 1; // CH
u32 interrupt_on_completion : 1; // IOC
u32 reserved2 : 3; // RsvdZ
u32 block_event_interrupt : 1; // BEI
TRBType transfer_request_block_type : 6; // TRB Type
u32 reserved3 : 16;
} event_data;
};
static_assert(AssertSize<TransferRequestBlock, 0x10>());
static StringView enum_to_string(TransferRequestBlock::TRBType);
static StringView enum_to_string(TransferRequestBlock::CompletionCode);
static constexpr size_t command_ring_size = 16;
// Use up all the space left in the page
static constexpr size_t event_ring_segment_size = ((PAGE_SIZE - (sizeof(EventRingSegmentTableEntry) + sizeof(TransferRequestBlock) * command_ring_size)) & ~0x3F) / sizeof(TransferRequestBlock);
// System software is responsible for ensuring the Size of every ERST entry (Event Ring segment) is at least 16.
static_assert(event_ring_segment_size >= 16);
// System software shall allocate a buffer for the Event Ring Segment Table that rounds up its size to the nearest 64B boundary to allow full cache-line accesses.
static_assert((event_ring_segment_size * sizeof(TransferRequestBlock)) % 64 == 0);
// The command ring and event ring (ERST and the segment itself) are combined to not take 3 pages for something that fits in 1)
struct CommandAndEventRings {
TransferRequestBlock command_ring[command_ring_size];
TransferRequestBlock event_ring_segment[event_ring_segment_size];
// Software shall allocate a buffer for the Event Ring Segment Table that rounds up its size to the nearest 64B boundary to allow full cache-line accesses.
[[gnu::aligned(64)]] EventRingSegmentTableEntry event_ring_segment_table_entry;
};
static_assert(sizeof(CommandAndEventRings) <= 0x1000);
// System software shall allocate a buffer for the Event Ring Segment that rounds up its size to the nearest 64B boundary to allow full cache-line accesses
static_assert(__builtin_offsetof(CommandAndEventRings, command_ring) % 64 == 0);
static_assert(__builtin_offsetof(CommandAndEventRings, event_ring_segment) % 64 == 0);
struct InputControlContext {
u32 reserved0 : 2;
u32 drop_contexts : 30;
u32 add_contexts : 32;
u32 reserved1 : 32;
u32 reserved2 : 32;
u32 reserved3 : 32;
u32 reserved4 : 32;
u32 reserved5 : 32;
u32 configuration_value : 8;
u32 interface_number : 8;
u32 alternate_setting : 8;
u32 reserved6 : 8;
};
static_assert(AssertSize<InputControlContext, 0x20>());
struct SlotContext {
u32 route_string : 20;
u32 speed : 4;
u32 reserved0 : 1;
u32 multi_transaction_translator : 1; // MTT
u32 hub : 1;
u32 context_entries : 5;
u32 max_exit_latency : 16;
u32 root_hub_port_number : 8;
u32 number_of_ports : 8;
u32 parent_hub_slot_id : 8;
u32 parent_port_number : 8;
u32 transaction_translator_think_time : 2; // TTT
u32 reserved1 : 4;
u32 interrupter_target : 10;
u32 usb_device_address : 8;
u32 reserved2 : 19;
u32 slot_state : 5;
u32 reserved3 : 32;
u32 reserved4 : 32;
u32 reserved5 : 32;
u32 reserved6 : 32;
};
static_assert(AssertSize<SlotContext, 0x20>());
struct EndpointContext {
enum class EndpointType : u32 {
Not_Valid = 0,
Isoch_Out = 1,
Bulk_Out = 2,
Interrupt_Out = 3,
Control_Bidirectional = 4,
Isoch_In = 5,
Bulk_In = 6,
Interrupt_In = 7
};
u32 endpoint_state : 3;
u32 reserved0 : 5;
u32 mult : 2;
u32 max_primary_streams : 5; // MaxPStreams
u32 linear_stream_array : 1; // LSA
u32 interval : 8;
u32 max_endpoint_service_time_interval_payload_high : 8; // Max ESIT Payload Hi
u32 reserved1 : 1;
u32 error_count : 2; // CErr
EndpointType endpoint_type : 3;
u32 reserved2 : 1;
u32 host_initiate_disable : 1; // HID
u32 max_burst_size : 8;
u32 max_packet_size : 16;
u32 dequeue_cycle_state : 1;
u32 reserved3 : 3;
u32 transfer_ring_dequeue_pointer_low : 28;
u32 transfer_ring_dequeue_pointer_high : 32;
u32 average_transfer_request_block : 16;
u32 max_endpoint_service_time_interval_payload_low : 16; // Max ESIT Payload Lo
u32 reserved4 : 32;
u32 reserved5 : 32;
u32 reserved6 : 32;
};
static_assert(AssertSize<EndpointContext, 0x20>());
struct PendingTransfer {
IntrusiveListNode<PendingTransfer> endpoint_list_node;
u32 start_index { 0 };
u32 end_index { 0 };
};
struct SyncPendingTransfer : public PendingTransfer {
WaitQueue wait_queue;
TransferRequestBlock::CompletionCode completion_code { TransferRequestBlock::CompletionCode::Invalid };
u32 remainder { 0 };
};
struct PeriodicPendingTransfer : public PendingTransfer {
Vector<TransferRequestBlock> transfer_request_blocks;
NonnullLockRefPtr<Transfer> original_transfer;
};
struct EndpointRing {
OwnPtr<Memory::Region> region;
u32 enqueue_index { 0 };
u32 free_transfer_request_blocks { endpoint_ring_size - 1 }; // One less, since we use up the last one for the link TRB
u32 max_burst_payload { 0 };
Pipe::Type type { Pipe::Type::Control };
u8 producer_cycle_state { 1 };
IntrusiveList<&PendingTransfer::endpoint_list_node> pending_transfers;
TransferRequestBlock* ring_vaddr() const { return reinterpret_cast<TransferRequestBlock*>(region->vaddr().as_ptr()); }
PhysicalPtr ring_paddr() const { return region->physical_page(0)->paddr().get(); }
};
static constexpr size_t max_endpoints = 31;
static constexpr size_t endpoint_ring_size = PAGE_SIZE / sizeof(TransferRequestBlock);
struct SlotState {
RecursiveSpinlock<LockRank::None> lock;
OwnPtr<Memory::Region> input_context_region;
OwnPtr<Memory::Region> device_context_region;
Array<EndpointRing, max_endpoints> endpoint_rings;
};
static u8 endpoint_index(u8 endpoint, Pipe::Direction direction)
{
if (direction == Pipe::Direction::Bidirectional) {
VERIFY(endpoint == 0);
direction = Pipe::Direction::In;
}
return endpoint * 2 + to_underlying(direction);
}
size_t context_entry_size() const { return m_large_contexts ? 64 : 32; }
size_t input_context_size() const { return context_entry_size() * 33; }
u8* input_context(u8 slot, u8 index) const
{
auto* base = m_slots_state[slot - 1].input_context_region->vaddr().as_ptr();
return base + (context_entry_size() * index);
}
InputControlContext* input_control_context(u8 slot) const
{
return reinterpret_cast<InputControlContext*>(input_context(slot, 0));
}
SlotContext* input_slot_context(u8 slot) const
{
return reinterpret_cast<SlotContext*>(input_context(slot, 1));
}
EndpointContext* input_endpoint_context(u8 slot, u8 endpoint, Pipe::Direction direction) const
{
return reinterpret_cast<EndpointContext*>(input_context(slot, endpoint_index(endpoint, direction) + 1));
}
size_t device_context_size() const { return context_entry_size() * 32; }
u8* device_context(u8 slot, u8 index) const
{
auto* base = m_slots_state[slot - 1].device_context_region->vaddr().as_ptr();
return base + (context_entry_size() * index);
}
SlotContext* device_slot_context(u8 slot) const
{
return reinterpret_cast<SlotContext*>(device_context(slot, 0));
}
EndpointContext* device_endpoint_context(u8 slot, u8 endpoint, Pipe::Direction direction) const
{
return reinterpret_cast<EndpointContext*>(device_context(slot, endpoint_index(endpoint, direction)));
}
void ring_doorbell(u8 doorbell, u8 doorbell_target);
void ring_endpoint_doorbell(u8 slot, u8 endpoint, Pipe::Direction direction)
{
VERIFY(slot > 0);
ring_doorbell(slot, endpoint_index(endpoint, direction));
}
void ring_command_doorbell() { ring_doorbell(0, 0); }
void enqueue_command(TransferRequestBlock&);
void execute_command(TransferRequestBlock&);
ErrorOr<u8> enable_slot();
ErrorOr<void> address_device(u8 slot, u64 input_context_address);
ErrorOr<void> evaluate_context(u8 slot, u64 input_context_address);
ErrorOr<void> configure_endpoint(u8 slot, u64 input_context_address);
enum class TransferStatePreserve {
No,
Yes,
};
ErrorOr<void> reset_endpoint(u8 slot, u8 endpoint, TransferStatePreserve);
ErrorOr<void> set_tr_dequeue_pointer(u8 slot, u8 endpoint, u8 stream_context_type, u16 stream, u64 new_tr_dequeue_pointer, u8 dequeue_cycle_state);
ErrorOr<void> enqueue_transfer(u8 slot, u8 endpoint, Pipe::Direction direction, Span<TransferRequestBlock>, PendingTransfer&);
void handle_transfer_event(TransferRequestBlock const&);
ErrorOr<Vector<TransferRequestBlock>> prepare_normal_transfer(Transfer& transfer);
Memory::TypedMapping<u8> m_registers_mapping;
CapabilityRegisters const volatile& m_capability_registers;
OperationalRegisters volatile& m_operational_registers;
RuntimeRegisters volatile& m_runtime_registers;
DoorbellRegisters volatile& m_doorbell_registers;
RefPtr<Process> m_process;
WaitQueue m_event_queue;
bool m_using_message_signalled_interrupts { false };
bool m_large_contexts { false };
u8 m_device_slots { 0 };
Array<SlotState, max_devices> m_slots_state;
u8 m_ports { 0 };
Array<USB::Device::DeviceSpeed, 255> m_port_max_speeds {};
Vector<NonnullOwnPtr<PeriodicPendingTransfer>> m_active_periodic_transfers;
Spinlock<LockRank::None> m_command_lock;
WaitQueue m_command_completion_queue;
TransferRequestBlock m_command_result_transfer_request_block {};
u32 m_command_ring_enqueue_index { 0 };
u8 m_command_ring_producer_cycle_state { 1 };
u32 m_event_ring_dequeue_index { 0 };
u8 m_event_ring_consumer_cycle_state { 1 };
OwnPtr<Memory::Region> m_device_context_base_address_array_region;
u64* m_device_context_base_address_array { nullptr };
OwnPtr<Memory::Region> m_scratchpad_buffers_array_region;
Vector<NonnullRefPtr<Memory::PhysicalRAMPage>> m_scratchpad_buffers;
OwnPtr<Memory::Region> m_command_and_event_rings_region;
TransferRequestBlock* m_command_ring { nullptr };
TransferRequestBlock* m_event_ring_segment { nullptr };
PhysicalPtr m_event_ring_segment_pointer { 0 };
OwnPtr<GenericInterruptHandler> m_interrupter;
OwnPtr<xHCIRootHub> m_root_hub;
protected:
template<typename... Parameters>
void dmesgln_xhci(AK::CheckedFormatString<Parameters...>&& fmt, Parameters const&... parameters) const
{
StringBuilder builder;
MUST(write_dmesgln_prefix(builder));
AK::VariadicFormatParams<AK::AllowDebugOnlyFormatters::Yes, Parameters...> variadic_format_params { parameters... };
MUST(AK::vformat(builder, fmt.view(), variadic_format_params));
dmesgln("{}", builder.string_view());
}
};
}