mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-11 17:00:37 +00:00
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:
parent
a768685d16
commit
f4bfd0468b
Notes:
sideshowbarker
2024-07-17 17:06:59 +09:00
Author: https://github.com/Hendiadyoin1 Commit: https://github.com/SerenityOS/serenity/commit/f4bfd0468b Pull-request: https://github.com/SerenityOS/serenity/pull/22281 Reviewed-by: https://github.com/ADKaster ✅ Reviewed-by: https://github.com/supercomputer7
6 changed files with 742 additions and 1 deletions
324
Kernel/Bus/USB/EHCI/DataStructures.h
Normal file
324
Kernel/Bus/USB/EHCI/DataStructures.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
83
Kernel/Bus/USB/EHCI/EHCIController.cpp
Normal file
83
Kernel/Bus/USB/EHCI/EHCIController.cpp
Normal 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 {};
|
||||
}
|
||||
|
||||
}
|
46
Kernel/Bus/USB/EHCI/EHCIController.h
Normal file
46
Kernel/Bus/USB/EHCI/EHCIController.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
284
Kernel/Bus/USB/EHCI/Registers.h
Normal file
284
Kernel/Bus/USB/EHCI/Registers.h
Normal 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: 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<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);
|
||||
|
||||
}
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue