Kernel/USB: Add a rudimentary interrogation only EHCI driver

This adds a simple EHCI driver that currently only interrogates the
device and checks if all ports are addressable via associated legacy
controllers (companion controllers), and warns if this is not the case.

This also adds a lot of the other data structures needed for actually
driving the controller, but these are currently not hooked up to
anything.

To test this run with `SERENITY_EXTRA_QEMU_ARGS="--device usb-ehci"`
or the q35 machine type
This commit is contained in:
Hendiadyoin1 2023-12-13 21:59:03 +01:00 committed by Andrew Kaster
parent a768685d16
commit f4bfd0468b
6 changed files with 742 additions and 1 deletions

View file

@ -0,0 +1,324 @@
/*
* Copyright (c) 2023, Leon Albrecht <leon.a@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
#include <Kernel/Memory/PhysicalAddress.h>
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<typename T>
static FrameListElementPointer make(PhysicalPtr addr);
template<>
FrameListElementPointer make<IsochronousTransferDescriptor>(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<SplitTransactionIsochronousTransferDescriptor>(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<QueueHead>(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<FrameSpanTraversalNode>(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<IsochronousTransferDescriptor, 0x40>());
struct IsochronousTransferDescriptor64 : public IsochronousTransferDescriptor {
u32 extended_buffer_pointer_list[7];
};
static_assert(AssertSize<IsochronousTransferDescriptor64, 0x5C>());
// 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<SplitTransactionIsochronousTransferDescriptor, 0x1C>());
struct SplitTransactionIsochronousTransferDescriptor64 : public SplitTransactionIsochronousTransferDescriptor {
u32 extended_buffer_pointer_list[2];
};
static_assert(AssertSize<SplitTransactionIsochronousTransferDescriptor64, 0x24>());
// 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<QueueElementTransferDescriptor, 0x20>());
struct QueueElementTransferDescriptor64 : public QueueElementTransferDescriptor {
u32 extended_buffer_pointer_list[5];
};
static_assert(AssertSize<QueueElementTransferDescriptor64, 0x34>());
// 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<QueueHead, 0x30>());
struct QueueHead64 : public QueueHead {
u32 extended_buffer_pointer_list[5];
};
static_assert(AssertSize<QueueHead64, 0x44>());
// 3.7 Periodic Frame Span Traversal Node (FSTN)
struct FrameSpanTraversalNode {
FrameListElementPointer normal_path_link_pointer;
FrameListElementPointer back_path_link_pointer;
};
}

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2023, Leon Albrecht <leon.a@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Bus/USB/EHCI/EHCIController.h>
namespace Kernel::USB::EHCI {
ErrorOr<NonnullLockRefPtr<EHCIController>> 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<PCI::HeaderType0BaseRegister>(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<Memory::Region> register_region)
: PCI::Device(pci_device_identifier)
, m_register_region(move(register_region))
{
m_cap_regs = bit_cast<CapabilityRegisters const*>(m_register_region->vaddr().get());
m_op_regs = bit_cast<OperationalRegisters volatile*>(m_register_region->vaddr().get() + m_cap_regs->capability_length);
}
ErrorOr<void> 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<LegacySupport>(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 {};
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2023, Leon Albrecht <leon.a@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
#include <Kernel/Bus/PCI/Device.h>
#include <Kernel/Bus/USB/EHCI/Registers.h>
#include <Kernel/Bus/USB/USBController.h>
#include <Kernel/Memory/TypedMapping.h>
namespace Kernel::USB::EHCI {
class EHCIController : public USBController
, public PCI::Device {
public:
static ErrorOr<NonnullLockRefPtr<EHCIController>> 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<void> initialize() override;
virtual ErrorOr<void> reset() override { return ENOTSUP; }
virtual ErrorOr<void> stop() override { return ENOTSUP; }
virtual ErrorOr<void> start() override { return ENOTSUP; }
virtual void cancel_async_transfer(NonnullLockRefPtr<Transfer>) override {};
virtual ErrorOr<size_t> submit_control_transfer(Transfer&) override { return ENOTSUP; }
virtual ErrorOr<size_t> submit_bulk_transfer(Transfer&) override { return ENOTSUP; }
virtual ErrorOr<void> submit_async_interrupt_transfer(NonnullLockRefPtr<Transfer>, u16) override { return ENOTSUP; }
private:
EHCIController(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr<Memory::Region> register_region);
NonnullOwnPtr<Memory::Region> m_register_region;
CapabilityRegisters const* m_cap_regs;
OperationalRegisters volatile* m_op_regs;
};
}

View file

@ -0,0 +1,284 @@
/*
* Copyright (c) 2023, Leon Albrecht <leon.a@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
#include <Kernel/Bus/PCI/Definitions.h>
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: 1013h => 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<BaseRegister, 32 / 8>());
// 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<SBRN, 8 / 8>());
// 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<LegacySupport, 32 / 8>());
// 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<LegacySupportControl, 32 / 8>());
// 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<InterfaceVersion, 16 / 8>());
// 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<StructuralParameters, 32 / 8>());
// 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<CapabilityParameters, 32 / 8>());
// 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<CommandRegister, 32 / 8>());
// 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<StatusRegister, 32 / 8>());
// 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<InterruptEnable, 32 / 8>());
// 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<PortStatusControl, 32 / 8>());
};
// 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);
}

View file

@ -9,6 +9,7 @@
#include <Kernel/Boot/CommandLine.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Bus/PCI/Definitions.h>
#include <Kernel/Bus/USB/EHCI/EHCIController.h>
#include <Kernel/Bus/USB/UHCI/UHCIController.h>
#include <Kernel/Bus/USB/USBManagement.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Bus/USB/BusDirectory.h>
@ -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());

View file

@ -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