Kernel/USB: Create controller base class and introduce USBManagement

This removes Pipes dependency on the UHCIController by introducing a
controller base class. This will be used to implement other controllers
such as OHCI.

Additionally, there can be multiple instances of a UHCI controller.
For example, multiple UHCI instances can be required for systems with
EHCI controllers. EHCI relies on using multiple of either UHCI or OHCI
controllers to drive USB 1.x devices.

This means UHCIController can no longer be a singleton. Multiple
instances of it can now be created and passed to the device and then to
the pipe.

To handle finding and creating these instances, USBManagement has been
introduced. It has the same pattern as the other management classes
such as NetworkManagement.
This commit is contained in:
Luke 2021-08-08 19:50:20 +01:00 committed by Andreas Kling
parent c5974d3518
commit 7a86a8df90
Notes: sideshowbarker 2024-07-18 07:10:53 +09:00
14 changed files with 261 additions and 74 deletions

View file

@ -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> SysFSUSBDeviceInformation::create(USB::
return adopt_ref(*new SysFSUSBDeviceInformation(device));
}
UHCIController& UHCIController::the()
KResultOr<NonnullRefPtr<UHCIController>> 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<USB::Device> 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());

View file

@ -12,8 +12,7 @@
#include <AK/NonnullOwnPtr.h>
#include <Kernel/Bus/PCI/Device.h>
#include <Kernel/Bus/USB/UHCIDescriptorTypes.h>
#include <Kernel/Bus/USB/USBDevice.h>
#include <Kernel/Bus/USB/USBTransfer.h>
#include <Kernel/Bus/USB/USBController.h>
#include <Kernel/IO.h>
#include <Kernel/Memory/AnonymousVMObject.h>
#include <Kernel/Process.h>
@ -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<NonnullRefPtr<UHCIController>> 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<size_t> submit_control_transfer(Transfer& transfer);
virtual KResultOr<size_t> submit_control_transfer(Transfer& transfer) override;
RefPtr<USB::Device> const get_device_at_port(USB::Device::PortNumber);
RefPtr<USB::Device> const get_device_from_address(u8 device_address);
virtual RefPtr<USB::Device> const get_device_at_port(USB::Device::PortNumber) override;
virtual RefPtr<USB::Device> 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>(); }
u16 read_usbsts() { return m_io_base.offset(0x2).in<u16>(); }

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Bus/USB/USBController.h>
namespace Kernel::USB {
u8 USBController::allocate_address()
{
// FIXME: This can be smarter.
return m_next_device_index++;
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/RefCounted.h>
#include <Kernel/Bus/USB/USBDevice.h>
#include <Kernel/Bus/USB/USBTransfer.h>
#include <Kernel/KResult.h>
namespace Kernel::USB {
class USBController : public RefCounted<USBController> {
public:
virtual ~USBController() = default;
virtual KResult initialize() = 0;
virtual KResult reset() = 0;
virtual KResult stop() = 0;
virtual KResult start() = 0;
virtual KResultOr<size_t> submit_control_transfer(Transfer&) = 0;
virtual RefPtr<USB::Device> const get_device_at_port(USB::Device::PortNumber) = 0;
virtual RefPtr<USB::Device> const get_device_from_address(u8) = 0;
u8 allocate_address();
private:
u8 m_next_device_index { 1 };
IntrusiveListNode<USBController, NonnullRefPtr<USBController>> m_controller_list_node;
public:
using List = IntrusiveList<USBController, NonnullRefPtr<USBController>, &USBController::m_controller_list_node>;
};
}

View file

@ -5,24 +5,24 @@
*/
#include <AK/OwnPtr.h>
#include <AK/String.h>
#include <AK/Types.h>
#include <AK/Vector.h>
#include <Kernel/Bus/USB/UHCIController.h>
#include <Kernel/Bus/USB/USBController.h>
#include <Kernel/Bus/USB/USBDescriptors.h>
#include <Kernel/Bus/USB/USBDevice.h>
#include <Kernel/Bus/USB/USBRequest.h>
static u32 s_next_usb_address = 1; // Next address we hand out to a device once it's plugged into the machine
#include <Kernel/StdLib.h>
namespace Kernel::USB {
KResultOr<NonnullRefPtr<Device>> Device::try_create(PortNumber port, DeviceSpeed speed)
KResultOr<NonnullRefPtr<Device>> 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<Device>(port, speed, pipe_or_error.release_value());
auto device = AK::try_create<Device>(controller, port, speed, pipe_or_error.release_value());
if (!device)
return ENOMEM;
@ -33,10 +33,11 @@ KResultOr<NonnullRefPtr<Device>> Device::try_create(PortNumber port, DeviceSpeed
return device.release_nonnull();
}
Device::Device(PortNumber port, DeviceSpeed speed, NonnullOwnPtr<Pipe> default_pipe)
Device::Device(USBController const& controller, PortNumber port, DeviceSpeed speed, NonnullOwnPtr<Pipe> 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;

View file

@ -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<NonnullRefPtr<Device>> try_create(PortNumber, DeviceSpeed);
static KResultOr<NonnullRefPtr<Device>> try_create(USBController const&, PortNumber, DeviceSpeed);
Device(PortNumber, DeviceSpeed, NonnullOwnPtr<Pipe> default_pipe);
Device(USBController const&, PortNumber, DeviceSpeed, NonnullOwnPtr<Pipe> 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<USBController> m_controller;
NonnullOwnPtr<Pipe> m_default_pipe; // Default communication pipe (endpoint0) used during enumeration
};
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Singleton.h>
#include <Kernel/Bus/PCI/Access.h>
#include <Kernel/Bus/USB/UHCIController.h>
#include <Kernel/Bus/USB/USBManagement.h>
#include <Kernel/CommandLine.h>
#include <Kernel/Sections.h>
namespace Kernel::USB {
static Singleton<USBManagement> 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;
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/NonnullRefPtr.h>
#include <AK/NonnullRefPtrVector.h>
#include <Kernel/Bus/USB/USBController.h>
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;
};
}

View file

@ -11,17 +11,18 @@
namespace Kernel::USB {
KResultOr<NonnullOwnPtr<Pipe>> Pipe::try_create_pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, u8 poll_interval)
KResultOr<NonnullOwnPtr<Pipe>> 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<size_t> 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();

View file

@ -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<NonnullOwnPtr<Pipe>> try_create_pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, u8 poll_interval = 0);
static KResultOr<NonnullOwnPtr<Pipe>> 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<size_t> 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<USBController> m_controller;
Type m_type;
Direction m_direction;
DeviceSpeed m_speed;

View file

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

View file

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

View file

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

View file

@ -13,7 +13,7 @@
#include <Kernel/BootInfo.h>
#include <Kernel/Bus/PCI/Access.h>
#include <Kernel/Bus/PCI/Initializer.h>
#include <Kernel/Bus/USB/UHCIController.h>
#include <Kernel/Bus/USB/USBManagement.h>
#include <Kernel/CMOS.h>
#include <Kernel/CommandLine.h>
#include <Kernel/Devices/FullDevice.h>
@ -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();