diff --git a/Kernel/Bus/USB/EHCI/DataStructures.h b/Kernel/Bus/USB/EHCI/DataStructures.h new file mode 100644 index 0000000000..ee4114c3aa --- /dev/null +++ b/Kernel/Bus/USB/EHCI/DataStructures.h @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2023, Leon Albrecht + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Kernel::USB::EHCI { + +// https://www.intel.com/content/www/us/en/products/docs/io/universal-serial-bus/ehci-specification-for-usb.html +// Section 3 (32 bit structures)and Appendix B (64 bit structures) + +// Table 3-1 Typ Field Value Definitions +enum class Typ : u8 { + iTD = 0b00, + QH = 0b01, + siTD = 0b10, + FSTN = 0b11 +}; + +struct IsochronousTransferDescriptor; +struct SplitTransactionIsochronousTransferDescriptor; +struct QueueElementTransferDescriptor; +struct QueueHead; +struct FrameSpanTraversalNode; + +// 3.1 Periodic Frame List +// Also for "3.3.1 Next Link Pointer" and the like +union FrameListElementPointer { + u32 link_pointer; + struct { + u32 terminate : 1; + Typ typ : 2; + u32 zero : 2; + u32 link_pointer_hi : 27; + }; + + template + static FrameListElementPointer make(PhysicalPtr addr); + template<> + FrameListElementPointer make(PhysicalPtr addr) + { + VERIFY((addr & 0b11111) == 0); + VERIFY(addr == (u32)addr); + return { .terminate = 0, .typ = Typ::iTD, .zero = 0, .link_pointer_hi = (u32)addr >> 5 }; + } + template<> + FrameListElementPointer make(PhysicalPtr addr) + { + VERIFY((addr & 0b11111) == 0); + VERIFY(addr == (u32)addr); + return { .terminate = 0, .typ = Typ::siTD, .zero = 0, .link_pointer_hi = (u32)addr >> 5 }; + } + template<> + FrameListElementPointer make(PhysicalPtr addr) + { + VERIFY((addr & 0b11111) == 0); + VERIFY(addr == (u32)addr); + return { .terminate = 0, .typ = Typ::QH, .zero = 0, .link_pointer_hi = (u32)addr >> 5 }; + } + template<> + FrameListElementPointer make(PhysicalPtr addr) + { + VERIFY((addr & 0b11111) == 0); + VERIFY(addr == (u32)addr); + return { .terminate = 0, .typ = Typ::FSTN, .zero = 0, .link_pointer_hi = (u32)addr >> 5 }; + } +}; + +// 3.3 Isochronous (High-Speed) Transfer Descriptor (iTD) +struct IsochronousTransferDescriptor { + FrameListElementPointer next_link_pointer; + // 3.3.2 iTD Transaction Status and Control List + struct TransactionStatusControl { + u32 transaction_x_offset : 11; // (RW) + u32 page_select : 3; // (RW) + u32 interrupt_on_complete : 1; + u32 transaction_x_length : 12; // RW + // Status Bit Field: // RW + u32 transaction_error : 1; + u32 babble_detected : 1; + u32 data_buffer_error : 1; + u32 active : 1; + } transaction_status_and_control[8]; + + // 3.3.3 iTD Buffer Page Pointer List (Plus) + union { + struct { + u32 reserved : 12; + u32 pointer_hi : 20; + } buffer_pointer_list[7]; + struct { + u32 device_address : 7; + u32 : 1; + u32 endpoint_number : 4; + u32 : 20; + + u32 maximum_packet_size : 11; + u32 direction : 1; + u32 : 20; + + u32 transactions_per_micro_frame : 2; // Multi + u32 : 10; + u32 : 20; + + u32 _[4]; + }; + }; +}; +static_assert(AssertSize()); + +struct IsochronousTransferDescriptor64 : public IsochronousTransferDescriptor { + u32 extended_buffer_pointer_list[7]; +}; +static_assert(AssertSize()); + +// 3.4 Split Transaction Isochronous Transfer Descriptor (siTD) +struct SplitTransactionIsochronousTransferDescriptor { + FrameListElementPointer next_link_pointer; + // 3.4.2 siTD Endpoint Capabilities/Characteristics + // Table 3-9. Endpoint and Transaction Translator Characteristics + struct { + u8 device_address : 6; + u8 reserved0 : 1 { 0 }; + u8 endpoint_number : 4; + u8 reserved1 : 4 { 0 }; + u8 hub_address : 7; + u8 reserved2 : 1 { 0 }; + u8 port_number : 7; + u8 direction : 1; + }; + // Table 3-10. Micro-frame Schedule Control + struct { + u8 split_start_mask : 8; + u8 split_completion_mask : 8; + u16 reserved { 0 }; + } schedule_control; + + // 3.4.3 siTD Transfer State + struct { + struct Status { + u8 reserved : 1 { 0 }; + u8 split_transaction_state : 1; + u8 missed_micro_frame : 1; + u8 transaction_error : 1; + u8 babble_detected : 1; + u8 data_buffer_error : 1; + u8 err : 1; + u8 active : 1; + } status; // RW + u8 micro_frame_complete_split_progress_mask; // RW + u16 total_bytes_to_transfer : 10; // RW + u16 reserved : 4; // RW + u16 page_select : 1; // RW + u16 interrupt_on_complete : 1; + } status_and_control; + + // 3.4.4 siTD Buffer Pointer List (plus) + enum class TransactionPosition : u32 { + All = 0b00, + Begin = 0b01, + Mid = 0b10, + End = 0b11 + }; + union { + struct { + u32 reserved : 12; + u32 pointer_hi : 20; + } buffer_pointer_list[2]; + struct { + u32 current_offset : 12; // RW + u32 : 20; + u32 transaction_count : 3; // RW + TransactionPosition transaction_position : 2; // RW + u32 reserved : 7 { 0 }; + u32 : 20; + }; + }; + // 3.4.5 siTD Back Link Pointer + struct { + u32 terminate : 1; + u32 reserved : 4 { 0 }; + u32 back_pointer_hi : 27; + } back_link_pointer; +}; +static_assert(AssertSize()); + +struct SplitTransactionIsochronousTransferDescriptor64 : public SplitTransactionIsochronousTransferDescriptor { + u32 extended_buffer_pointer_list[2]; +}; +static_assert(AssertSize()); + +// 3.5 Queue Element Transfer Descriptor (qTD) +struct QueueElementTransferDescriptor { + enum class PIDCode : u8 { + OUT = 0b00, // generates token (E1H) + IN = 0b01, // generates token (69H) + SETUP = 0b10, // generates token (2DH) + }; + // 3.5.1 Next qTD Pointer + // Note: the type field is not evaluated here, as is ignored: + // "These bits are reserved and their value has no effect on operation." + // ~ Table 3-14. qTD Next Element Transfer Pointer (DWord 0) + FrameListElementPointer next_qTD_pointer; + // 3.5.2 Alternate Next qTD Pointer + FrameListElementPointer alternate_next_qTD_pointer; + // 3.5.3 qTD Token + struct Status { + u8 ping_state : 1; + u8 split_transaction_state : 1; + u8 missed_micro_frame : 1; + u8 transaction_error : 1; + u8 babble_detected : 1; + u8 data_buffer_error : 1; + u8 halted : 1; + u8 active : 1; + } status; + PIDCode pid_code : 2; + u8 error_counter : 2; + u8 current_page : 3; + u8 interrupt_on_complete : 1; + u16 total_bytes_to_transfer : 15; + u16 data_toggle : 1; + + // 3.5.4 qTD Buffer Page Pointer List + union { + u32 buffer_pointer_list[5]; + struct { + u32 current_page_offset : 12 { 0 }; + u32 : 20; + // Table 3-22. Host-Controller Rules for Bits in Overlay (DWords 5, 6, 8 and 9) + // adds more fields here: + u32 split_transaction_complete_split_progress : 8; // (C-prog-mask) + u32 : 4; + u32 : 20; + u32 split_transaction_frame_tag : 5; + u32 s_bytes : 7; + u32 : 20; + u32 _[2]; + }; + }; +}; +static_assert(AssertSize()); + +struct QueueElementTransferDescriptor64 : public QueueElementTransferDescriptor { + u32 extended_buffer_pointer_list[5]; +}; +static_assert(AssertSize()); + +// 3.6 Queue Head +struct QueueHead { + // 3.6.1 Queue Head Horizontal Link Pointer + FrameListElementPointer queue_head_horizontal_link_pointer; + // 3.6.2 Endpoint Capabilities/Characteristics + struct EndpointCharacteristics { + u32 device_address : 6; + u32 inactive_on_next_transaction : 1; + u32 endpoint_number : 4; + enum class EndpointSpeed : u32 { + FullSpeed = 0b00, + LowSpeed = 0b01, + HighSpeed = 0b10, + } endpoint_speed : 2; + u32 data_toggle_control : 1; + u32 head_of_reclamation_list_flag : 1; + u32 maximum_packet_length : 11; + u32 control_endpoint_flag : 1; + u32 nak_count_reload : 4; + } endpoint_characteristics; + struct EndpointCapabilities { + u32 interrupt_shedule_mask : 8; + u32 split_completion_mask : 8; + u32 hub_address : 7; + u32 port_number : 7; + u32 high_bandwidth_multiplier : 2; + } endpoint_capabilities; + + // 3.6.3 Transfer Overlay + // Note: The lower bits (T, Typ) are ignored + FrameListElementPointer current_transaction_pointer; + // "The DWords 4-11 of a queue head are the transaction overlay area. This area has the same base structure as + // a Queue Element Transfer Descriptor, defined in Section 3.5. The queue head utilizes the reserved fields of + // the page pointers defined in Figure 3-7 to implement tracking the state of split transactions" + // Table 3-22. Host-Controller Rules for Bits in Overlay (DWords 5, 6, 8 and 9) + // FIXME: Do this with less code duplication + enum class PIDCode : u8 { + OUT = 0b00, // generates token (E1H) + IN = 0b01, // generates token (69H) + SETUP = 0b10, // generates token (2DH) + }; + // 3.5.1 Next qTD Pointer + // Note: the type field is not evaluated here, as is ignored: + // "These bits are reserved and their value has no effect on operation." + // ~ Table 3-14. qTD Next Element Transfer Pointer (DWord 0) + union { + QueueElementTransferDescriptor overlay; + struct { + u32 : 32; + u32 : 1; + + u32 nak_counter : 4; + u32 : 27; + u32 _[sizeof(QueueElementTransferDescriptor) / 4 - 2]; + }; + }; +}; +static_assert(AssertSize()); + +struct QueueHead64 : public QueueHead { + u32 extended_buffer_pointer_list[5]; +}; +static_assert(AssertSize()); + +// 3.7 Periodic Frame Span Traversal Node (FSTN) +struct FrameSpanTraversalNode { + FrameListElementPointer normal_path_link_pointer; + FrameListElementPointer back_path_link_pointer; +}; + +} diff --git a/Kernel/Bus/USB/EHCI/EHCIController.cpp b/Kernel/Bus/USB/EHCI/EHCIController.cpp new file mode 100644 index 0000000000..abc6934375 --- /dev/null +++ b/Kernel/Bus/USB/EHCI/EHCIController.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023, Leon Albrecht + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Kernel::USB::EHCI { + +ErrorOr> EHCIController::try_to_initialize(const PCI::DeviceIdentifier& pci_device_identifier) +{ + + // FIXME: This assumes the BIOS left us a physical region for the controller + u64 pci_bar_value = PCI::get_BAR(pci_device_identifier, SpaceBaseAddressRegister); + auto pci_bar_space_type = PCI::get_BAR_space_type(pci_bar_value); + if (pci_bar_space_type == PCI::BARSpaceType::Memory64BitSpace) { + u64 next_pci_bar_value = PCI::get_BAR(pci_device_identifier, static_cast(to_underlying(SpaceBaseAddressRegister) + 1)); + pci_bar_value |= next_pci_bar_value << 32; + } + auto pci_bar_space_size = PCI::get_BAR_space_size(pci_device_identifier, SpaceBaseAddressRegister); + auto register_region = TRY(MM.allocate_kernel_region(PhysicalAddress { pci_bar_value }, pci_bar_space_size, {}, Memory::Region::Access::ReadWrite)); + + PCI::enable_bus_mastering(pci_device_identifier); + PCI::enable_memory_space(pci_device_identifier); + + auto controller = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) EHCIController(pci_device_identifier, move(register_region)))); + + TRY(controller->initialize()); + + return controller; +} + +EHCIController::EHCIController(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr register_region) + : PCI::Device(pci_device_identifier) + , m_register_region(move(register_region)) +{ + m_cap_regs = bit_cast(m_register_region->vaddr().get()); + m_op_regs = bit_cast(m_register_region->vaddr().get() + m_cap_regs->capability_length); +} + +ErrorOr EHCIController::initialize() +{ + dmesgln_pci(*this, "Controller found {} @ {}", PCI::get_hardware_id(device_identifier()), device_identifier().address()); + dmesgln_pci(*this, "Version {}.{}", m_cap_regs->interface_version.major, m_cap_regs->interface_version.minor); + u8 n_ports = m_cap_regs->structural_parameters.n_ports; + dmesgln_pci(*this, "NPorts: {}", n_ports); + u8 n_cc = m_cap_regs->structural_parameters.n_companion_controllers; + u8 n_pcc = m_cap_regs->structural_parameters.n_ports_per_companion_controller; + dmesgln_pci(*this, "Companion Controllers: {}", n_cc); + dmesgln_pci(*this, "Ports per Companion Controllers: {}", n_pcc); + + if (n_ports > n_cc * n_pcc) { + dmesgln_pci(*this, "Warning: Not all ports of the EHCI controller are addressable via companion controllers"); + dmesgln_pci(*this, " Some USB 2.0 ports might not be functional"); + } + + u8 EECP = m_cap_regs->capability_parameters.ehci_extended_capabilities_pointer; + if (EECP) { + SpinlockLocker locker(device_identifier().operation_lock()); + auto legacy_support = bit_cast(PCI::read32_locked(device_identifier(), PCI::RegisterOffset { EECP })); + if (legacy_support.HC_BIOS_owned_semaphore) + dmesgln_pci(*this, "Warning: EHCI controller is BIOS owned"); + } + + // FIXME: Decide which Interrupts we want + // FIXME: Detect and switch on 64 bit support + // FIXME: Allocate and initialize Task Lists + // * Synchronous + // * Asynchronous + // * Leave space for for the actual list items + // and IO scratch space in case we cannot use the buffer from the request + + // FIXME: Initialize the controller and start it + // * Setup the root hub emulation + // * Enable Software routing (CF) + // * Maybe configure port power + + return {}; +} + +} diff --git a/Kernel/Bus/USB/EHCI/EHCIController.h b/Kernel/Bus/USB/EHCI/EHCIController.h new file mode 100644 index 0000000000..e9bdf70d3d --- /dev/null +++ b/Kernel/Bus/USB/EHCI/EHCIController.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023, Leon Albrecht + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Kernel::USB::EHCI { + +class EHCIController : public USBController + , public PCI::Device { +public: + static ErrorOr> try_to_initialize(PCI::DeviceIdentifier const& pci_device_identifier); + virtual ~EHCIController() override = default; + + // ^PCI::Device + virtual StringView device_name() const override { return "EHCI"sv; } + + // ^USBController + virtual ErrorOr initialize() override; + + virtual ErrorOr reset() override { return ENOTSUP; } + virtual ErrorOr stop() override { return ENOTSUP; } + virtual ErrorOr start() override { return ENOTSUP; } + + virtual void cancel_async_transfer(NonnullLockRefPtr) override {}; + virtual ErrorOr submit_control_transfer(Transfer&) override { return ENOTSUP; } + virtual ErrorOr submit_bulk_transfer(Transfer&) override { return ENOTSUP; } + virtual ErrorOr submit_async_interrupt_transfer(NonnullLockRefPtr, u16) override { return ENOTSUP; } + +private: + EHCIController(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr register_region); + + NonnullOwnPtr m_register_region; + CapabilityRegisters const* m_cap_regs; + OperationalRegisters volatile* m_op_regs; +}; + +} diff --git a/Kernel/Bus/USB/EHCI/Registers.h b/Kernel/Bus/USB/EHCI/Registers.h new file mode 100644 index 0000000000..a515068a17 --- /dev/null +++ b/Kernel/Bus/USB/EHCI/Registers.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2023, Leon Albrecht + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Kernel::USB::EHCI { + +// https://www.intel.com/content/www/us/en/products/docs/io/universal-serial-bus/ehci-specification-for-usb.html + +// 2.1.3 USBBASE - Register Space Base Address Register +// Address Offset: 10−13h => BAR0 +constexpr auto SpaceBaseAddressRegister = PCI::HeaderType0BaseRegister::BAR0; + +union BaseRegister { + enum class MappingSupport64Bit : u32 { + No = 0b00, + Yes = 0b10 + }; + struct { + + u32 : 1; + MappingSupport64Bit mapping_support : 2; + u32 : 5; + u32 base_address_hi : 24; + }; + u32 raw; +}; +static_assert(AssertSize()); + +// 2.1.4 SBRN - Serial Bus Release Number Register +// Address Offset: 60h +// Attribute: RO +// Size: 8 bits +// Note: Assuming the layout based on the default value of 0x20 representing USB 2.0 +struct SBRN { + u8 minor : 4; + u8 major : 4; +}; +static_assert(AssertSize()); + +// 2.1.7 USBLEGSUP - USB Legacy Support Extended Capability +// Offset: EECP + 00h +// Attribute RO, R/W +// Size: 32 bits +struct LegacySupport { + u8 capability : 8; + u8 next_ehci_extended_capabilites_pointer : 8; + // These should be u8's as we want individual accesses to these bits, + // if we decide to cooperative ownership of the Controller with the BIOS + u8 HC_BIOS_owned_semaphore : 1; + u8 : 7; + u8 HC_OS_owned_semaphore : 1; + u8 : 7; +}; +static_assert(AssertSize()); + +// 2.1.8 USBLEGCTLSTS - USB Legacy Support Control/Status +// Offset: EECP + 04h +// Default Value 00000000h +// Size: 32 bits +struct LegacySupportControl { + u32 smi_enable : 1; + u32 smi_on_usb_error_enable : 1; + u32 smi_on_port_change_enable : 1; + u32 smi_on_frame_list_rollover_enable : 1; + u32 smi_on_sys_error_enable : 1; + u32 smi_on_async_advance_enable : 1; + u32 : 7; + u32 smi_on_os_ownership_enable : 1; + u32 smi_on_pci_command_enable : 1; + u32 smi_on_bar_enable : 1; + + u32 smi_on_usb_complete : 1; + u32 smi_on_usb_error : 1; + u32 smi_on_port_change_detected : 1; + u32 smi_on_frame_list_rollover : 1; + u32 smi_on_host_system_error : 1; + u32 smi_on_async_advance : 1; + u32 : 7; + u32 smi_on_os_ownership_change : 1; + u32 smi_on_pci_command : 1; + u32 smi_on_bar : 1; +}; +static_assert(AssertSize()); + +// 2.2 Host Controller Capability Registers +struct CapabilityRegisters { + // 2.2.1 CAPLENGTH - Capability Registers Length + u8 capability_length; // Offset to beginning to Operational Register + u8 : 8; + + // 2.2.2 HCIVERSION - Host Controller Interface Version Number + struct InterfaceVersion { + u8 minor; + u8 major; + } interface_version; + static_assert(AssertSize()); + + // 2.2.3 HCSPARAMS - Structural Parameters + struct StructuralParameters { + u32 n_ports : 4; + u32 port_power_control : 1; // N_PPC + u32 : 2; + u32 port_routing_rules : 1; + u32 n_ports_per_companion_controller : 4; // N_PCC + u32 n_companion_controllers : 4; // N_CC + u32 port_indicators : 1; // P_INDICATOR + u32 : 3; + u32 debug_port_number : 4; + u32 : 8; + } structural_parameters; + static_assert(AssertSize()); + + // 2.2.4 HCCPARAMS - Capability Parameters + struct CapabilityParameters { + u32 addressing_capability_64bit : 1; + u32 programmable_frame_list_flag : 1; + u32 asynchronous_schedule_park_capability : 1; + u32 : 1; + u32 isochronous_scheduling_threshold : 4; + u32 ehci_extended_capabilities_pointer : 8; // EECP + u32 : 16; + } capability_parameters; + static_assert(AssertSize()); + + // 2.2.5 HCSP-PORTROUTE - Companion Port Route Description + // Note: Technically only 60 bits + // Technically a u4[n_ports] + u32 companion_port_route_description[2]; +}; +// Table 2-5. Enhanced Host Controller Capability Registers +static_assert(__builtin_offsetof(CapabilityRegisters, capability_length) == 0x00); +static_assert(__builtin_offsetof(CapabilityRegisters, interface_version) == 0x02); +static_assert(__builtin_offsetof(CapabilityRegisters, structural_parameters) == 0x04); +static_assert(__builtin_offsetof(CapabilityRegisters, capability_parameters) == 0x08); +static_assert(__builtin_offsetof(CapabilityRegisters, companion_port_route_description) == 0x0C); + +// 2.3 Host Controller Operational Registers +struct OperationalRegisters { + // 2.3.1 USBCMD - USB Command Register + // Default Value: 00080000h (00080B00h if Asynchronous Schedule Park Capability is a one) + union CommandRegister { + struct { + u32 run_stop : 1; // RS + u32 reset : 1; // HCRESET + u32 frame_list_size : 2; // 1024 / N Elements | N < 0b11 + u32 periodic_schedule_enable : 1; + u32 asynchronous_schedule_enable : 1; + u32 interrupt_on_async_advance_doorbell : 1; + u32 light_host_controller_reset : 1; + u32 asynchronous_schedule_park_mode_count : 2; + u32 : 1; + u32 asynchronous_schedule_park_mode_enable : 1; + u32 : 4; + u32 interrupt_threshold_control : 8; + u32 : 8; + }; + u32 raw; + } command; + static_assert(AssertSize()); + + // 2.3.2 USBSTS - USB Status Register + // Default Value: 00001000h + union StatusRegister { + // To zero an interrupt use a selective write to raw, as otherwise other + // interrupt bits might be cleared as well + const struct { + u32 interrupt : 1; // R/WC + u32 error_interrupt : 1; // R/WC + u32 port_change_detect : 1; // R/WC + u32 frame_list_rollover : 1; // R/WC + u32 host_system_error : 1; // R/WC + u32 interrupt_on_async_advance : 1; // R/WC + u32 : 6; + u32 const hc_halted : 1; + u32 const periodic_schedule_status : 1; + u32 const asynchronous_schedule_status : 1; + u32 : 16; + }; + u32 raw; + } status; + static_assert(AssertSize()); + + // 2.3.3 USBINTR - USB Interrupt Enable Register + struct InterruptEnable { + u32 usb_interrupt_enable : 1; + u32 usb_error_interrupt_enable : 1; + u32 port_change_enable : 1; + u32 frame_list_rollover_enable : 1; + u32 host_system_error_enable : 1; + u32 interrupt_on_async_advance_enable : 1; + u32 : 26; + } interrupt_enable; + static_assert(AssertSize()); + + // 2.3.4 FRINDEX - Frame Index Register + // Note: We use `volatile` to ensure 32 bit writes + // Note: Only up to 14 bits are actually used, and the last 3 bits must never be `000` or `111` + volatile u32 frame_index; + + // 2.3.5 CTRLDSSEGMENT - Control Data Structure Segment Register + // Note: We use `volatile` to ensure 32 bit writes + // Note: Upper 32 bits of periodic-frame- and asynchronous-list pointers + volatile u32 segment_selector; + + // 2.3.6 PERIODICLISTBASE - Periodic Frame List Base Address Register + // Note: We use `volatile` to ensure 32 bit writes + // Note: Page-aligned addresses only + volatile u32 frame_list_base_address; + // 2.3.7 ASYNCLISTADDR - Current Asynchronous List Address Register + // Note: We use `volatile` to ensure 32 bit writes + // Note: 32 byte (cache-line) aligned addresses only + volatile u32 next_asynchronous_list_address; + + u32 _padding[9]; + + // 2.3.8 CONFIGFLAG - Configure Flag Register + u32 configured_flag; + + union PortStatusControl { + enum class LineStatus : u32 { + SE0 = 0b00, + J_State = 0b10, + K_State = 0b01, + Undefined = 0b11 + }; + enum class PortIndicatorControl : u32 { + Off = 0b00, + Amber = 0b01, + Green = 0b10, + Undefined = 0b11 + }; + enum class PortTestControl : u32 { + NotEnabled = 0b0000, + J_State = 0b0001, + K_State = 0b0010, + SE0_NAK = 0b0011, + Packet = 0b0100, + Force_Enable = 0b0101, + }; + + const struct { + u32 current_connect_status : 1; + u32 connect_status_change : 1; // R/WC + u32 port_enable : 1; + u32 port_enable_change : 1; // R/WC + u32 over_current_active : 1; + u32 over_current_change : 1; // R/WC + u32 force_resume : 1; + u32 suspend : 1; + u32 port_reset : 1; + u32 : 1; + LineStatus line_status : 2; + u32 port_power : 1; + u32 port_owner : 1; + PortIndicatorControl port_indicator_control : 2; + PortTestControl port_test_control : 4; + u32 wake_on_connect_enable : 1; // WKCNNT_E + u32 wake_on_disconnect_enable : 1; // WKDSCNNT_E + u32 wake_on_over_current_enable : 1; // WKOC_E + u32 : 9; + }; + u32 raw; + } port_status_control[]; + static_assert(AssertSize()); +}; +// Table 2-8. Host Controller Operational Registers +static_assert(__builtin_offsetof(OperationalRegisters, command) == 0x00); +static_assert(__builtin_offsetof(OperationalRegisters, status) == 0x04); +static_assert(__builtin_offsetof(OperationalRegisters, interrupt_enable) == 0x08); +static_assert(__builtin_offsetof(OperationalRegisters, frame_index) == 0x0C); +static_assert(__builtin_offsetof(OperationalRegisters, segment_selector) == 0x10); +static_assert(__builtin_offsetof(OperationalRegisters, frame_list_base_address) == 0x14); +static_assert(__builtin_offsetof(OperationalRegisters, next_asynchronous_list_address) == 0x18); +static_assert(__builtin_offsetof(OperationalRegisters, configured_flag) == 0x40); +static_assert(__builtin_offsetof(OperationalRegisters, port_status_control) == 0x44); + +} diff --git a/Kernel/Bus/USB/USBManagement.cpp b/Kernel/Bus/USB/USBManagement.cpp index 82cb13e908..fadd30b8fe 100644 --- a/Kernel/Bus/USB/USBManagement.cpp +++ b/Kernel/Bus/USB/USBManagement.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +49,9 @@ UNMAP_AFTER_INIT void USBManagement::enumerate_controllers() dmesgln("USBManagement: OHCI controller found at {} is not currently supported.", device_identifier.address()); return; case EHCI: - dmesgln("USBManagement: EHCI controller found at {} is not currently supported.", device_identifier.address()); + dmesgln("USBManagement: EHCI controller found at {} is currently not fully supported.", device_identifier.address()); + if (auto ehci_controller_or_error = EHCI::EHCIController::try_to_initialize(device_identifier); !ehci_controller_or_error.is_error()) + m_controllers.append(ehci_controller_or_error.release_value()); return; case xHCI: dmesgln("USBManagement: xHCI controller found at {} is not currently supported.", device_identifier.address()); diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 842733cde9..80c05e9067 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -31,6 +31,7 @@ set(KERNEL_SOURCES Bus/PCI/API.cpp Bus/PCI/Device.cpp Bus/PCI/DeviceIdentifier.cpp + Bus/USB/EHCI/EHCIController.cpp Bus/USB/UHCI/UHCIController.cpp Bus/USB/UHCI/UHCIRootHub.cpp Bus/USB/Drivers/HID/MouseDriver.cpp