diff --git a/Kernel/Bus/USB/UHCIController.cpp b/Kernel/Bus/USB/UHCIController.cpp index 42c0197497..3a6c52d95b 100644 --- a/Kernel/Bus/USB/UHCIController.cpp +++ b/Kernel/Bus/USB/UHCIController.cpp @@ -27,8 +27,6 @@ static constexpr u8 RETRY_COUNTER_RELOAD = 3; namespace Kernel::USB { -static UHCIController* s_the; - static constexpr u16 UHCI_USBCMD_RUN = 0x0001; static constexpr u16 UHCI_USBCMD_HOST_CONTROLLER_RESET = 0x0002; static constexpr u16 UHCI_USBCMD_GLOBAL_RESET = 0x0004; @@ -211,43 +209,44 @@ NonnullRefPtr SysFSUSBDeviceInformation::create(USB:: return adopt_ref(*new SysFSUSBDeviceInformation(device)); } -UHCIController& UHCIController::the() +KResultOr> UHCIController::try_to_initialize(PCI::Address address) { - return *s_the; + // NOTE: This assumes that address is pointing to a valid UHCI controller. + auto controller = adopt_ref_if_nonnull(new (nothrow) UHCIController(address)); + if (!controller) + return ENOMEM; + + auto init_result = controller->initialize(); + if (init_result.is_error()) + return init_result; + + return controller.release_nonnull(); } -UNMAP_AFTER_INIT void UHCIController::detect() +KResult UHCIController::initialize() { - if (kernel_command_line().disable_uhci_controller()) - return; - // FIXME: We create the /proc/bus/usb representation here, but it should really be handled // in a more broad singleton than this once we refactor things in USB subsystem. SysFSUSBBusDirectory::initialize(); - PCI::enumerate([&](const PCI::Address& address, PCI::ID id) { - if (address.is_null()) - return; - - if (PCI::get_class(address) == 0xc && PCI::get_subclass(address) == 0x03 && PCI::get_programming_interface(address) == 0) { - if (!s_the) { - s_the = new UHCIController(address, id); - s_the->spawn_port_proc(); - } - } - }); -} - -UNMAP_AFTER_INIT UHCIController::UHCIController(PCI::Address address, PCI::ID id) - : PCI::Device(address) - , m_io_base(PCI::get_BAR4(pci_address()) & ~1) -{ - dmesgln("UHCI: Controller found {} @ {}", id, address); + dmesgln("UHCI: Controller found {} @ {}", PCI::get_id(pci_address()), pci_address()); dmesgln("UHCI: I/O base {}", m_io_base); dmesgln("UHCI: Interrupt line: {}", PCI::get_interrupt_line(pci_address())); - reset(); - start(); + spawn_port_proc(); + + auto reset_result = reset(); + if (reset_result.is_error()) + return reset_result; + + auto start_result = start(); + return start_result; +} + +UNMAP_AFTER_INIT UHCIController::UHCIController(PCI::Address address) + : PCI::Device(address) + , m_io_base(PCI::get_BAR4(pci_address()) & ~1) +{ } UNMAP_AFTER_INIT UHCIController::~UHCIController() @@ -275,9 +274,10 @@ RefPtr const UHCIController::get_device_from_address(u8 device_addr return nullptr; } -void UHCIController::reset() +KResult UHCIController::reset() { - stop(); + if (auto stop_result = stop(); stop_result.is_error()) + return stop_result; write_usbcmd(UHCI_USBCMD_HOST_CONTROLLER_RESET); @@ -305,6 +305,8 @@ void UHCIController::reset() // Disable UHCI Controller from raising an IRQ write_usbintr(0); dbgln("UHCI: Reset completed"); + + return KSuccess; } UNMAP_AFTER_INIT void UHCIController::create_structures() @@ -464,7 +466,7 @@ TransferDescriptor* UHCIController::allocate_transfer_descriptor() const return nullptr; // Huh?! We're outta TDs!! } -void UHCIController::stop() +KResult UHCIController::stop() { write_usbcmd(read_usbcmd() & ~UHCI_USBCMD_RUN); // FIXME: Timeout @@ -472,9 +474,10 @@ void UHCIController::stop() if (read_usbsts() & UHCI_USBSTS_HOST_CONTROLLER_HALTED) break; } + return KSuccess; } -void UHCIController::start() +KResult UHCIController::start() { write_usbcmd(read_usbcmd() | UHCI_USBCMD_RUN); // FIXME: Timeout @@ -483,6 +486,7 @@ void UHCIController::start() break; } dbgln("UHCI: Started"); + return KSuccess; } TransferDescriptor* UHCIController::create_transfer_descriptor(Pipe& pipe, PacketID direction, size_t data_len) @@ -713,7 +717,7 @@ void UHCIController::spawn_port_proc() dbgln("port should be enabled now: {:#04x}\n", read_portsc1()); USB::Device::DeviceSpeed speed = (port_data & UHCI_PORTSC_LOW_SPEED_DEVICE) ? USB::Device::DeviceSpeed::LowSpeed : USB::Device::DeviceSpeed::FullSpeed; - auto device = USB::Device::try_create(USB::Device::PortNumber::Port1, speed); + auto device = USB::Device::try_create(*this, USB::Device::PortNumber::Port1, speed); if (device.is_error()) dmesgln("UHCI: Device creation failed on port 1 ({})", device.error()); @@ -730,7 +734,7 @@ void UHCIController::spawn_port_proc() } } } else { - port_data = UHCIController::the().read_portsc2(); + port_data = read_portsc2(); if (port_data & UHCI_PORTSC_CONNECT_STATUS_CHANGED) { if (port_data & UHCI_PORTSC_CURRRENT_CONNECT_STATUS) { dmesgln("UHCI: Device attach detected on Root Port 2"); @@ -751,7 +755,7 @@ void UHCIController::spawn_port_proc() write_portsc2(port_data | UHCI_PORTSC_PORT_ENABLED); dbgln("port should be enabled now: {:#04x}\n", read_portsc2()); USB::Device::DeviceSpeed speed = (port_data & UHCI_PORTSC_LOW_SPEED_DEVICE) ? USB::Device::DeviceSpeed::LowSpeed : USB::Device::DeviceSpeed::FullSpeed; - auto device = USB::Device::try_create(USB::Device::PortNumber::Port2, speed); + auto device = USB::Device::try_create(*this, USB::Device::PortNumber::Port2, speed); if (device.is_error()) dmesgln("UHCI: Device creation failed on port 2 ({})", device.error()); diff --git a/Kernel/Bus/USB/UHCIController.h b/Kernel/Bus/USB/UHCIController.h index 8594c0f024..2dfc5235a4 100644 --- a/Kernel/Bus/USB/UHCIController.h +++ b/Kernel/Bus/USB/UHCIController.h @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -21,30 +20,31 @@ namespace Kernel::USB { -class UHCIController final : public PCI::Device { +class UHCIController final + : public USBController + , public PCI::Device { public: - static void detect(); - static UHCIController& the(); - + static KResultOr> try_to_initialize(PCI::Address address); virtual ~UHCIController() override; virtual StringView purpose() const override { return "UHCI"; } - void reset(); - void stop(); - void start(); + virtual KResult initialize() override; + virtual KResult reset() override; + virtual KResult stop() override; + virtual KResult start() override; void spawn_port_proc(); void do_debug_transfer(); - KResultOr submit_control_transfer(Transfer& transfer); + virtual KResultOr submit_control_transfer(Transfer& transfer) override; - RefPtr const get_device_at_port(USB::Device::PortNumber); - RefPtr const get_device_from_address(u8 device_address); + virtual RefPtr const get_device_at_port(USB::Device::PortNumber) override; + virtual RefPtr const get_device_from_address(u8 device_address) override; private: - UHCIController(PCI::Address, PCI::ID); + explicit UHCIController(PCI::Address); u16 read_usbcmd() { return m_io_base.offset(0).in(); } u16 read_usbsts() { return m_io_base.offset(0x2).in(); } diff --git a/Kernel/Bus/USB/USBController.cpp b/Kernel/Bus/USB/USBController.cpp new file mode 100644 index 0000000000..2733767c5a --- /dev/null +++ b/Kernel/Bus/USB/USBController.cpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2021, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Kernel::USB { + +u8 USBController::allocate_address() +{ + // FIXME: This can be smarter. + return m_next_device_index++; +} + +} diff --git a/Kernel/Bus/USB/USBController.h b/Kernel/Bus/USB/USBController.h new file mode 100644 index 0000000000..065dbb166d --- /dev/null +++ b/Kernel/Bus/USB/USBController.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel::USB { + +class USBController : public RefCounted { +public: + virtual ~USBController() = default; + + virtual KResult initialize() = 0; + + virtual KResult reset() = 0; + virtual KResult stop() = 0; + virtual KResult start() = 0; + + virtual KResultOr submit_control_transfer(Transfer&) = 0; + + virtual RefPtr const get_device_at_port(USB::Device::PortNumber) = 0; + virtual RefPtr const get_device_from_address(u8) = 0; + + u8 allocate_address(); + +private: + u8 m_next_device_index { 1 }; + + IntrusiveListNode> m_controller_list_node; + +public: + using List = IntrusiveList, &USBController::m_controller_list_node>; +}; + +} diff --git a/Kernel/Bus/USB/USBDevice.cpp b/Kernel/Bus/USB/USBDevice.cpp index 0feb4becee..29026b89a4 100644 --- a/Kernel/Bus/USB/USBDevice.cpp +++ b/Kernel/Bus/USB/USBDevice.cpp @@ -5,24 +5,24 @@ */ #include +#include #include #include -#include +#include #include #include #include - -static u32 s_next_usb_address = 1; // Next address we hand out to a device once it's plugged into the machine +#include namespace Kernel::USB { -KResultOr> Device::try_create(PortNumber port, DeviceSpeed speed) +KResultOr> Device::try_create(USBController const& controller, PortNumber port, DeviceSpeed speed) { - auto pipe_or_error = Pipe::try_create_pipe(Pipe::Type::Control, Pipe::Direction::Bidirectional, 0, 8, 0); + auto pipe_or_error = Pipe::try_create_pipe(controller, Pipe::Type::Control, Pipe::Direction::Bidirectional, 0, 8, 0); if (pipe_or_error.is_error()) return pipe_or_error.error(); - auto device = AK::try_create(port, speed, pipe_or_error.release_value()); + auto device = AK::try_create(controller, port, speed, pipe_or_error.release_value()); if (!device) return ENOMEM; @@ -33,10 +33,11 @@ KResultOr> Device::try_create(PortNumber port, DeviceSpeed return device.release_nonnull(); } -Device::Device(PortNumber port, DeviceSpeed speed, NonnullOwnPtr default_pipe) +Device::Device(USBController const& controller, PortNumber port, DeviceSpeed speed, NonnullOwnPtr default_pipe) : m_device_port(port) , m_device_speed(speed) , m_address(0) + , m_controller(controller) , m_default_pipe(move(default_pipe)) { } @@ -83,8 +84,10 @@ KResult Device::enumerate() dbgln("Number of configurations: {:02x}", dev_descriptor.num_configurations); } + m_address = m_controller->allocate_address(); + // Attempt to set devices address on the bus - transfer_length_or_error = m_default_pipe->control_transfer(USB_DEVICE_REQUEST_HOST_TO_DEVICE, USB_REQUEST_SET_ADDRESS, s_next_usb_address, 0, 0, nullptr); + transfer_length_or_error = m_default_pipe->control_transfer(USB_DEVICE_REQUEST_HOST_TO_DEVICE, USB_REQUEST_SET_ADDRESS, m_address, 0, 0, nullptr); if (transfer_length_or_error.is_error()) return transfer_length_or_error.error(); @@ -92,7 +95,6 @@ KResult Device::enumerate() transfer_length = transfer_length_or_error.release_value(); VERIFY(transfer_length > 0); - m_address = s_next_usb_address++; memcpy(&m_device_descriptor, &dev_descriptor, sizeof(USBDeviceDescriptor)); return KSuccess; diff --git a/Kernel/Bus/USB/USBDevice.h b/Kernel/Bus/USB/USBDevice.h index c57c8ce49f..e31f517957 100644 --- a/Kernel/Bus/USB/USBDevice.h +++ b/Kernel/Bus/USB/USBDevice.h @@ -12,6 +12,8 @@ namespace Kernel::USB { +class USBController; + // // Some nice info from FTDI on device enumeration and how some of this // glues together: @@ -30,9 +32,9 @@ public: }; public: - static KResultOr> try_create(PortNumber, DeviceSpeed); + static KResultOr> try_create(USBController const&, PortNumber, DeviceSpeed); - Device(PortNumber, DeviceSpeed, NonnullOwnPtr default_pipe); + Device(USBController const&, PortNumber, DeviceSpeed, NonnullOwnPtr default_pipe); ~Device(); KResult enumerate(); @@ -54,6 +56,7 @@ private: u16 m_product_id { 0 }; // This device's product ID assigned by the USB group USBDeviceDescriptor m_device_descriptor; // Device Descriptor obtained from USB Device + NonnullRefPtr m_controller; NonnullOwnPtr m_default_pipe; // Default communication pipe (endpoint0) used during enumeration }; } diff --git a/Kernel/Bus/USB/USBManagement.cpp b/Kernel/Bus/USB/USBManagement.cpp new file mode 100644 index 0000000000..53363f02c2 --- /dev/null +++ b/Kernel/Bus/USB/USBManagement.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +namespace Kernel::USB { + +static Singleton s_the; + +UNMAP_AFTER_INIT USBManagement::USBManagement() +{ + enumerate_controllers(); +} + +UNMAP_AFTER_INIT void USBManagement::enumerate_controllers() +{ + if (!kernel_command_line().disable_usb()) { + PCI::enumerate([this](PCI::Address const& address, PCI::ID) { + if (PCI::get_class(address) == 0xc && PCI::get_subclass(address) == 0x3) { + if (PCI::get_programming_interface(address) == 0x0) { + if (kernel_command_line().disable_uhci_controller()) + return; + + if (auto uhci_controller_or_error = UHCIController::try_to_initialize(address); !uhci_controller_or_error.is_error()) + m_controllers.append(uhci_controller_or_error.release_value()); + + return; + } + + if (PCI::get_programming_interface(address) == 0x10) { + dmesgln("USBManagement: OHCI controller found at {} is not currently supported.", address); + return; + } + + if (PCI::get_programming_interface(address) == 0x20) { + dmesgln("USBManagement: EHCI controller found at {} is not currently supported.", address); + return; + } + + if (PCI::get_programming_interface(address) == 0x30) { + dmesgln("USBManagement: xHCI controller found at {} is not currently supported.", address); + return; + } + + dmesgln("USBManagement: Unknown/unsupported controller at {} with programming interface 0x{:02x}", address, PCI::get_programming_interface(address)); + } + }); + } +} + +bool USBManagement::initialized() +{ + return s_the.is_initialized(); +} + +UNMAP_AFTER_INIT void USBManagement::initialize() +{ + s_the.ensure_instance(); +} + +USBManagement& USBManagement::the() +{ + return *s_the; +} + +} diff --git a/Kernel/Bus/USB/USBManagement.h b/Kernel/Bus/USB/USBManagement.h new file mode 100644 index 0000000000..0219c13350 --- /dev/null +++ b/Kernel/Bus/USB/USBManagement.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Kernel::USB { + +class USBManagement { + AK_MAKE_ETERNAL; + +public: + USBManagement(); + static bool initialized(); + static void initialize(); + static USBManagement& the(); + +private: + void enumerate_controllers(); + + USBController::List m_controllers; +}; + +} diff --git a/Kernel/Bus/USB/USBPipe.cpp b/Kernel/Bus/USB/USBPipe.cpp index 29bf198742..92c7a161c3 100644 --- a/Kernel/Bus/USB/USBPipe.cpp +++ b/Kernel/Bus/USB/USBPipe.cpp @@ -11,17 +11,18 @@ namespace Kernel::USB { -KResultOr> Pipe::try_create_pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, u8 poll_interval) +KResultOr> Pipe::try_create_pipe(USBController const& controller, Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, u8 poll_interval) { - auto pipe = adopt_own_if_nonnull(new (nothrow) Pipe(type, direction, endpoint_address, max_packet_size, device_address, poll_interval)); + auto pipe = adopt_own_if_nonnull(new (nothrow) Pipe(controller, type, direction, endpoint_address, max_packet_size, device_address, poll_interval)); if (!pipe) return ENOMEM; return pipe.release_nonnull(); } -Pipe::Pipe(Type type, Pipe::Direction direction, u16 max_packet_size) - : m_type(type) +Pipe::Pipe(USBController const& controller, Type type, Pipe::Direction direction, u16 max_packet_size) + : m_controller(controller) + , m_type(type) , m_direction(direction) , m_endpoint_address(0) , m_max_packet_size(max_packet_size) @@ -30,15 +31,17 @@ Pipe::Pipe(Type type, Pipe::Direction direction, u16 max_packet_size) { } -Pipe::Pipe(Type type, Direction direction, USBEndpointDescriptor& endpoint [[maybe_unused]]) - : m_type(type) +Pipe::Pipe(USBController const& controller, Type type, Direction direction, USBEndpointDescriptor& endpoint [[maybe_unused]]) + : m_controller(controller) + , m_type(type) , m_direction(direction) { // TODO: decode endpoint structure } -Pipe::Pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, u8 poll_interval, i8 device_address) - : m_type(type) +Pipe::Pipe(USBController const& controller, Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, u8 poll_interval, i8 device_address) + : m_controller(controller) + , m_type(type) , m_direction(direction) , m_device_address(device_address) , m_endpoint_address(endpoint_address) @@ -66,7 +69,7 @@ KResultOr Pipe::control_transfer(u8 request_type, u8 request, u16 value, transfer->set_setup_packet(usb_request); dbgln_if(USB_DEBUG, "Pipe: Transfer allocated @ {}", transfer->buffer_physical()); - auto transfer_len_or_error = UHCIController::the().submit_control_transfer(*transfer); + auto transfer_len_or_error = m_controller->submit_control_transfer(*transfer); if (transfer_len_or_error.is_error()) return transfer_len_or_error.error(); diff --git a/Kernel/Bus/USB/USBPipe.h b/Kernel/Bus/USB/USBPipe.h index ce1af94bd0..5a41bdfa9c 100644 --- a/Kernel/Bus/USB/USBPipe.h +++ b/Kernel/Bus/USB/USBPipe.h @@ -13,6 +13,8 @@ namespace Kernel::USB { +class USBController; + // // A pipe is the logical connection between a memory buffer on the PC (host) and // an endpoint on the device. In this implementation, the data buffer the pipe connects @@ -39,7 +41,7 @@ public: }; public: - static KResultOr> try_create_pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, u8 poll_interval = 0); + static KResultOr> try_create_pipe(USBController const& controller, Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, u8 poll_interval = 0); Type type() const { return m_type; } Direction direction() const { return m_direction; } @@ -57,13 +59,15 @@ public: KResultOr control_transfer(u8 request_type, u8 request, u16 value, u16 index, u16 length, void* data); - Pipe(Type type, Direction direction, u16 max_packet_size); - Pipe(Type type, Direction direction, USBEndpointDescriptor& endpoint); - Pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, u8 poll_interval, i8 device_address); + Pipe(USBController const& controller, Type type, Direction direction, u16 max_packet_size); + Pipe(USBController const& controller, Type type, Direction direction, USBEndpointDescriptor& endpoint); + Pipe(USBController const& controller, Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, u8 poll_interval, i8 device_address); private: friend class Device; + NonnullRefPtr m_controller; + Type m_type; Direction m_direction; DeviceSpeed m_speed; diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index cd426a38ba..1ac315a2d8 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -32,7 +32,9 @@ set(KERNEL_SOURCES Bus/PCI/Initializer.cpp Bus/PCI/WindowedMMIOAccess.cpp Bus/USB/UHCIController.cpp + Bus/USB/USBController.cpp Bus/USB/USBDevice.cpp + Bus/USB/USBManagement.cpp Bus/USB/USBPipe.cpp Bus/USB/USBTransfer.cpp CMOS.cpp diff --git a/Kernel/CommandLine.cpp b/Kernel/CommandLine.cpp index 64716fb8bf..237abec349 100644 --- a/Kernel/CommandLine.cpp +++ b/Kernel/CommandLine.cpp @@ -177,6 +177,11 @@ UNMAP_AFTER_INIT bool CommandLine::disable_uhci_controller() const return contains("disable_uhci_controller"sv); } +UNMAP_AFTER_INIT bool CommandLine::disable_usb() const +{ + return contains("disable_usb"sv); +} + UNMAP_AFTER_INIT bool CommandLine::disable_virtio() const { return contains("disable_virtio"sv); diff --git a/Kernel/CommandLine.h b/Kernel/CommandLine.h index 35d70e3db7..ab7bcf4eea 100644 --- a/Kernel/CommandLine.h +++ b/Kernel/CommandLine.h @@ -73,6 +73,7 @@ public: [[nodiscard]] bool disable_physical_storage() const; [[nodiscard]] bool disable_ps2_controller() const; [[nodiscard]] bool disable_uhci_controller() const; + [[nodiscard]] bool disable_usb() const; [[nodiscard]] bool disable_virtio() const; [[nodiscard]] AHCIResetMode ahci_reset_mode() const; [[nodiscard]] String userspace_init() const; diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 9895809d24..b0a5b96927 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -300,7 +300,7 @@ void init_stage2(void*) auto boot_profiling = kernel_command_line().is_boot_profiling_enabled(); - USB::UHCIController::detect(); + USB::USBManagement::initialize(); BIOSSysFSDirectory::initialize(); ACPI::ACPISysFSDirectory::initialize();