forked from Bananymous/banan-os
Kernel: Start work on USB stack
Current code can enumerate all xHCI devices and detect their type based on the class code on device or interface descriptors.
This commit is contained in:
parent
8ddab05ed3
commit
14dce1abac
|
@ -93,6 +93,10 @@ set(KERNEL_SOURCES
|
|||
kernel/Timer/PIT.cpp
|
||||
kernel/Timer/RTC.cpp
|
||||
kernel/Timer/Timer.cpp
|
||||
kernel/USB/Device.cpp
|
||||
kernel/USB/USBManager.cpp
|
||||
kernel/USB/XHCI/Controller.cpp
|
||||
kernel/USB/XHCI/Device.cpp
|
||||
icxxabi.cpp
|
||||
)
|
||||
|
||||
|
@ -169,7 +173,8 @@ target_compile_definitions(kernel PUBLIC __arch=${BANAN_ARCH})
|
|||
target_compile_options(kernel PUBLIC -O2 -g)
|
||||
target_compile_options(kernel PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-Wno-literal-suffix -fno-rtti -fno-exceptions>)
|
||||
target_compile_options(kernel PUBLIC -fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.)
|
||||
target_compile_options(kernel PUBLIC -fstack-protector -ffreestanding -Wall -Wextra -Werror -Wstack-usage=1024 -fno-omit-frame-pointer -mgeneral-regs-only)
|
||||
target_compile_options(kernel PUBLIC -fstack-protector -ffreestanding -fno-omit-frame-pointer -fstrict-volatile-bitfields -mgeneral-regs-only)
|
||||
target_compile_options(kernel PUBLIC -Wall -Wextra -Werror -Wstack-usage=1024)
|
||||
|
||||
# This might not work with other toolchains
|
||||
target_compile_options(kernel PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>)
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class USBController
|
||||
{
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
namespace USB
|
||||
{
|
||||
enum class SpeedClass
|
||||
{
|
||||
None,
|
||||
LowSpeed,
|
||||
FullSpeed,
|
||||
HighSpeed,
|
||||
SuperSpeed,
|
||||
};
|
||||
|
||||
enum class DeviceBaseClass : uint8_t
|
||||
{
|
||||
CommunicationAndCDCControl = 0x02,
|
||||
Hub = 0x09,
|
||||
BillboardDeviceClass = 0x11,
|
||||
DiagnosticDevice = 0xDC,
|
||||
Miscellaneous = 0xEF,
|
||||
VendorSpecific = 0xFF,
|
||||
};
|
||||
|
||||
enum class InterfaceBaseClass : uint8_t
|
||||
{
|
||||
Audio = 0x01,
|
||||
CommunicationAndCDCControl = 0x02,
|
||||
HID = 0x03,
|
||||
Physical = 0x05,
|
||||
Image = 0x06,
|
||||
Printer = 0x07,
|
||||
MassStorage = 0x08,
|
||||
CDCData = 0x0A,
|
||||
SmartCard = 0x0B,
|
||||
ContentSecurity = 0x0D,
|
||||
Video = 0x0E,
|
||||
PersonalHealthcare = 0x0F,
|
||||
AudioVideoDevice = 0x10,
|
||||
USBTypeCBridgeClass = 0x12,
|
||||
USBBulkDisplayProtocolDeviceClass = 0x13,
|
||||
MCTPOverUSBProtocolEndpointDeviceClass = 0x14,
|
||||
I3CDeviceClass = 0x3C,
|
||||
DiagnosticDevice = 0xDC,
|
||||
WirelessController = 0xE0,
|
||||
Miscellaneous = 0xEF,
|
||||
ApplicationSpecific = 0xFE,
|
||||
VendorSpecific = 0xFF,
|
||||
};
|
||||
|
||||
enum RequestType : uint8_t
|
||||
{
|
||||
HostToDevice = 0b0 << 7,
|
||||
DeviceToHost = 0b1 << 7,
|
||||
|
||||
Standard = 0b00 << 5,
|
||||
Class = 0b01 << 5,
|
||||
Vendor = 0b10 << 5,
|
||||
|
||||
Device = 0b00000,
|
||||
Interface = 0b00001,
|
||||
Endpoint = 0b00010,
|
||||
Other = 0b00011,
|
||||
};
|
||||
|
||||
enum Request : uint8_t
|
||||
{
|
||||
GET_STATUS = 0,
|
||||
CLEAR_FEATURE = 1,
|
||||
SET_FEATURE = 3,
|
||||
SET_ADDRESS = 5,
|
||||
GET_DESCRIPTOR = 6,
|
||||
SET_DESCRIPTOR = 7,
|
||||
GET_CONFIGURATION = 8,
|
||||
SET_CONFIGURATION = 9,
|
||||
GET_INTERFACE = 10,
|
||||
SET_INTERFACE = 11,
|
||||
SYNC_FRAME = 12,
|
||||
};
|
||||
}
|
||||
|
||||
struct USBDeviceDescriptor
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t bcdUSB;
|
||||
uint8_t bDeviceClass;
|
||||
uint8_t bDeviceSubClass;
|
||||
uint8_t bDeviceProtocol;
|
||||
uint8_t bMaxPacketSize0;
|
||||
uint16_t idVendor;
|
||||
uint16_t idProduct;
|
||||
uint16_t bcdDevice;
|
||||
uint8_t iManufacturer;
|
||||
uint8_t iProduct;
|
||||
uint8_t iSerialNumber;
|
||||
uint8_t bNumConfigurations;
|
||||
};
|
||||
static_assert(sizeof(USBDeviceDescriptor) == 18);
|
||||
|
||||
struct USBConfigurationDescriptor
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t wTotalLength __attribute__((packed));
|
||||
uint8_t bNumInterfaces;
|
||||
uint8_t bConfigurationValue;
|
||||
uint8_t iConfiguration;
|
||||
uint8_t bmAttributes;
|
||||
uint8_t bMaxPower;
|
||||
};
|
||||
static_assert(sizeof(USBConfigurationDescriptor) == 9);
|
||||
static constexpr size_t foo = sizeof(USBConfigurationDescriptor);
|
||||
|
||||
struct USBInterfaceDescritor
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bInterfaceNumber;
|
||||
uint8_t bAlternateSetting;
|
||||
uint8_t bNumEndpoints;
|
||||
uint8_t bInterfaceClass;
|
||||
uint8_t bInterfaceSubClass;
|
||||
uint8_t bInterfaceProtocol;
|
||||
uint8_t iInterface;
|
||||
};
|
||||
static_assert(sizeof(USBInterfaceDescritor) == 9);
|
||||
|
||||
struct USBEndpointDescriptor
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bEndpointAddress;
|
||||
uint8_t bmAttributes;
|
||||
uint16_t wMaxPacketSize __attribute__((packed));
|
||||
uint8_t bInterval;
|
||||
};
|
||||
static_assert(sizeof(USBEndpointDescriptor) == 7);
|
||||
|
||||
struct USBDeviceRequest
|
||||
{
|
||||
uint8_t bmRequestType;
|
||||
uint8_t bRequest;
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
};
|
||||
static_assert(sizeof(USBDeviceRequest) == 8);
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/NoCopyMove.h>
|
||||
|
||||
#include <kernel/USB/USBManager.h>
|
||||
#include <kernel/USB/Controller.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class USBDevice
|
||||
{
|
||||
BAN_NON_COPYABLE(USBDevice);
|
||||
BAN_NON_MOVABLE(USBDevice);
|
||||
|
||||
public:
|
||||
struct EndpointDescriptor
|
||||
{
|
||||
USBEndpointDescriptor descriptor;
|
||||
};
|
||||
|
||||
struct InterfaceDescriptor
|
||||
{
|
||||
USBInterfaceDescritor descriptor;
|
||||
BAN::Vector<EndpointDescriptor> endpoints;
|
||||
};
|
||||
|
||||
struct ConfigurationDescriptor
|
||||
{
|
||||
USBConfigurationDescriptor desciptor;
|
||||
BAN::Vector<InterfaceDescriptor> interfaces;
|
||||
};
|
||||
|
||||
struct DeviceDescriptor
|
||||
{
|
||||
USBDeviceDescriptor descriptor;
|
||||
BAN::Vector<ConfigurationDescriptor> configurations;
|
||||
};
|
||||
|
||||
public:
|
||||
USBDevice() = default;
|
||||
virtual ~USBDevice() = default;
|
||||
|
||||
BAN::ErrorOr<void> initialize();
|
||||
|
||||
static USB::SpeedClass determine_speed_class(uint64_t bits_per_second);
|
||||
|
||||
protected:
|
||||
virtual BAN::ErrorOr<void> initialize_control_endpoint() = 0;
|
||||
virtual BAN::ErrorOr<void> send_request(const USBDeviceRequest&, paddr_t buffer) = 0;
|
||||
|
||||
private:
|
||||
DeviceDescriptor m_descriptor;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/PCI.h>
|
||||
#include <kernel/USB/Controller.h>
|
||||
#include <kernel/USB/Definitions.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class USBManager
|
||||
{
|
||||
BAN_NON_COPYABLE(USBManager);
|
||||
BAN_NON_MOVABLE(USBManager);
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<void> initialize();
|
||||
static USBManager& get();
|
||||
|
||||
BAN::ErrorOr<void> add_controller(PCI::Device& pci_device);
|
||||
|
||||
private:
|
||||
USBManager() = default;
|
||||
|
||||
private:
|
||||
BAN::Vector<BAN::UniqPtr<USBController>> m_controllers;
|
||||
|
||||
friend class BAN::UniqPtr<USBManager>;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <kernel/Lock/Mutex.h>
|
||||
#include <kernel/Memory/DMARegion.h>
|
||||
#include <kernel/USB/USBManager.h>
|
||||
#include <kernel/USB/XHCI/Definitions.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class XHCIDevice;
|
||||
|
||||
class XHCIController : public USBController, public Interruptable
|
||||
{
|
||||
BAN_NON_COPYABLE(XHCIController);
|
||||
BAN_NON_MOVABLE(XHCIController);
|
||||
|
||||
public:
|
||||
struct Port
|
||||
{
|
||||
uint8_t revision_major { 0 };
|
||||
uint8_t revision_minor { 0 };
|
||||
uint8_t slot_type { 0 };
|
||||
uint8_t slot_id { 0 };
|
||||
|
||||
uint64_t speed_id_to_speed[0x10] {
|
||||
0,
|
||||
12'000'000,
|
||||
1'500'000,
|
||||
480'000'000,
|
||||
5'000'000'000,
|
||||
10'000'000'000,
|
||||
5'000'000'000,
|
||||
10'000'000'000,
|
||||
};
|
||||
};
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::UniqPtr<XHCIController>> initialize(PCI::Device&);
|
||||
|
||||
void handle_irq() final override;
|
||||
|
||||
private:
|
||||
XHCIController(PCI::Device& pci_device);
|
||||
~XHCIController();
|
||||
|
||||
BAN::ErrorOr<void> initialize_impl();
|
||||
BAN::ErrorOr<void> initialize_ports();
|
||||
BAN::ErrorOr<void> initialize_primary_interrupter();
|
||||
|
||||
BAN::ErrorOr<void> reset_controller();
|
||||
|
||||
void port_updater_task();
|
||||
|
||||
BAN::ErrorOr<void> initialize_slot(int port_index);
|
||||
|
||||
BAN::ErrorOr<XHCI::TRB> send_command(const XHCI::TRB&);
|
||||
void advance_command_enqueue();
|
||||
|
||||
bool context_size_set() { return capability_regs().hccparams1.context_size; }
|
||||
|
||||
const Port& port(uint32_t port_id) const { return m_ports[port_id - 1]; }
|
||||
|
||||
volatile XHCI::CapabilityRegs& capability_regs();
|
||||
volatile XHCI::OperationalRegs& operational_regs();
|
||||
volatile XHCI::RuntimeRegs& runtime_regs();
|
||||
volatile uint32_t& doorbell_reg(uint32_t slot_id);
|
||||
volatile uint64_t& dcbaa_reg(uint32_t slot_id);
|
||||
|
||||
const volatile XHCI::TRB& current_event_trb();
|
||||
|
||||
private:
|
||||
static constexpr uint32_t m_command_ring_trb_count = 256;
|
||||
static constexpr uint32_t m_event_ring_trb_count = 252;
|
||||
|
||||
Mutex m_mutex;
|
||||
|
||||
Process* m_port_updater { nullptr };
|
||||
Semaphore m_port_semaphore;
|
||||
BAN::Atomic<bool> m_port_changed { false };
|
||||
|
||||
PCI::Device& m_pci_device;
|
||||
BAN::UniqPtr<PCI::BarRegion> m_configuration_bar;
|
||||
BAN::UniqPtr<DMARegion> m_dcbaa_region;
|
||||
|
||||
BAN::UniqPtr<DMARegion> m_command_ring_region;
|
||||
uint32_t m_command_enqueue { 0 };
|
||||
bool m_command_cycle { 1 };
|
||||
|
||||
BAN::UniqPtr<DMARegion> m_event_ring_region;
|
||||
uint32_t m_event_dequeue { 0 };
|
||||
bool m_event_cycle { 1 };
|
||||
|
||||
BAN::Vector<XHCI::TRB> m_command_completions;
|
||||
|
||||
BAN::Vector<Port> m_ports;
|
||||
BAN::Vector<BAN::UniqPtr<XHCIDevice>> m_slots;
|
||||
|
||||
friend class XHCIDevice;
|
||||
friend class BAN::UniqPtr<XHCIController>;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,593 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Kernel::XHCI
|
||||
{
|
||||
|
||||
struct CapabilityRegs
|
||||
{
|
||||
uint32_t caplength : 8;
|
||||
uint32_t : 8;
|
||||
uint32_t minor_revision : 8;
|
||||
uint32_t major_revision : 8;
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t hcsparams1_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t max_slots : 8;
|
||||
uint32_t max_interrupters : 11;
|
||||
uint32_t : 5;
|
||||
uint32_t max_ports : 8;
|
||||
} hcsparams1;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t hcsparams2_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t isochronous_scheduling_threshold : 4;
|
||||
uint32_t event_ring_segment_table_max : 4;
|
||||
uint32_t : 13;
|
||||
uint32_t max_scratchpad_buffers_hi : 5;
|
||||
uint32_t scratchpad_restore : 1;
|
||||
uint32_t max_scratchpad_buffers_lo : 5;
|
||||
} hcsparams2;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t hcsparams3_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t u1_device_exit_latency : 8;
|
||||
uint32_t : 8;
|
||||
uint32_t u2_device_exit_latency : 16;
|
||||
} hcsparams3;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t hccparams1_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t addressing_capability64 : 1;
|
||||
uint32_t bw_negotation_capability : 1;
|
||||
uint32_t context_size : 1;
|
||||
uint32_t port_power_control : 1;
|
||||
uint32_t port_indicators : 1;
|
||||
uint32_t light_hc_reset_capability : 1;
|
||||
uint32_t latency_tolerance_messaging_capability : 1;
|
||||
uint32_t no_secondary_sid_support : 1;
|
||||
uint32_t parse_all_event_data : 1;
|
||||
uint32_t stopped_short_packet_capability : 1;
|
||||
uint32_t stopped_edtla_capability : 1;
|
||||
uint32_t contiguous_frame_id_capability : 1;
|
||||
uint32_t maximum_primary_stream_array_size : 4;
|
||||
uint32_t xhci_extended_capabilities_pointer : 16;
|
||||
} hccparams1;
|
||||
};
|
||||
|
||||
uint32_t dboff;
|
||||
uint32_t rstoff;
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t hccparams2_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t u3_entry_capability : 1;
|
||||
uint32_t configure_endpoint_command_max_latency_too_large_capability : 1;
|
||||
uint32_t force_save_context_capability : 1;
|
||||
uint32_t compliance_transition_capability : 1;
|
||||
uint32_t large_esit_payload_capability : 1;
|
||||
uint32_t configuration_information_capability : 1;
|
||||
uint32_t extended_tbc_capability : 1;
|
||||
uint32_t extended_tbc_trb_status_capability : 1;
|
||||
uint32_t get_set_extended_property_capability : 1;
|
||||
uint32_t virtualiaztion_based_trusted_io_capability : 1;
|
||||
uint32_t : 22;
|
||||
} hccparams2;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(CapabilityRegs) == 0x20);
|
||||
|
||||
struct PortRegs
|
||||
{
|
||||
uint32_t portsc;
|
||||
uint32_t portmsc;
|
||||
uint32_t portli;
|
||||
uint32_t porthlpmc;
|
||||
};
|
||||
static_assert(sizeof(PortRegs) == 0x10);
|
||||
|
||||
struct OperationalRegs
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t usbcmd_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t run_stop : 1;
|
||||
uint32_t host_controller_reset : 1;
|
||||
uint32_t interrupter_enable : 1;
|
||||
uint32_t host_system_error_enable : 1;
|
||||
uint32_t : 3;
|
||||
uint32_t light_host_controller_reset : 1;
|
||||
uint32_t controller_save_state : 1;
|
||||
uint32_t controller_restore_state : 1;
|
||||
uint32_t enable_wrap_event : 1;
|
||||
uint32_t enable_u3_mfindex_stop : 1;
|
||||
uint32_t : 1;
|
||||
uint32_t cem_enable : 1;
|
||||
uint32_t extended_tbc_enable : 1;
|
||||
uint32_t extended_tbc_trb_status_enable : 1;
|
||||
uint32_t vtio_enable : 1;
|
||||
uint32_t : 15;
|
||||
} usbcmd;
|
||||
};
|
||||
|
||||
uint32_t usbsts;
|
||||
uint32_t pagesize;
|
||||
uint32_t __reserved0[2];
|
||||
uint32_t dnctrl;
|
||||
uint32_t crcr_lo;
|
||||
uint32_t crcr_hi;
|
||||
uint32_t __reserved1[4];
|
||||
uint32_t dcbaap_lo;
|
||||
uint32_t dcbaap_hi;
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t config_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t max_device_slots_enabled : 8;
|
||||
uint32_t u3_entry_enable : 1;
|
||||
uint32_t configuration_information_enable : 1;
|
||||
uint32_t : 22;
|
||||
} config;
|
||||
};
|
||||
|
||||
uint32_t __reserved2[241];
|
||||
PortRegs ports[];
|
||||
};
|
||||
static_assert(sizeof(OperationalRegs) == 0x400);
|
||||
|
||||
struct InterrupterRegs
|
||||
{
|
||||
uint32_t iman;
|
||||
uint32_t imod;
|
||||
uint32_t erstsz;
|
||||
uint32_t __reserved;
|
||||
uint64_t erstba;
|
||||
uint64_t erdp;
|
||||
};
|
||||
static_assert(sizeof(InterrupterRegs) == 0x20);
|
||||
|
||||
struct RuntimeRegs
|
||||
{
|
||||
uint32_t mfindex;
|
||||
uint32_t __reserved[7];
|
||||
InterrupterRegs irs[];
|
||||
};
|
||||
static_assert(sizeof(RuntimeRegs) == 0x20);
|
||||
|
||||
struct TRB
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint64_t parameter;
|
||||
uint32_t status;
|
||||
uint16_t cycle : 1;
|
||||
uint16_t ent : 1;
|
||||
uint16_t : 8;
|
||||
uint16_t trb_type : 6;
|
||||
uint16_t control;
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t dword0;
|
||||
uint32_t dword1;
|
||||
uint32_t dword2;
|
||||
uint32_t dword3;
|
||||
} raw;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t bmRequestType : 8;
|
||||
uint32_t bRequest : 8;
|
||||
uint32_t wValue : 16;
|
||||
|
||||
uint32_t wIndex : 16;
|
||||
uint32_t wLength : 16;
|
||||
|
||||
uint32_t trb_transfer_length : 17;
|
||||
uint32_t : 5;
|
||||
uint32_t interrupt_target : 10;
|
||||
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t : 4;
|
||||
uint32_t interrupt_on_completion : 1;
|
||||
uint32_t immediate_data : 1;
|
||||
uint32_t : 3;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t transfer_type : 2;
|
||||
uint32_t : 14;
|
||||
} setup_stage;
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t data_buffer_pointer : 64;
|
||||
|
||||
uint32_t trb_transfer_length : 17;
|
||||
uint32_t : 5;
|
||||
uint32_t interrupt_target : 10;
|
||||
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t evaluate_next_trb : 1;
|
||||
uint32_t interrupt_on_short_packet : 1;
|
||||
uint32_t no_snoop : 1;
|
||||
uint32_t chain_bit : 1;
|
||||
uint32_t interrupt_on_completion : 1;
|
||||
uint32_t immediate_data : 1;
|
||||
uint32_t : 3;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t direction : 1;
|
||||
uint32_t : 15;
|
||||
} data_stage;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
|
||||
uint32_t : 22;
|
||||
uint32_t interrupter_target : 10;
|
||||
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t evaluate_next_trb : 1;
|
||||
uint32_t : 2;
|
||||
uint32_t chain_bit : 1;
|
||||
uint32_t interrupt_on_completion : 1;
|
||||
uint32_t : 4;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t direction : 1;
|
||||
uint32_t : 15;
|
||||
} status_stage;
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t trb_pointer : 64;
|
||||
|
||||
uint32_t trb_transfer_length : 24;
|
||||
uint32_t completion_code : 8;
|
||||
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t : 1;
|
||||
uint32_t event_data : 1;
|
||||
uint32_t : 7;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t endpoint_id : 5;
|
||||
uint32_t : 3;
|
||||
uint32_t slot_id : 8;
|
||||
} transfer_event;
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t command_trb_pointer : 64;
|
||||
uint32_t command_completion_parameter : 24;
|
||||
uint32_t completion_code : 8;
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t : 9;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t vf_id : 8;
|
||||
uint32_t slot_id : 8;
|
||||
} command_completion_event;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t : 24;
|
||||
uint32_t port_id : 8;
|
||||
uint32_t : 32;
|
||||
uint32_t : 24;
|
||||
uint32_t completion_code : 8;
|
||||
uint32_t cycle : 1;
|
||||
uint32_t : 9;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t : 16;
|
||||
} port_status_chage_event;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t cycle : 1;
|
||||
uint32_t : 9;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t slot_type : 5;
|
||||
uint32_t : 11;
|
||||
} enable_slot_command;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t cycle : 1;
|
||||
uint32_t : 9;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t : 8;
|
||||
uint32_t slot_id : 8;
|
||||
} disable_slot_command;
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t input_context_pointer : 64;
|
||||
uint32_t : 32;
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t : 8;
|
||||
uint32_t block_set_address_request : 1;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t : 8;
|
||||
uint32_t slot_id : 8;
|
||||
} address_device_command;
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t ring_segment_ponter : 64;
|
||||
|
||||
uint32_t : 22;
|
||||
uint32_t interrupter_target : 10;
|
||||
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t toggle_cycle : 1;
|
||||
uint32_t : 2;
|
||||
uint32_t chain_bit : 1;
|
||||
uint32_t interrupt_on_completion : 1;
|
||||
uint32_t : 4;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t : 16;
|
||||
} link_trb;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(TRB) == 0x10);
|
||||
|
||||
struct EventRingTableEntry
|
||||
{
|
||||
uint64_t rsba;
|
||||
uint32_t rsz;
|
||||
uint32_t __reserved;
|
||||
};
|
||||
static_assert(sizeof(EventRingTableEntry) == 0x10);
|
||||
|
||||
struct ExtendedCap
|
||||
{
|
||||
uint32_t capability_id : 8;
|
||||
uint32_t next_capability : 8;
|
||||
uint32_t : 16;
|
||||
};
|
||||
static_assert(sizeof(ExtendedCap) == 4);
|
||||
|
||||
struct SupportedPrococolCap
|
||||
{
|
||||
uint32_t capability_id : 8;
|
||||
uint32_t next_capability : 8;
|
||||
uint32_t minor_revision : 8;
|
||||
uint32_t major_revision : 8;
|
||||
uint32_t name_string : 32;
|
||||
uint32_t compatible_port_offset : 8;
|
||||
uint32_t compatible_port_count : 8;
|
||||
uint32_t protocol_defied : 12;
|
||||
uint32_t protocol_speed_id_count : 4;
|
||||
uint32_t protocol_slot_type : 5;
|
||||
uint32_t : 27;
|
||||
};
|
||||
static_assert(sizeof(SupportedPrococolCap) == 0x10);
|
||||
|
||||
struct SlotContext
|
||||
{
|
||||
uint32_t route_string : 20;
|
||||
uint32_t speed : 4;
|
||||
uint32_t : 1;
|
||||
uint32_t multi_tt : 1;
|
||||
uint32_t hub : 1;
|
||||
uint32_t context_entries : 5;
|
||||
|
||||
uint16_t max_exit_latency;
|
||||
uint8_t root_hub_port_number;
|
||||
uint8_t number_of_ports;
|
||||
|
||||
uint32_t parent_hub_slot_id : 8;
|
||||
uint32_t parent_port_number : 8;
|
||||
uint32_t tt_think_time : 2;
|
||||
uint32_t : 4;
|
||||
uint32_t interrupter_target : 10;
|
||||
|
||||
uint32_t usb_device_address : 8;
|
||||
uint32_t : 19;
|
||||
uint32_t slot_state : 5;
|
||||
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
};
|
||||
static_assert(sizeof(SlotContext) == 0x20);
|
||||
|
||||
struct EndpointContext
|
||||
{
|
||||
uint32_t endpoint_state : 3;
|
||||
uint32_t : 5;
|
||||
uint32_t mult : 2;
|
||||
uint32_t max_primary_streams : 5;
|
||||
uint32_t linear_stream_array : 1;
|
||||
uint32_t interval : 8;
|
||||
uint32_t max_esit_payload_hi : 8;
|
||||
|
||||
uint32_t : 1;
|
||||
uint32_t error_count : 2;
|
||||
uint32_t endpoint_type : 3;
|
||||
uint32_t : 1;
|
||||
uint32_t host_initiate_disable : 1;
|
||||
uint32_t max_burst_size : 8;
|
||||
uint32_t max_packet_size : 16;
|
||||
|
||||
// LSB is dequeue cycle state
|
||||
uint64_t tr_dequeue_pointer;
|
||||
|
||||
uint32_t average_trb_length : 16;
|
||||
uint32_t max_esit_paylod_lo : 16;
|
||||
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
};
|
||||
static_assert(sizeof(EndpointContext) == 0x20);
|
||||
|
||||
struct InputControlContext
|
||||
{
|
||||
uint32_t drop_context_flags;
|
||||
uint32_t add_context_flags;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint8_t configuration_value;
|
||||
uint8_t interface_number;
|
||||
uint8_t alternate_setting;
|
||||
uint8_t : 8;
|
||||
};
|
||||
static_assert(sizeof(InputControlContext) == 0x20);
|
||||
|
||||
enum USBSTS
|
||||
{
|
||||
HCHalted = 1 << 0,
|
||||
HostSystemError = 1 << 2,
|
||||
EventInterrupt = 1 << 3,
|
||||
PortChangeDetect = 1 << 4,
|
||||
SaveStateStatus = 1 << 8,
|
||||
RstoreStateStatus = 1 << 9,
|
||||
SaveRestoreError = 1 << 10,
|
||||
ControllerNotReady = 1 << 11,
|
||||
HostControllerError = 1 << 12,
|
||||
};
|
||||
|
||||
enum CRCR : uint32_t
|
||||
{
|
||||
RingCycleState = 1 << 0,
|
||||
CommandStop = 1 << 1,
|
||||
CommandAbort = 1 << 2,
|
||||
CommandRingRunning = 1 << 3,
|
||||
};
|
||||
|
||||
enum IMAN : uint32_t
|
||||
{
|
||||
InterruptPending = 1 << 0,
|
||||
InterruptEnable = 1 << 1,
|
||||
};
|
||||
|
||||
enum ERDP
|
||||
{
|
||||
EventHandlerBusy = 1 << 3,
|
||||
};
|
||||
|
||||
enum PORTSC : uint32_t
|
||||
{
|
||||
CCS = 1u << 0,
|
||||
PED = 1u << 1,
|
||||
OCA = 1u << 3,
|
||||
PR = 1u << 4,
|
||||
PP = 1u << 9,
|
||||
LWS = 1u << 16,
|
||||
CSC = 1u << 17,
|
||||
PEC = 1u << 18,
|
||||
WRC = 1u << 19,
|
||||
OCC = 1u << 20,
|
||||
PRC = 1u << 21,
|
||||
PLC = 1u << 22,
|
||||
CEC = 1u << 23,
|
||||
CAS = 1u << 24,
|
||||
WCE = 1u << 25,
|
||||
WDE = 1u << 26,
|
||||
WOE = 1u << 27,
|
||||
DR = 1u << 30,
|
||||
WPR = 1u << 31,
|
||||
|
||||
PLS_SHIFT = 5,
|
||||
PLS_MASK = 0xF,
|
||||
PORT_SPEED_SHIFT = 10,
|
||||
PORT_SPEED_MASK = 0xF,
|
||||
PIC_SHIFT = 14,
|
||||
PIC_MASK = 0x3,
|
||||
};
|
||||
|
||||
enum ExtendedCapabilityID : uint8_t
|
||||
{
|
||||
USBLegacySupport = 1,
|
||||
SupportedProtocol = 2,
|
||||
ExtendedPowerManagement = 3,
|
||||
IOVirtualization = 4,
|
||||
MessageInterrupt = 5,
|
||||
LocalMemory = 6,
|
||||
USBDebugCapability = 10,
|
||||
ExtendedMessageInterrupt = 17,
|
||||
};
|
||||
|
||||
enum TRBType
|
||||
{
|
||||
Normal = 1,
|
||||
SetupStage = 2,
|
||||
DataStage = 3,
|
||||
StatusStage = 4,
|
||||
|
||||
Link = 6,
|
||||
|
||||
EnableSlotCommand = 9,
|
||||
DisableSlotCommand = 10,
|
||||
AddressDeviceCommand = 11,
|
||||
ConfigureEndpointCommand = 12,
|
||||
EvaluateContextCommand = 13,
|
||||
ResetEndpointCommand = 14,
|
||||
StopEndpointCommand = 15,
|
||||
SetTRDequeuePointerCommand = 16,
|
||||
ResetDeviceCommand = 17,
|
||||
ForceEventCommand = 18,
|
||||
NegotiateBandwidthCommand = 19,
|
||||
SetLatencyToleranceValueCommand = 20,
|
||||
GetPortBandwidthCommand = 21,
|
||||
ForceHeaderCommand = 22,
|
||||
NoOpCommand = 23,
|
||||
GetExtendedPropertyCommand = 24,
|
||||
SetExtendedPropertyCommand = 25,
|
||||
|
||||
TransferEvent = 32,
|
||||
CommandCompletionEvent = 33,
|
||||
PortStatusChangeEvent = 34,
|
||||
BandwidthRequestEvent = 35,
|
||||
DoorbellEvent = 36,
|
||||
HostControllerEvent = 37,
|
||||
DeviceNotificationEvent = 38,
|
||||
MFINDEXWrapEvent = 39,
|
||||
};
|
||||
|
||||
enum EndpointType
|
||||
{
|
||||
IsochOut = 1,
|
||||
BulkOut = 2,
|
||||
InterruptOut = 3,
|
||||
Control = 4,
|
||||
IsochIn = 5,
|
||||
BulkIn = 6,
|
||||
InterruptIn = 7,
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Array.h>
|
||||
#include <BAN/RefPtr.h>
|
||||
|
||||
#include <kernel/USB/Device.h>
|
||||
#include <kernel/USB/XHCI/Controller.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class XHCIDevice final : public USBDevice
|
||||
{
|
||||
BAN_NON_COPYABLE(XHCIDevice);
|
||||
BAN_NON_MOVABLE(XHCIDevice);
|
||||
|
||||
public:
|
||||
struct Endpoint
|
||||
{
|
||||
BAN::UniqPtr<DMARegion> transfer_ring;
|
||||
uint32_t enqueue_index { 0 };
|
||||
bool cycle_bit { 1 };
|
||||
|
||||
Mutex mutex;
|
||||
volatile XHCI::TRB completion_trb;
|
||||
};
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::UniqPtr<XHCIDevice>> create(XHCIController&, uint32_t port_id, uint32_t slot_id);
|
||||
|
||||
void on_transfer_event(const volatile XHCI::TRB&);
|
||||
|
||||
protected:
|
||||
BAN::ErrorOr<void> initialize_control_endpoint() override;
|
||||
BAN::ErrorOr<void> send_request(const USBDeviceRequest&, paddr_t buffer) override;
|
||||
|
||||
private:
|
||||
XHCIDevice(XHCIController& controller, uint32_t port_id, uint32_t slot_id)
|
||||
: m_controller(controller)
|
||||
, m_port_id(port_id)
|
||||
, m_slot_id(slot_id)
|
||||
{}
|
||||
~XHCIDevice();
|
||||
BAN::ErrorOr<void> update_actual_max_packet_size();
|
||||
|
||||
void advance_endpoint_enqueue(Endpoint&, bool chain);
|
||||
|
||||
private:
|
||||
static constexpr uint32_t m_transfer_ring_trb_count = 256;
|
||||
|
||||
XHCIController& m_controller;
|
||||
const uint32_t m_port_id;
|
||||
const uint32_t m_slot_id;
|
||||
|
||||
Mutex m_mutex;
|
||||
|
||||
uint32_t m_max_packet_size { 0 };
|
||||
|
||||
BAN::UniqPtr<DMARegion> m_input_context;
|
||||
BAN::UniqPtr<DMARegion> m_output_context;
|
||||
|
||||
BAN::Array<Endpoint, 31> m_endpoints;
|
||||
|
||||
friend class BAN::UniqPtr<XHCIDevice>;
|
||||
};
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
#include <kernel/Storage/ATA/AHCI/Controller.h>
|
||||
#include <kernel/Storage/ATA/ATAController.h>
|
||||
#include <kernel/Storage/NVMe/Controller.h>
|
||||
#include <kernel/USB/USBManager.h>
|
||||
|
||||
#define INVALID_VENDOR 0xFFFF
|
||||
#define MULTI_FUNCTION 0x80
|
||||
|
@ -209,6 +210,20 @@ namespace Kernel::PCI
|
|||
dprintln("{}", res.error());
|
||||
break;
|
||||
}
|
||||
case 0x0C:
|
||||
{
|
||||
switch (pci_device.subclass())
|
||||
{
|
||||
case 0x03:
|
||||
if (auto res = USBManager::get().add_controller(pci_device); res.is_error())
|
||||
dprintln("{}", res.error());
|
||||
break;
|
||||
default:
|
||||
dprintln("unsupported serail bus controller (pci {2H}.{2H}.{2H})", pci_device.class_code(), pci_device.subclass(), pci_device.prog_if());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,272 @@
|
|||
#include <kernel/Memory/DMARegion.h>
|
||||
#include <kernel/USB/Device.h>
|
||||
|
||||
#define DEBUG_USB 0
|
||||
#define USB_DUMP_DESCRIPTORS 0
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
BAN::ErrorOr<void> USBDevice::initialize()
|
||||
{
|
||||
TRY(initialize_control_endpoint());
|
||||
|
||||
auto buffer = TRY(DMARegion::create(PAGE_SIZE));
|
||||
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Standard | USB::RequestType::Device;
|
||||
request.bRequest = USB::Request::GET_DESCRIPTOR;
|
||||
request.wValue = 0x0100;
|
||||
request.wIndex = 0;
|
||||
request.wLength = sizeof(USBDeviceDescriptor);
|
||||
TRY(send_request(request, buffer->paddr()));
|
||||
|
||||
m_descriptor.descriptor = *reinterpret_cast<const USBDeviceDescriptor*>(buffer->vaddr());
|
||||
dprintln_if(DEBUG_USB, "device has {} configurations", m_descriptor.descriptor.bNumConfigurations);
|
||||
|
||||
for (uint32_t i = 0; i < m_descriptor.descriptor.bNumConfigurations; i++)
|
||||
{
|
||||
{
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Standard | USB::RequestType::Device;
|
||||
request.bRequest = USB::Request::GET_DESCRIPTOR;
|
||||
request.wValue = 0x0200 | i;
|
||||
request.wIndex = 0;
|
||||
request.wLength = sizeof(USBConfigurationDescriptor);
|
||||
TRY(send_request(request, buffer->paddr()));
|
||||
|
||||
auto configuration = *reinterpret_cast<const USBConfigurationDescriptor*>(buffer->vaddr());
|
||||
|
||||
dprintln_if(DEBUG_USB, " configuration {} is {} bytes", i, +configuration.wTotalLength);
|
||||
if (configuration.wTotalLength > buffer->size())
|
||||
{
|
||||
dwarnln(" our buffer is only {} bytes, skipping some fields...");
|
||||
configuration.wTotalLength = buffer->size();
|
||||
}
|
||||
|
||||
if (configuration.wTotalLength > request.wLength)
|
||||
{
|
||||
request.wLength = configuration.wTotalLength;
|
||||
TRY(send_request(request, buffer->paddr()));
|
||||
}
|
||||
}
|
||||
|
||||
auto configuration = *reinterpret_cast<const USBConfigurationDescriptor*>(buffer->vaddr());
|
||||
|
||||
BAN::Vector<InterfaceDescriptor> interfaces;
|
||||
TRY(interfaces.reserve(configuration.bNumInterfaces));
|
||||
|
||||
dprintln_if(DEBUG_USB, " configuration {} has {} interfaces", i, configuration.bNumInterfaces);
|
||||
|
||||
uintptr_t offset = configuration.bLength;
|
||||
for (uint32_t j = 0; j < configuration.bNumInterfaces; j++)
|
||||
{
|
||||
if (offset + sizeof(USBInterfaceDescritor) > buffer->size())
|
||||
break;
|
||||
auto interface = *reinterpret_cast<const USBInterfaceDescritor*>(buffer->vaddr() + offset);
|
||||
|
||||
BAN::Vector<EndpointDescriptor> endpoints;
|
||||
TRY(endpoints.reserve(interface.bNumEndpoints));
|
||||
|
||||
dprintln_if(DEBUG_USB, " interface {} has {} endpoints", j, interface.bNumEndpoints);
|
||||
|
||||
offset += interface.bLength;
|
||||
for (uint32_t k = 0; k < interface.bNumEndpoints; k++)
|
||||
{
|
||||
if (offset + sizeof(USBEndpointDescriptor) > buffer->size())
|
||||
break;
|
||||
auto endpoint = *reinterpret_cast<const USBEndpointDescriptor*>(buffer->vaddr() + offset);
|
||||
offset += endpoint.bLength;
|
||||
|
||||
TRY(endpoints.emplace_back(endpoint));
|
||||
}
|
||||
|
||||
TRY(interfaces.emplace_back(interface, BAN::move(endpoints)));
|
||||
}
|
||||
|
||||
TRY(m_descriptor.configurations.emplace_back(configuration, BAN::move(interfaces)));
|
||||
}
|
||||
|
||||
#if USB_DUMP_DESCRIPTORS
|
||||
const auto& descriptor = m_descriptor.descriptor;
|
||||
dprintln("device descriptor");
|
||||
dprintln(" bLength: {}", descriptor.bLength);
|
||||
dprintln(" bDescriptorType: {}", descriptor.bDescriptorType);
|
||||
dprintln(" bcdUSB: {}", descriptor.bcdUSB);
|
||||
dprintln(" bDeviceClass: {}", descriptor.bDeviceClass);
|
||||
dprintln(" bDeviceSubClass: {}", descriptor.bDeviceSubClass);
|
||||
dprintln(" bDeviceProtocol: {}", descriptor.bDeviceProtocol);
|
||||
dprintln(" bMaxPacketSize0: {}", descriptor.bMaxPacketSize0);
|
||||
dprintln(" idVendor: {}", descriptor.idVendor);
|
||||
dprintln(" idProduct: {}", descriptor.idProduct);
|
||||
dprintln(" bcdDevice: {}", descriptor.bcdDevice);
|
||||
dprintln(" iManufacturer: {}", descriptor.iManufacturer);
|
||||
dprintln(" iProduct: {}", descriptor.iProduct);
|
||||
dprintln(" iSerialNumber: {}", descriptor.iSerialNumber);
|
||||
dprintln(" bNumConfigurations: {}", descriptor.bNumConfigurations);
|
||||
|
||||
for (const auto& configuration : m_descriptor.configurations)
|
||||
{
|
||||
const auto& descriptor = configuration.desciptor;
|
||||
dprintln(" configuration");
|
||||
dprintln(" bLength: {}", descriptor.bLength);
|
||||
dprintln(" bDescriptorType: {}", descriptor.bDescriptorType);
|
||||
dprintln(" wTotalLength: {}", descriptor.wTotalLength);
|
||||
dprintln(" bNumInterfaces: {}", descriptor.bNumInterfaces);
|
||||
dprintln(" bConfigurationValue: {}", descriptor.bConfigurationValue);
|
||||
dprintln(" iConfiguration: {}", descriptor.iConfiguration);
|
||||
dprintln(" bmAttributes: {}", descriptor.bmAttributes);
|
||||
dprintln(" bMaxPower: {}", descriptor.bMaxPower);
|
||||
|
||||
for (const auto& interface : configuration.interfaces)
|
||||
{
|
||||
const auto& descriptor = interface.descriptor;
|
||||
dprintln(" interface");
|
||||
dprintln(" bLength: {}", descriptor.bLength);
|
||||
dprintln(" bDescriptorType: {}", descriptor.bDescriptorType);
|
||||
dprintln(" bInterfaceNumber: {}", descriptor.bInterfaceNumber);
|
||||
dprintln(" bAlternateSetting: {}", descriptor.bAlternateSetting);
|
||||
dprintln(" bNumEndpoints: {}", descriptor.bNumEndpoints);
|
||||
dprintln(" bInterfaceClass: {}", descriptor.bInterfaceClass);
|
||||
dprintln(" bInterfaceSubClass: {}", descriptor.bInterfaceSubClass);
|
||||
dprintln(" bInterfaceProtocol: {}", descriptor.bInterfaceProtocol);
|
||||
dprintln(" iInterface: {}", descriptor.iInterface);
|
||||
|
||||
for (const auto& endpoint : interface.endpoints)
|
||||
{
|
||||
const auto& descriptor = endpoint.descriptor;
|
||||
dprintln(" endpoint");
|
||||
dprintln(" bLength: {}", descriptor.bLength);
|
||||
dprintln(" bDescriptorType: {}", descriptor.bDescriptorType);
|
||||
dprintln(" bEndpointAddress: {}", descriptor.bEndpointAddress);
|
||||
dprintln(" bmAttributes: {}", descriptor.bmAttributes);
|
||||
dprintln(" wMaxPacketSize: {}", +descriptor.wMaxPacketSize);
|
||||
dprintln(" bInterval: {}", descriptor.bInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_descriptor.descriptor.bDeviceClass)
|
||||
{
|
||||
switch (static_cast<USB::DeviceBaseClass>(m_descriptor.descriptor.bDeviceClass))
|
||||
{
|
||||
case USB::DeviceBaseClass::CommunicationAndCDCControl:
|
||||
dprintln_if(DEBUG_USB, "Found CommunicationAndCDCControl device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::Hub:
|
||||
dprintln_if(DEBUG_USB, "Found Hub device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::BillboardDeviceClass:
|
||||
dprintln_if(DEBUG_USB, "Found BillboardDeviceClass device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::DiagnosticDevice:
|
||||
dprintln_if(DEBUG_USB, "Found DiagnosticDevice device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::Miscellaneous:
|
||||
dprintln_if(DEBUG_USB, "Found Miscellaneous device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::VendorSpecific:
|
||||
dprintln_if(DEBUG_USB, "Found VendorSpecific device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
default:
|
||||
dprintln_if(DEBUG_USB, "Invalid device base class {2H}", m_descriptor.descriptor.bDeviceClass);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
for (const auto& configuration : m_descriptor.configurations)
|
||||
{
|
||||
for (const auto& interface : configuration.interfaces)
|
||||
{
|
||||
switch (static_cast<USB::InterfaceBaseClass>(interface.descriptor.bInterfaceClass))
|
||||
{
|
||||
case USB::InterfaceBaseClass::Audio:
|
||||
dprintln_if(DEBUG_USB, "Found Audio interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::CommunicationAndCDCControl:
|
||||
dprintln_if(DEBUG_USB, "Found CommunicationAndCDCControl interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::HID:
|
||||
dprintln_if(DEBUG_USB, "Found HID interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Physical:
|
||||
dprintln_if(DEBUG_USB, "Found Physical interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Image:
|
||||
dprintln_if(DEBUG_USB, "Found Image interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Printer:
|
||||
dprintln_if(DEBUG_USB, "Found Printer interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::MassStorage:
|
||||
dprintln_if(DEBUG_USB, "Found MassStorage interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::CDCData:
|
||||
dprintln_if(DEBUG_USB, "Found CDCData interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::SmartCard:
|
||||
dprintln_if(DEBUG_USB, "Found SmartCard interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::ContentSecurity:
|
||||
dprintln_if(DEBUG_USB, "Found ContentSecurity interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Video:
|
||||
dprintln_if(DEBUG_USB, "Found Video interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::PersonalHealthcare:
|
||||
dprintln_if(DEBUG_USB, "Found PersonalHealthcare interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::AudioVideoDevice:
|
||||
dprintln_if(DEBUG_USB, "Found AudioVideoDevice interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::USBTypeCBridgeClass:
|
||||
dprintln_if(DEBUG_USB, "Found USBTypeCBridgeClass interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::USBBulkDisplayProtocolDeviceClass:
|
||||
dprintln_if(DEBUG_USB, "Found USBBulkDisplayProtocolDeviceClass interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::MCTPOverUSBProtocolEndpointDeviceClass:
|
||||
dprintln_if(DEBUG_USB, "Found MCTPOverUSBProtocolEndpointDeviceClass interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::I3CDeviceClass:
|
||||
dprintln_if(DEBUG_USB, "Found I3CDeviceClass interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::DiagnosticDevice:
|
||||
dprintln_if(DEBUG_USB, "Found DiagnosticDevice interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::WirelessController:
|
||||
dprintln_if(DEBUG_USB, "Found WirelessController interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Miscellaneous:
|
||||
dprintln_if(DEBUG_USB, "Found Miscellaneous interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::ApplicationSpecific:
|
||||
dprintln_if(DEBUG_USB, "Found ApplicationSpecific interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::VendorSpecific:
|
||||
dprintln_if(DEBUG_USB, "Found VendorSpecific interface");
|
||||
break;
|
||||
default:
|
||||
dprintln_if(DEBUG_USB, "Invalid interface base class {2H}", interface.descriptor.bInterfaceClass);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
USB::SpeedClass USBDevice::determine_speed_class(uint64_t bits_per_second)
|
||||
{
|
||||
if (bits_per_second <= 1'500'000)
|
||||
return USB::SpeedClass::LowSpeed;
|
||||
if (bits_per_second <= 12'000'000)
|
||||
return USB::SpeedClass::FullSpeed;
|
||||
else if (bits_per_second <= 480'000'000)
|
||||
return USB::SpeedClass::HighSpeed;
|
||||
return USB::SpeedClass::SuperSpeed;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
#include <BAN/UniqPtr.h>
|
||||
|
||||
#include <kernel/USB/USBManager.h>
|
||||
#include <kernel/USB/XHCI/Controller.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
static BAN::UniqPtr<USBManager> s_instance;
|
||||
|
||||
BAN::ErrorOr<void> USBManager::initialize()
|
||||
{
|
||||
ASSERT(!s_instance);
|
||||
auto manager = TRY(BAN::UniqPtr<USBManager>::create());
|
||||
s_instance = BAN::move(manager);
|
||||
return {};
|
||||
}
|
||||
|
||||
USBManager& USBManager::get()
|
||||
{
|
||||
ASSERT(s_instance);
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> USBManager::add_controller(PCI::Device& pci_device)
|
||||
{
|
||||
ASSERT(pci_device.class_code() == 0x0C);
|
||||
ASSERT(pci_device.subclass() == 0x03);
|
||||
|
||||
BAN::UniqPtr<USBController> controller;
|
||||
switch (pci_device.prog_if())
|
||||
{
|
||||
case 0x00:
|
||||
dprintln("Unsupported UHCI controller");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case 0x10:
|
||||
dprintln("Unsupported OHCI controller");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case 0x20:
|
||||
dprintln("Unsupported EHCI controller");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case 0x30:
|
||||
if (auto ret = XHCIController::initialize(pci_device); ret.is_error())
|
||||
dprintln("Could not initialize XHCI controller: {}", ret.error());
|
||||
else
|
||||
controller = ret.release_value();
|
||||
break;
|
||||
default:
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
TRY(m_controllers.push_back(BAN::move(controller)));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,507 @@
|
|||
#include <BAN/Bitcast.h>
|
||||
#include <BAN/StringView.h>
|
||||
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
#include <kernel/Process.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
#include <kernel/USB/XHCI/Controller.h>
|
||||
#include <kernel/USB/XHCI/Device.h>
|
||||
|
||||
#define DEBUG_XHCI 0
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
XHCIController::XHCIController(PCI::Device& pci_device)
|
||||
: m_pci_device(pci_device)
|
||||
{ }
|
||||
|
||||
XHCIController::~XHCIController()
|
||||
{
|
||||
if (m_port_updater)
|
||||
m_port_updater->exit(0, SIGKILL);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<XHCIController>> XHCIController::initialize(PCI::Device& pci_device)
|
||||
{
|
||||
auto controller = TRY(BAN::UniqPtr<XHCIController>::create(pci_device));
|
||||
TRY(controller->initialize_impl());
|
||||
return controller;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::initialize_impl()
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "XHCI controller at PCI {2H}:{2H}:{2H}", m_pci_device.bus(), m_pci_device.dev(), m_pci_device.func());
|
||||
|
||||
m_pci_device.enable_bus_mastering();
|
||||
m_pci_device.enable_memory_space();
|
||||
|
||||
m_configuration_bar = TRY(m_pci_device.allocate_bar_region(0));
|
||||
if (m_configuration_bar->type() != PCI::BarType::MEM)
|
||||
{
|
||||
dwarnln("XHCI controller with non-memory configuration space");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (auto ret = reset_controller(); ret.is_error())
|
||||
{
|
||||
dwarnln("Could not reset XHCI Controller: {}", ret.error());
|
||||
return ret.release_error();
|
||||
}
|
||||
|
||||
auto& capabilities = capability_regs();
|
||||
dprintln_if(DEBUG_XHCI, " version {H}.{H}.{H}",
|
||||
+capabilities.major_revision,
|
||||
capabilities.minor_revision >> 4,
|
||||
capabilities.minor_revision & 0x0F
|
||||
);
|
||||
dprintln_if(DEBUG_XHCI, " max slots {}", +capabilities.hcsparams1.max_slots);
|
||||
dprintln_if(DEBUG_XHCI, " max intrs {}", +capabilities.hcsparams1.max_interrupters);
|
||||
dprintln_if(DEBUG_XHCI, " max ports {}", +capabilities.hcsparams1.max_ports);
|
||||
|
||||
TRY(m_slots.resize(capabilities.hcsparams1.max_slots));
|
||||
TRY(m_ports.resize(capabilities.hcsparams1.max_ports));
|
||||
|
||||
TRY(initialize_ports());
|
||||
|
||||
auto& operational = operational_regs();
|
||||
|
||||
// allocate and program dcbaa
|
||||
m_dcbaa_region = TRY(DMARegion::create(capabilities.hcsparams1.max_slots * 8));
|
||||
memset(reinterpret_cast<void*>(m_dcbaa_region->vaddr()), 0, m_dcbaa_region->size());
|
||||
operational.dcbaap_lo = m_dcbaa_region->paddr() & 0xFFFFFFFF;
|
||||
operational.dcbaap_hi = m_dcbaa_region->paddr() >> 32;
|
||||
|
||||
// allocate and program crcr
|
||||
TRY(m_command_completions.resize(m_command_ring_trb_count));
|
||||
m_command_ring_region = TRY(DMARegion::create(m_command_ring_trb_count * sizeof(XHCI::TRB)));
|
||||
memset(reinterpret_cast<void*>(m_command_ring_region->vaddr()), 0, m_command_ring_region->size());
|
||||
operational.crcr_lo = m_command_ring_region->paddr() | XHCI::CRCR::RingCycleState;
|
||||
operational.crcr_hi = m_command_ring_region->paddr() >> 32;
|
||||
|
||||
TRY(initialize_primary_interrupter());
|
||||
|
||||
// enable the controller
|
||||
operational.usbcmd.run_stop = 1;
|
||||
while (operational.usbsts & XHCI::USBSTS::HCHalted)
|
||||
continue;
|
||||
|
||||
m_port_updater = Process::create_kernel([](void* data) { reinterpret_cast<XHCIController*>(data)->port_updater_task(); }, this);
|
||||
if (m_port_updater == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::initialize_ports()
|
||||
{
|
||||
auto& capabilities = capability_regs();
|
||||
uint8_t max_ports = capabilities.hcsparams1.max_ports;
|
||||
|
||||
ASSERT(m_ports.size() == max_ports);
|
||||
|
||||
{
|
||||
uint16_t ext_offset = capabilities.hccparams1.xhci_extended_capabilities_pointer;
|
||||
if (ext_offset == 0)
|
||||
{
|
||||
dwarnln("XHCI controller does not have extended capabilities");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
vaddr_t ext_addr = m_configuration_bar->vaddr() + ext_offset * 4;
|
||||
while (true)
|
||||
{
|
||||
auto& ext_cap = *reinterpret_cast<volatile XHCI::ExtendedCap*>(ext_addr);
|
||||
|
||||
if (ext_cap.capability_id == XHCI::ExtendedCapabilityID::SupportedProtocol)
|
||||
{
|
||||
auto& protocol = reinterpret_cast<volatile XHCI::SupportedPrococolCap&>(ext_cap);
|
||||
|
||||
if (protocol.name_string != *reinterpret_cast<const uint32_t*>("USB "))
|
||||
{
|
||||
dwarnln("Invalid port protocol name string");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
if (protocol.compatible_port_offset == 0 || protocol.compatible_port_offset + protocol.compatible_port_count - 1 > max_ports)
|
||||
{
|
||||
dwarnln("Invalid port specified in SupportedProtocols");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < protocol.compatible_port_count; i++)
|
||||
{
|
||||
auto& port = m_ports[protocol.compatible_port_offset + i - 1];
|
||||
port.revision_major = protocol.major_revision;
|
||||
port.revision_minor = protocol.minor_revision;
|
||||
port.slot_type = protocol.protocol_slot_type;
|
||||
|
||||
for (size_t j = 0; j < protocol.protocol_speed_id_count; j++)
|
||||
{
|
||||
uint32_t speed_info = reinterpret_cast<const volatile uint32_t*>(ext_addr + sizeof(XHCI::SupportedPrococolCap))[j];
|
||||
uint32_t port_speed = speed_info >> 16;
|
||||
for (size_t exp = 0; exp < ((speed_info >> 4) & 0x03); exp++)
|
||||
port_speed *= 1000;
|
||||
port.speed_id_to_speed[speed_info & 0x0F] = port_speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ext_cap.next_capability == 0)
|
||||
break;
|
||||
ext_addr += ext_cap.next_capability * 4;
|
||||
}
|
||||
}
|
||||
|
||||
// set max slots enabled
|
||||
auto& operational = operational_regs();
|
||||
operational.config.max_device_slots_enabled = capabilities.hcsparams1.max_slots;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::initialize_primary_interrupter()
|
||||
{
|
||||
TRY(m_pci_device.reserve_irqs(1));
|
||||
|
||||
auto& runtime = runtime_regs();
|
||||
|
||||
static constexpr size_t event_ring_table_offset = m_event_ring_trb_count * sizeof(XHCI::TRB);
|
||||
|
||||
m_event_ring_region = TRY(DMARegion::create(m_event_ring_trb_count * sizeof(XHCI::TRB) + sizeof(XHCI::EventRingTableEntry)));
|
||||
memset(reinterpret_cast<void*>(m_event_ring_region->vaddr()), 0, m_event_ring_region->size());
|
||||
|
||||
auto& event_ring_table_entry = *reinterpret_cast<XHCI::EventRingTableEntry*>(m_event_ring_region->vaddr() + event_ring_table_offset);
|
||||
event_ring_table_entry.rsba = m_event_ring_region->paddr();
|
||||
event_ring_table_entry.rsz = m_event_ring_trb_count;
|
||||
|
||||
auto& primary_interrupter = runtime.irs[0];
|
||||
primary_interrupter.erstsz = 1;
|
||||
primary_interrupter.erdp = m_event_ring_region->paddr();
|
||||
primary_interrupter.erstba = m_event_ring_region->paddr() + event_ring_table_offset;
|
||||
|
||||
auto& operational = operational_regs();
|
||||
operational.usbcmd.interrupter_enable = 1;
|
||||
|
||||
primary_interrupter.iman = primary_interrupter.iman | XHCI::IMAN::InterruptPending | XHCI::IMAN::InterruptEnable;
|
||||
|
||||
set_irq(m_pci_device.get_irq(0));
|
||||
enable_interrupt();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Mutex s_port_mutex;
|
||||
|
||||
void XHCIController::port_updater_task()
|
||||
{
|
||||
// allow initial pass of port iteration because controller
|
||||
// does not send Port Status Change event for already
|
||||
// attached ports
|
||||
m_port_changed = true;
|
||||
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
bool expected { true };
|
||||
while (!m_port_changed.compare_exchange(expected, false))
|
||||
{
|
||||
m_port_semaphore.block_with_timeout(100);
|
||||
expected = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_ports.size(); i++)
|
||||
{
|
||||
LockGuard _(s_port_mutex);
|
||||
|
||||
auto& my_port = m_ports[i];
|
||||
if (my_port.revision_major == 0)
|
||||
continue;
|
||||
|
||||
auto& op_port = operational_regs().ports[i];
|
||||
|
||||
if (!(op_port.portsc & XHCI::PORTSC::PP))
|
||||
continue;
|
||||
|
||||
// read and clear needed change flags
|
||||
const bool reset_change = op_port.portsc & XHCI::PORTSC::PRC;
|
||||
const bool connection_change = op_port.portsc & XHCI::PORTSC::CSC;
|
||||
op_port.portsc = XHCI::PORTSC::CSC | XHCI::PORTSC::PRC | XHCI::PORTSC::PP;
|
||||
|
||||
if (!(op_port.portsc & XHCI::PORTSC::CCS))
|
||||
{
|
||||
// if device detached, clear the port
|
||||
if (my_port.slot_id != 0)
|
||||
{
|
||||
m_slots[my_port.slot_id - 1].clear();
|
||||
my_port.slot_id = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (my_port.revision_major)
|
||||
{
|
||||
case 2:
|
||||
if (!reset_change)
|
||||
{
|
||||
if (connection_change)
|
||||
op_port.portsc = XHCI::PORTSC::PR | XHCI::PORTSC::PP;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!connection_change)
|
||||
continue;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto ret = initialize_slot(i); ret.is_error())
|
||||
{
|
||||
dwarnln("Could not initialize USB {H}.{H} device: {}",
|
||||
my_port.revision_major,
|
||||
my_port.revision_minor >> 4,
|
||||
ret.error()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::initialize_slot(int port_index)
|
||||
{
|
||||
auto& my_port = m_ports[port_index];
|
||||
|
||||
XHCI::TRB enable_slot { .enable_slot_command {} };
|
||||
enable_slot.enable_slot_command.trb_type = XHCI::TRBType::EnableSlotCommand;
|
||||
enable_slot.enable_slot_command.slot_type = my_port.slot_type;
|
||||
auto result = TRY(send_command(enable_slot));
|
||||
|
||||
uint8_t slot_id = result.command_completion_event.slot_id;
|
||||
if (slot_id == 0 || slot_id > capability_regs().hcsparams1.max_slots)
|
||||
{
|
||||
dwarnln("EnableSlot gave an invalid slot {}", slot_id);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
dprintln_if(DEBUG_XHCI, "allocated slot {} for port {}", slot_id, port_index + 1);
|
||||
|
||||
m_slots[slot_id - 1] = TRY(XHCIDevice::create(*this, port_index + 1, slot_id));
|
||||
if (auto ret = m_slots[slot_id - 1]->initialize(); ret.is_error())
|
||||
{
|
||||
dwarnln("Could not initialize device on slot {}: {}", slot_id, ret.error());
|
||||
m_slots[slot_id - 1].clear();
|
||||
return ret.release_error();
|
||||
}
|
||||
|
||||
my_port.slot_id = slot_id;
|
||||
|
||||
dprintln_if(DEBUG_XHCI, "device on slot {} initialized", slot_id);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::reset_controller()
|
||||
{
|
||||
auto& operational = operational_regs();
|
||||
|
||||
const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + 500;
|
||||
|
||||
// wait until controller not ready clears
|
||||
while (operational.usbsts & XHCI::USBSTS::ControllerNotReady)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
|
||||
// issue software reset and wait for it to clear
|
||||
operational.usbcmd.host_controller_reset = 1;
|
||||
while (operational.usbcmd.host_controller_reset)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<XHCI::TRB> XHCIController::send_command(const XHCI::TRB& trb)
|
||||
{
|
||||
LockGuard _(m_mutex);
|
||||
|
||||
auto& command_trb = reinterpret_cast<volatile XHCI::TRB*>(m_command_ring_region->vaddr())[m_command_enqueue];
|
||||
command_trb.raw.dword0 = trb.raw.dword0;
|
||||
command_trb.raw.dword1 = trb.raw.dword1;
|
||||
command_trb.raw.dword2 = trb.raw.dword2;
|
||||
command_trb.raw.dword3 = trb.raw.dword3;
|
||||
command_trb.cycle = m_command_cycle;
|
||||
|
||||
auto& completion_trb = const_cast<volatile XHCI::TRB&>(m_command_completions[m_command_enqueue]);
|
||||
completion_trb.raw.dword0 = 0;
|
||||
completion_trb.raw.dword1 = 0;
|
||||
completion_trb.raw.dword2 = 0;
|
||||
completion_trb.raw.dword3 = 0;
|
||||
|
||||
advance_command_enqueue();
|
||||
|
||||
doorbell_reg(0) = 0;
|
||||
|
||||
uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + 1000;
|
||||
while ((__atomic_load_n(&completion_trb.raw.dword2, __ATOMIC_SEQ_CST) >> 24) == 0)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
|
||||
if (completion_trb.command_completion_event.completion_code != 1)
|
||||
{
|
||||
dwarnln("Completion error: {}", +completion_trb.command_completion_event.completion_code);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
return BAN::bit_cast<XHCI::TRB>(completion_trb);
|
||||
}
|
||||
|
||||
void XHCIController::advance_command_enqueue()
|
||||
{
|
||||
m_command_enqueue++;
|
||||
if (m_command_enqueue < m_command_ring_trb_count - 1)
|
||||
return;
|
||||
|
||||
auto& link_trb = reinterpret_cast<volatile XHCI::TRB*>(m_command_ring_region->vaddr())[m_command_enqueue].link_trb;
|
||||
link_trb.trb_type = XHCI::TRBType::Link;
|
||||
link_trb.ring_segment_ponter = m_command_ring_region->paddr();
|
||||
link_trb.interrupter_target = 0;
|
||||
link_trb.cycle_bit = m_command_cycle;
|
||||
link_trb.toggle_cycle = 1;
|
||||
link_trb.chain_bit = 0;
|
||||
link_trb.interrupt_on_completion = 0;
|
||||
|
||||
m_command_enqueue = 0;
|
||||
m_command_cycle = !m_command_cycle;
|
||||
}
|
||||
|
||||
void XHCIController::handle_irq()
|
||||
{
|
||||
auto& operational = operational_regs();
|
||||
if (!(operational.usbsts & XHCI::USBSTS::EventInterrupt))
|
||||
return;
|
||||
operational.usbsts = XHCI::USBSTS::EventInterrupt;
|
||||
|
||||
auto& primary_interrupter = runtime_regs().irs[0];
|
||||
primary_interrupter.iman = primary_interrupter.iman | XHCI::IMAN::InterruptPending | XHCI::IMAN::InterruptEnable;
|
||||
|
||||
if (current_event_trb().cycle == m_event_cycle)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
auto& trb = current_event_trb();
|
||||
if (trb.cycle != m_event_cycle)
|
||||
break;
|
||||
|
||||
switch (trb.trb_type)
|
||||
{
|
||||
case XHCI::TRBType::TransferEvent:
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "TransferEvent");
|
||||
|
||||
const uint32_t slot_id = trb.transfer_event.slot_id;
|
||||
if (slot_id == 0 || slot_id > m_slots.size() || !m_slots[slot_id - 1])
|
||||
{
|
||||
dwarnln("TransferEvent for invalid slot {}", slot_id);
|
||||
dwarnln("Completion error: {}", +trb.transfer_event.completion_code);
|
||||
break;
|
||||
}
|
||||
|
||||
m_slots[slot_id - 1]->on_transfer_event(trb);
|
||||
|
||||
break;
|
||||
}
|
||||
case XHCI::TRBType::CommandCompletionEvent:
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "CommandCompletionEvent");
|
||||
|
||||
const uint32_t trb_index = (trb.command_completion_event.command_trb_pointer - m_command_ring_region->paddr()) / sizeof(XHCI::TRB);
|
||||
|
||||
// NOTE: dword2 is last (and atomic) as that is what send_command is waiting for
|
||||
auto& completion_trb = const_cast<volatile XHCI::TRB&>(m_command_completions[trb_index]);
|
||||
completion_trb.raw.dword0 = trb.raw.dword0;
|
||||
completion_trb.raw.dword1 = trb.raw.dword1;
|
||||
completion_trb.raw.dword3 = trb.raw.dword3;
|
||||
__atomic_store_n(&completion_trb.raw.dword2, trb.raw.dword2, __ATOMIC_SEQ_CST);
|
||||
|
||||
break;
|
||||
}
|
||||
case XHCI::TRBType::PortStatusChangeEvent:
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "PortStatusChangeEvent");
|
||||
uint8_t port_id = trb.port_status_chage_event.port_id;
|
||||
if (port_id > capability_regs().hcsparams1.max_ports)
|
||||
{
|
||||
dwarnln("PortStatusChangeEvent on non-existent port {}", port_id);
|
||||
break;
|
||||
}
|
||||
m_port_changed = true;
|
||||
m_port_semaphore.unblock();
|
||||
break;
|
||||
}
|
||||
case XHCI::TRBType::BandwidthRequestEvent:
|
||||
dwarnln("Unhandled BandwidthRequestEvent");
|
||||
break;
|
||||
case XHCI::TRBType::DoorbellEvent:
|
||||
dwarnln("Unhandled DoorbellEvent");
|
||||
break;
|
||||
case XHCI::TRBType::HostControllerEvent:
|
||||
dwarnln("Unhandled HostControllerEvent");
|
||||
break;
|
||||
case XHCI::TRBType::DeviceNotificationEvent:
|
||||
dwarnln("Unhandled DeviceNotificationEvent");
|
||||
break;
|
||||
case XHCI::TRBType::MFINDEXWrapEvent:
|
||||
dwarnln("Unhandled MFINDEXWrapEvent");
|
||||
break;
|
||||
default:
|
||||
dwarnln("Unrecognized event TRB type {}", +trb.trb_type);
|
||||
break;
|
||||
}
|
||||
|
||||
m_event_dequeue++;
|
||||
if (m_event_dequeue >= m_event_ring_trb_count)
|
||||
{
|
||||
m_event_dequeue = 0;
|
||||
m_event_cycle = !m_event_cycle;
|
||||
|
||||
derrorln("WRAP");
|
||||
}
|
||||
}
|
||||
|
||||
primary_interrupter.erdp = (m_event_ring_region->paddr() + (m_event_dequeue * sizeof(XHCI::TRB))) | XHCI::ERDP::EventHandlerBusy;
|
||||
}
|
||||
}
|
||||
|
||||
volatile XHCI::CapabilityRegs& XHCIController::capability_regs()
|
||||
{
|
||||
return *reinterpret_cast<volatile XHCI::CapabilityRegs*>(m_configuration_bar->vaddr());
|
||||
}
|
||||
|
||||
volatile XHCI::OperationalRegs& XHCIController::operational_regs()
|
||||
{
|
||||
return *reinterpret_cast<volatile XHCI::OperationalRegs*>(m_configuration_bar->vaddr() + capability_regs().caplength);
|
||||
}
|
||||
|
||||
volatile XHCI::RuntimeRegs& XHCIController::runtime_regs()
|
||||
{
|
||||
return *reinterpret_cast<volatile XHCI::RuntimeRegs*>(m_configuration_bar->vaddr() + (capability_regs().rstoff & ~0x1Fu));
|
||||
}
|
||||
|
||||
volatile uint32_t& XHCIController::doorbell_reg(uint32_t slot_id)
|
||||
{
|
||||
return reinterpret_cast<volatile uint32_t*>(m_configuration_bar->vaddr() + capability_regs().dboff)[slot_id];
|
||||
}
|
||||
|
||||
const volatile XHCI::TRB& XHCIController::current_event_trb()
|
||||
{
|
||||
return reinterpret_cast<const volatile XHCI::TRB*>(m_event_ring_region->vaddr())[m_event_dequeue];;
|
||||
}
|
||||
|
||||
volatile uint64_t& XHCIController::dcbaa_reg(uint32_t slot_id)
|
||||
{
|
||||
return reinterpret_cast<volatile uint64_t*>(m_dcbaa_region->vaddr())[slot_id];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
#include <BAN/Bitcast.h>
|
||||
#include <BAN/ByteSpan.h>
|
||||
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
#include <kernel/USB/XHCI/Device.h>
|
||||
|
||||
#define DEBUG_XHCI 0
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<XHCIDevice>> XHCIDevice::create(XHCIController& controller, uint32_t port_id, uint32_t slot_id)
|
||||
{
|
||||
return TRY(BAN::UniqPtr<XHCIDevice>::create(controller, port_id, slot_id));
|
||||
}
|
||||
|
||||
XHCIDevice::~XHCIDevice()
|
||||
{
|
||||
XHCI::TRB disable_slot { .disable_slot_command {} };
|
||||
disable_slot.disable_slot_command.trb_type = XHCI::TRBType::DisableSlotCommand;
|
||||
disable_slot.disable_slot_command.slot_id = m_slot_id;
|
||||
if (auto ret = m_controller.send_command(disable_slot); ret.is_error())
|
||||
dwarnln("Could not disable slot {}: {}", m_slot_id, ret.error());
|
||||
else
|
||||
dprintln_if(DEBUG_XHCI, "Slot {} disabled", m_slot_id);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIDevice::initialize_control_endpoint()
|
||||
{
|
||||
const uint32_t context_size = m_controller.context_size_set() ? 64 : 32;
|
||||
|
||||
const uint32_t portsc = m_controller.operational_regs().ports[m_port_id - 1].portsc;
|
||||
const uint32_t speed_id = (portsc >> XHCI::PORTSC::PORT_SPEED_SHIFT) & XHCI::PORTSC::PORT_SPEED_MASK;
|
||||
const uint32_t bits_per_second = m_controller.port(m_port_id).speed_id_to_speed[speed_id];
|
||||
const auto speed_class = determine_speed_class(bits_per_second);
|
||||
|
||||
m_max_packet_size = 0;
|
||||
switch (speed_class)
|
||||
{
|
||||
case USB::SpeedClass::LowSpeed:
|
||||
case USB::SpeedClass::FullSpeed:
|
||||
m_max_packet_size = 8;
|
||||
break;
|
||||
case USB::SpeedClass::HighSpeed:
|
||||
m_max_packet_size = 64;
|
||||
break;
|
||||
case USB::SpeedClass::SuperSpeed:
|
||||
m_max_packet_size = 512;
|
||||
break;
|
||||
default: ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
m_input_context = TRY(DMARegion::create(33 * context_size));
|
||||
memset(reinterpret_cast<void*>(m_input_context->vaddr()), 0, m_input_context->size());
|
||||
|
||||
m_output_context = TRY(DMARegion::create(32 * context_size));
|
||||
memset(reinterpret_cast<void*>(m_output_context->vaddr()), 0, m_output_context->size());
|
||||
|
||||
m_endpoints[0].transfer_ring = TRY(DMARegion::create(m_transfer_ring_trb_count * sizeof(XHCI::TRB)));
|
||||
memset(reinterpret_cast<void*>(m_endpoints[0].transfer_ring->vaddr()), 0, m_endpoints[0].transfer_ring->size());
|
||||
|
||||
{
|
||||
auto& input_control_context = *reinterpret_cast<XHCI::InputControlContext*>(m_input_context->vaddr() + 0 * context_size);
|
||||
auto& slot_context = *reinterpret_cast<XHCI::SlotContext*> (m_input_context->vaddr() + 1 * context_size);
|
||||
auto& endpoint0_context = *reinterpret_cast<XHCI::EndpointContext*> (m_input_context->vaddr() + 2 * context_size);
|
||||
|
||||
input_control_context.add_context_flags = 0b11;
|
||||
|
||||
slot_context.root_hub_port_number = m_port_id;
|
||||
slot_context.route_string = 0;
|
||||
slot_context.context_entries = 1;
|
||||
slot_context.interrupter_target = 0;
|
||||
slot_context.speed = speed_id;
|
||||
|
||||
endpoint0_context.endpoint_type = XHCI::EndpointType::Control;
|
||||
endpoint0_context.max_packet_size = m_max_packet_size;
|
||||
endpoint0_context.max_burst_size = 0;
|
||||
endpoint0_context.tr_dequeue_pointer = m_endpoints[0].transfer_ring->paddr() | 1;
|
||||
endpoint0_context.interval = 0;
|
||||
endpoint0_context.max_primary_streams = 0;
|
||||
endpoint0_context.mult = 0;
|
||||
endpoint0_context.error_count = 3;
|
||||
}
|
||||
|
||||
m_controller.dcbaa_reg(m_slot_id) = m_output_context->paddr();
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
XHCI::TRB address_device { .address_device_command = {} };
|
||||
address_device.address_device_command.trb_type = XHCI::TRBType::AddressDeviceCommand;
|
||||
address_device.address_device_command.input_context_pointer = m_input_context->paddr();
|
||||
// NOTE: some legacy devices require sending request with BSR=1 before actual BSR=0
|
||||
address_device.address_device_command.block_set_address_request = (i == 0);
|
||||
address_device.address_device_command.slot_id = m_slot_id;
|
||||
TRY(m_controller.send_command(address_device));
|
||||
}
|
||||
|
||||
// NOTE: Full speed devices can have other max packet sizes than 8
|
||||
if (speed_class == USB::SpeedClass::FullSpeed)
|
||||
TRY(update_actual_max_packet_size());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIDevice::update_actual_max_packet_size()
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "Retrieving actual max packet size of full speed device");
|
||||
|
||||
BAN::Vector<uint8_t> buffer;
|
||||
TRY(buffer.resize(8, 0));
|
||||
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Standard | USB::RequestType::Device;
|
||||
request.bRequest = USB::Request::GET_DESCRIPTOR;
|
||||
request.wValue = 0x0100;
|
||||
request.wIndex = 0;
|
||||
request.wLength = 8;
|
||||
TRY(send_request(request, kmalloc_paddr_of((vaddr_t)buffer.data()).value()));
|
||||
|
||||
m_max_packet_size = buffer.back();
|
||||
|
||||
const uint32_t context_size = m_controller.context_size_set() ? 64 : 32;
|
||||
|
||||
{
|
||||
auto& input_control_context = *reinterpret_cast<XHCI::InputControlContext*>(m_input_context->vaddr() + 0 * context_size);
|
||||
auto& endpoint0_context = *reinterpret_cast<XHCI::EndpointContext*> (m_input_context->vaddr() + 2 * context_size);
|
||||
|
||||
input_control_context.add_context_flags = 0b10;
|
||||
|
||||
endpoint0_context.endpoint_type = XHCI::EndpointType::Control;
|
||||
endpoint0_context.max_packet_size = m_max_packet_size;
|
||||
endpoint0_context.max_burst_size = 0;
|
||||
endpoint0_context.tr_dequeue_pointer = (m_endpoints[0].transfer_ring->paddr() + (m_endpoints[0].enqueue_index * sizeof(XHCI::TRB))) | 1;
|
||||
endpoint0_context.interval = 0;
|
||||
endpoint0_context.max_primary_streams = 0;
|
||||
endpoint0_context.mult = 0;
|
||||
endpoint0_context.error_count = 3;
|
||||
}
|
||||
|
||||
XHCI::TRB evaluate_context { .address_device_command = {} };
|
||||
evaluate_context.address_device_command.trb_type = XHCI::TRBType::EvaluateContextCommand;
|
||||
evaluate_context.address_device_command.input_context_pointer = m_input_context->paddr();
|
||||
evaluate_context.address_device_command.block_set_address_request = 0;
|
||||
evaluate_context.address_device_command.slot_id = m_slot_id;
|
||||
TRY(m_controller.send_command(evaluate_context));
|
||||
|
||||
dprintln_if(DEBUG_XHCI, "successfully updated max packet size to {}", m_max_packet_size);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void XHCIDevice::on_transfer_event(const volatile XHCI::TRB& trb)
|
||||
{
|
||||
ASSERT(trb.trb_type == XHCI::TRBType::TransferEvent);
|
||||
if (trb.transfer_event.endpoint_id == 0)
|
||||
{
|
||||
dwarnln("TransferEvent for endpoint id 0");
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: dword2 is last (and atomic) as that is what send_request is waiting for
|
||||
auto& completion_trb = m_endpoints[trb.transfer_event.endpoint_id - 1].completion_trb;
|
||||
completion_trb.raw.dword0 = trb.raw.dword0;
|
||||
completion_trb.raw.dword1 = trb.raw.dword1;
|
||||
completion_trb.raw.dword3 = trb.raw.dword3;
|
||||
__atomic_store_n(&completion_trb.raw.dword2, trb.raw.dword2, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIDevice::send_request(const USBDeviceRequest& request, paddr_t buffer_paddr)
|
||||
{
|
||||
// minus 3: Setup, Status, Link
|
||||
if (request.wLength > (m_transfer_ring_trb_count - 3) * m_max_packet_size)
|
||||
return BAN::Error::from_errno((ENOBUFS));
|
||||
|
||||
auto& endpoint = m_endpoints[0];
|
||||
LockGuard _(endpoint.mutex);
|
||||
|
||||
auto* transfer_trb_arr = reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr());
|
||||
|
||||
{
|
||||
auto& trb = transfer_trb_arr[endpoint.enqueue_index];
|
||||
memset((void*)&trb, 0, sizeof(XHCI::TRB));
|
||||
|
||||
trb.setup_stage.trb_type = XHCI::TRBType::SetupStage;
|
||||
trb.setup_stage.transfer_type = 3;
|
||||
trb.setup_stage.trb_transfer_length = 8;
|
||||
trb.setup_stage.interrupt_on_completion = 0;
|
||||
trb.setup_stage.immediate_data = 1;
|
||||
trb.setup_stage.cycle_bit = endpoint.cycle_bit;
|
||||
|
||||
trb.setup_stage.bmRequestType = request.bmRequestType;
|
||||
trb.setup_stage.bRequest = request.bRequest;
|
||||
trb.setup_stage.wValue = request.wValue;
|
||||
trb.setup_stage.wIndex = request.wIndex;
|
||||
trb.setup_stage.wLength = request.wLength;
|
||||
|
||||
advance_endpoint_enqueue(endpoint, false);
|
||||
}
|
||||
|
||||
uint32_t bytes_handled = 0;
|
||||
while (bytes_handled < request.wLength)
|
||||
{
|
||||
const uint32_t to_handle = BAN::Math::min<uint32_t>(m_max_packet_size, request.wLength - bytes_handled);
|
||||
|
||||
auto& trb = transfer_trb_arr[endpoint.enqueue_index];
|
||||
memset((void*)&trb, 0, sizeof(XHCI::TRB));
|
||||
|
||||
trb.data_stage.trb_type = XHCI::TRBType::DataStage;
|
||||
trb.data_stage.direction = 1;
|
||||
trb.data_stage.trb_transfer_length = to_handle;
|
||||
trb.data_stage.chain_bit = (bytes_handled + to_handle < request.wLength);
|
||||
trb.data_stage.interrupt_on_completion = 0;
|
||||
trb.data_stage.immediate_data = 0;
|
||||
trb.data_stage.data_buffer_pointer = buffer_paddr + bytes_handled;
|
||||
trb.data_stage.cycle_bit = endpoint.cycle_bit;
|
||||
|
||||
bytes_handled += to_handle;
|
||||
|
||||
advance_endpoint_enqueue(endpoint, false);
|
||||
}
|
||||
|
||||
{
|
||||
auto& trb = transfer_trb_arr[endpoint.enqueue_index];
|
||||
memset((void*)&trb, 0, sizeof(XHCI::TRB));
|
||||
|
||||
trb.status_stage.trb_type = XHCI::TRBType::StatusStage;
|
||||
trb.status_stage.direction = 0;
|
||||
trb.status_stage.chain_bit = 0;
|
||||
trb.status_stage.interrupt_on_completion = 1;
|
||||
trb.data_stage.cycle_bit = endpoint.cycle_bit;
|
||||
|
||||
advance_endpoint_enqueue(endpoint, false);
|
||||
}
|
||||
|
||||
auto& completion_trb = endpoint.completion_trb;
|
||||
completion_trb.raw.dword0 = 0;
|
||||
completion_trb.raw.dword1 = 0;
|
||||
completion_trb.raw.dword2 = 0;
|
||||
completion_trb.raw.dword3 = 0;
|
||||
|
||||
m_controller.doorbell_reg(m_slot_id) = 1;
|
||||
|
||||
const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + 1000;
|
||||
while ((__atomic_load_n(&completion_trb.raw.dword2, __ATOMIC_SEQ_CST) >> 24) == 0)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
|
||||
if (completion_trb.transfer_event.completion_code != 1)
|
||||
{
|
||||
dwarnln("Completion error: {}", +completion_trb.transfer_event.completion_code);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void XHCIDevice::advance_endpoint_enqueue(Endpoint& endpoint, bool chain)
|
||||
{
|
||||
endpoint.enqueue_index++;
|
||||
if (endpoint.enqueue_index < m_transfer_ring_trb_count - 1)
|
||||
return;
|
||||
|
||||
// This is the last TRB in transfer ring. Make it link to the beginning of the ring
|
||||
auto& link_trb = reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr())[endpoint.enqueue_index].link_trb;
|
||||
link_trb.trb_type = XHCI::TRBType::Link;
|
||||
link_trb.ring_segment_ponter = endpoint.transfer_ring->paddr();
|
||||
link_trb.interrupter_target = 0;
|
||||
link_trb.cycle_bit = endpoint.cycle_bit;
|
||||
link_trb.toggle_cycle = 1;
|
||||
link_trb.chain_bit = chain;
|
||||
link_trb.interrupt_on_completion = 0;
|
||||
|
||||
endpoint.enqueue_index = 0;
|
||||
endpoint.cycle_bit = !endpoint.cycle_bit;
|
||||
}
|
||||
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
#include <kernel/Terminal/Serial.h>
|
||||
#include <kernel/Terminal/VirtualTTY.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
#include <kernel/USB/USBManager.h>
|
||||
|
||||
#include <LibInput/KeyboardLayout.h>
|
||||
|
||||
|
@ -205,6 +206,8 @@ static void init2(void*)
|
|||
|
||||
MUST(NetworkManager::initialize());
|
||||
|
||||
MUST(USBManager::initialize());
|
||||
|
||||
// NOTE: PCI devices are the last ones to be initialized
|
||||
// so other devices can reserve predefined interrupts
|
||||
PCI::PCIManager::get().initialize_devices();
|
||||
|
|
Loading…
Reference in New Issue