mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 17:27:35 +00:00
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:
parent
c5974d3518
commit
7a86a8df90
14 changed files with 261 additions and 74 deletions
|
@ -27,8 +27,6 @@ static constexpr u8 RETRY_COUNTER_RELOAD = 3;
|
||||||
|
|
||||||
namespace Kernel::USB {
|
namespace Kernel::USB {
|
||||||
|
|
||||||
static UHCIController* s_the;
|
|
||||||
|
|
||||||
static constexpr u16 UHCI_USBCMD_RUN = 0x0001;
|
static constexpr u16 UHCI_USBCMD_RUN = 0x0001;
|
||||||
static constexpr u16 UHCI_USBCMD_HOST_CONTROLLER_RESET = 0x0002;
|
static constexpr u16 UHCI_USBCMD_HOST_CONTROLLER_RESET = 0x0002;
|
||||||
static constexpr u16 UHCI_USBCMD_GLOBAL_RESET = 0x0004;
|
static constexpr u16 UHCI_USBCMD_GLOBAL_RESET = 0x0004;
|
||||||
|
@ -211,43 +209,44 @@ NonnullRefPtr<SysFSUSBDeviceInformation> SysFSUSBDeviceInformation::create(USB::
|
||||||
return adopt_ref(*new SysFSUSBDeviceInformation(device));
|
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
|
// 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.
|
// in a more broad singleton than this once we refactor things in USB subsystem.
|
||||||
SysFSUSBBusDirectory::initialize();
|
SysFSUSBBusDirectory::initialize();
|
||||||
|
|
||||||
PCI::enumerate([&](const PCI::Address& address, PCI::ID id) {
|
dmesgln("UHCI: Controller found {} @ {}", PCI::get_id(pci_address()), pci_address());
|
||||||
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: I/O base {}", m_io_base);
|
dmesgln("UHCI: I/O base {}", m_io_base);
|
||||||
dmesgln("UHCI: Interrupt line: {}", PCI::get_interrupt_line(pci_address()));
|
dmesgln("UHCI: Interrupt line: {}", PCI::get_interrupt_line(pci_address()));
|
||||||
|
|
||||||
reset();
|
spawn_port_proc();
|
||||||
start();
|
|
||||||
|
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()
|
UNMAP_AFTER_INIT UHCIController::~UHCIController()
|
||||||
|
@ -275,9 +274,10 @@ RefPtr<USB::Device> const UHCIController::get_device_from_address(u8 device_addr
|
||||||
return nullptr;
|
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);
|
write_usbcmd(UHCI_USBCMD_HOST_CONTROLLER_RESET);
|
||||||
|
|
||||||
|
@ -305,6 +305,8 @@ void UHCIController::reset()
|
||||||
// Disable UHCI Controller from raising an IRQ
|
// Disable UHCI Controller from raising an IRQ
|
||||||
write_usbintr(0);
|
write_usbintr(0);
|
||||||
dbgln("UHCI: Reset completed");
|
dbgln("UHCI: Reset completed");
|
||||||
|
|
||||||
|
return KSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
UNMAP_AFTER_INIT void UHCIController::create_structures()
|
UNMAP_AFTER_INIT void UHCIController::create_structures()
|
||||||
|
@ -464,7 +466,7 @@ TransferDescriptor* UHCIController::allocate_transfer_descriptor() const
|
||||||
return nullptr; // Huh?! We're outta TDs!!
|
return nullptr; // Huh?! We're outta TDs!!
|
||||||
}
|
}
|
||||||
|
|
||||||
void UHCIController::stop()
|
KResult UHCIController::stop()
|
||||||
{
|
{
|
||||||
write_usbcmd(read_usbcmd() & ~UHCI_USBCMD_RUN);
|
write_usbcmd(read_usbcmd() & ~UHCI_USBCMD_RUN);
|
||||||
// FIXME: Timeout
|
// FIXME: Timeout
|
||||||
|
@ -472,9 +474,10 @@ void UHCIController::stop()
|
||||||
if (read_usbsts() & UHCI_USBSTS_HOST_CONTROLLER_HALTED)
|
if (read_usbsts() & UHCI_USBSTS_HOST_CONTROLLER_HALTED)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return KSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UHCIController::start()
|
KResult UHCIController::start()
|
||||||
{
|
{
|
||||||
write_usbcmd(read_usbcmd() | UHCI_USBCMD_RUN);
|
write_usbcmd(read_usbcmd() | UHCI_USBCMD_RUN);
|
||||||
// FIXME: Timeout
|
// FIXME: Timeout
|
||||||
|
@ -483,6 +486,7 @@ void UHCIController::start()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dbgln("UHCI: Started");
|
dbgln("UHCI: Started");
|
||||||
|
return KSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
TransferDescriptor* UHCIController::create_transfer_descriptor(Pipe& pipe, PacketID direction, size_t data_len)
|
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());
|
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;
|
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())
|
if (device.is_error())
|
||||||
dmesgln("UHCI: Device creation failed on port 1 ({})", device.error());
|
dmesgln("UHCI: Device creation failed on port 1 ({})", device.error());
|
||||||
|
@ -730,7 +734,7 @@ void UHCIController::spawn_port_proc()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
port_data = UHCIController::the().read_portsc2();
|
port_data = read_portsc2();
|
||||||
if (port_data & UHCI_PORTSC_CONNECT_STATUS_CHANGED) {
|
if (port_data & UHCI_PORTSC_CONNECT_STATUS_CHANGED) {
|
||||||
if (port_data & UHCI_PORTSC_CURRRENT_CONNECT_STATUS) {
|
if (port_data & UHCI_PORTSC_CURRRENT_CONNECT_STATUS) {
|
||||||
dmesgln("UHCI: Device attach detected on Root Port 2");
|
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);
|
write_portsc2(port_data | UHCI_PORTSC_PORT_ENABLED);
|
||||||
dbgln("port should be enabled now: {:#04x}\n", read_portsc2());
|
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;
|
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())
|
if (device.is_error())
|
||||||
dmesgln("UHCI: Device creation failed on port 2 ({})", device.error());
|
dmesgln("UHCI: Device creation failed on port 2 ({})", device.error());
|
||||||
|
|
|
@ -12,8 +12,7 @@
|
||||||
#include <AK/NonnullOwnPtr.h>
|
#include <AK/NonnullOwnPtr.h>
|
||||||
#include <Kernel/Bus/PCI/Device.h>
|
#include <Kernel/Bus/PCI/Device.h>
|
||||||
#include <Kernel/Bus/USB/UHCIDescriptorTypes.h>
|
#include <Kernel/Bus/USB/UHCIDescriptorTypes.h>
|
||||||
#include <Kernel/Bus/USB/USBDevice.h>
|
#include <Kernel/Bus/USB/USBController.h>
|
||||||
#include <Kernel/Bus/USB/USBTransfer.h>
|
|
||||||
#include <Kernel/IO.h>
|
#include <Kernel/IO.h>
|
||||||
#include <Kernel/Memory/AnonymousVMObject.h>
|
#include <Kernel/Memory/AnonymousVMObject.h>
|
||||||
#include <Kernel/Process.h>
|
#include <Kernel/Process.h>
|
||||||
|
@ -21,30 +20,31 @@
|
||||||
|
|
||||||
namespace Kernel::USB {
|
namespace Kernel::USB {
|
||||||
|
|
||||||
class UHCIController final : public PCI::Device {
|
class UHCIController final
|
||||||
|
: public USBController
|
||||||
|
, public PCI::Device {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void detect();
|
static KResultOr<NonnullRefPtr<UHCIController>> try_to_initialize(PCI::Address address);
|
||||||
static UHCIController& the();
|
|
||||||
|
|
||||||
virtual ~UHCIController() override;
|
virtual ~UHCIController() override;
|
||||||
|
|
||||||
virtual StringView purpose() const override { return "UHCI"; }
|
virtual StringView purpose() const override { return "UHCI"; }
|
||||||
|
|
||||||
void reset();
|
virtual KResult initialize() override;
|
||||||
void stop();
|
virtual KResult reset() override;
|
||||||
void start();
|
virtual KResult stop() override;
|
||||||
|
virtual KResult start() override;
|
||||||
void spawn_port_proc();
|
void spawn_port_proc();
|
||||||
|
|
||||||
void do_debug_transfer();
|
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);
|
virtual RefPtr<USB::Device> const get_device_at_port(USB::Device::PortNumber) override;
|
||||||
RefPtr<USB::Device> const get_device_from_address(u8 device_address);
|
virtual RefPtr<USB::Device> const get_device_from_address(u8 device_address) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UHCIController(PCI::Address, PCI::ID);
|
explicit UHCIController(PCI::Address);
|
||||||
|
|
||||||
u16 read_usbcmd() { return m_io_base.offset(0).in<u16>(); }
|
u16 read_usbcmd() { return m_io_base.offset(0).in<u16>(); }
|
||||||
u16 read_usbsts() { return m_io_base.offset(0x2).in<u16>(); }
|
u16 read_usbsts() { return m_io_base.offset(0x2).in<u16>(); }
|
||||||
|
|
17
Kernel/Bus/USB/USBController.cpp
Normal file
17
Kernel/Bus/USB/USBController.cpp
Normal 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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
Kernel/Bus/USB/USBController.h
Normal file
42
Kernel/Bus/USB/USBController.h
Normal 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>;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -5,24 +5,24 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/OwnPtr.h>
|
#include <AK/OwnPtr.h>
|
||||||
|
#include <AK/String.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
#include <AK/Vector.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/USBDescriptors.h>
|
||||||
#include <Kernel/Bus/USB/USBDevice.h>
|
#include <Kernel/Bus/USB/USBDevice.h>
|
||||||
#include <Kernel/Bus/USB/USBRequest.h>
|
#include <Kernel/Bus/USB/USBRequest.h>
|
||||||
|
#include <Kernel/StdLib.h>
|
||||||
static u32 s_next_usb_address = 1; // Next address we hand out to a device once it's plugged into the machine
|
|
||||||
|
|
||||||
namespace Kernel::USB {
|
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())
|
if (pipe_or_error.is_error())
|
||||||
return pipe_or_error.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)
|
if (!device)
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
|
|
||||||
|
@ -33,10 +33,11 @@ KResultOr<NonnullRefPtr<Device>> Device::try_create(PortNumber port, DeviceSpeed
|
||||||
return device.release_nonnull();
|
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_port(port)
|
||||||
, m_device_speed(speed)
|
, m_device_speed(speed)
|
||||||
, m_address(0)
|
, m_address(0)
|
||||||
|
, m_controller(controller)
|
||||||
, m_default_pipe(move(default_pipe))
|
, m_default_pipe(move(default_pipe))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -83,8 +84,10 @@ KResult Device::enumerate()
|
||||||
dbgln("Number of configurations: {:02x}", dev_descriptor.num_configurations);
|
dbgln("Number of configurations: {:02x}", dev_descriptor.num_configurations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_address = m_controller->allocate_address();
|
||||||
|
|
||||||
// Attempt to set devices address on the bus
|
// 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())
|
if (transfer_length_or_error.is_error())
|
||||||
return transfer_length_or_error.error();
|
return transfer_length_or_error.error();
|
||||||
|
@ -92,7 +95,6 @@ KResult Device::enumerate()
|
||||||
transfer_length = transfer_length_or_error.release_value();
|
transfer_length = transfer_length_or_error.release_value();
|
||||||
|
|
||||||
VERIFY(transfer_length > 0);
|
VERIFY(transfer_length > 0);
|
||||||
m_address = s_next_usb_address++;
|
|
||||||
|
|
||||||
memcpy(&m_device_descriptor, &dev_descriptor, sizeof(USBDeviceDescriptor));
|
memcpy(&m_device_descriptor, &dev_descriptor, sizeof(USBDeviceDescriptor));
|
||||||
return KSuccess;
|
return KSuccess;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
namespace Kernel::USB {
|
namespace Kernel::USB {
|
||||||
|
|
||||||
|
class USBController;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Some nice info from FTDI on device enumeration and how some of this
|
// Some nice info from FTDI on device enumeration and how some of this
|
||||||
// glues together:
|
// glues together:
|
||||||
|
@ -30,9 +32,9 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
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();
|
~Device();
|
||||||
|
|
||||||
KResult enumerate();
|
KResult enumerate();
|
||||||
|
@ -54,6 +56,7 @@ private:
|
||||||
u16 m_product_id { 0 }; // This device's product ID assigned by the USB group
|
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
|
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
|
NonnullOwnPtr<Pipe> m_default_pipe; // Default communication pipe (endpoint0) used during enumeration
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
74
Kernel/Bus/USB/USBManagement.cpp
Normal file
74
Kernel/Bus/USB/USBManagement.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
Kernel/Bus/USB/USBManagement.h
Normal file
30
Kernel/Bus/USB/USBManagement.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -11,17 +11,18 @@
|
||||||
|
|
||||||
namespace Kernel::USB {
|
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)
|
if (!pipe)
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
|
|
||||||
return pipe.release_nonnull();
|
return pipe.release_nonnull();
|
||||||
}
|
}
|
||||||
|
|
||||||
Pipe::Pipe(Type type, Pipe::Direction direction, u16 max_packet_size)
|
Pipe::Pipe(USBController const& controller, Type type, Pipe::Direction direction, u16 max_packet_size)
|
||||||
: m_type(type)
|
: m_controller(controller)
|
||||||
|
, m_type(type)
|
||||||
, m_direction(direction)
|
, m_direction(direction)
|
||||||
, m_endpoint_address(0)
|
, m_endpoint_address(0)
|
||||||
, m_max_packet_size(max_packet_size)
|
, 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]])
|
Pipe::Pipe(USBController const& controller, Type type, Direction direction, USBEndpointDescriptor& endpoint [[maybe_unused]])
|
||||||
: m_type(type)
|
: m_controller(controller)
|
||||||
|
, m_type(type)
|
||||||
, m_direction(direction)
|
, m_direction(direction)
|
||||||
{
|
{
|
||||||
// TODO: decode endpoint structure
|
// TODO: decode endpoint structure
|
||||||
}
|
}
|
||||||
|
|
||||||
Pipe::Pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, u8 poll_interval, i8 device_address)
|
Pipe::Pipe(USBController const& controller, Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, u8 poll_interval, i8 device_address)
|
||||||
: m_type(type)
|
: m_controller(controller)
|
||||||
|
, m_type(type)
|
||||||
, m_direction(direction)
|
, m_direction(direction)
|
||||||
, m_device_address(device_address)
|
, m_device_address(device_address)
|
||||||
, m_endpoint_address(endpoint_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);
|
transfer->set_setup_packet(usb_request);
|
||||||
|
|
||||||
dbgln_if(USB_DEBUG, "Pipe: Transfer allocated @ {}", transfer->buffer_physical());
|
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())
|
if (transfer_len_or_error.is_error())
|
||||||
return transfer_len_or_error.error();
|
return transfer_len_or_error.error();
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
namespace Kernel::USB {
|
namespace Kernel::USB {
|
||||||
|
|
||||||
|
class USBController;
|
||||||
|
|
||||||
//
|
//
|
||||||
// A pipe is the logical connection between a memory buffer on the PC (host) and
|
// 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
|
// an endpoint on the device. In this implementation, the data buffer the pipe connects
|
||||||
|
@ -39,7 +41,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
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; }
|
Type type() const { return m_type; }
|
||||||
Direction direction() const { return m_direction; }
|
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);
|
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(USBController const& controller, Type type, Direction direction, u16 max_packet_size);
|
||||||
Pipe(Type type, Direction direction, USBEndpointDescriptor& endpoint);
|
Pipe(USBController const& controller, 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, u8 endpoint_address, u16 max_packet_size, u8 poll_interval, i8 device_address);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Device;
|
friend class Device;
|
||||||
|
|
||||||
|
NonnullRefPtr<USBController> m_controller;
|
||||||
|
|
||||||
Type m_type;
|
Type m_type;
|
||||||
Direction m_direction;
|
Direction m_direction;
|
||||||
DeviceSpeed m_speed;
|
DeviceSpeed m_speed;
|
||||||
|
|
|
@ -32,7 +32,9 @@ set(KERNEL_SOURCES
|
||||||
Bus/PCI/Initializer.cpp
|
Bus/PCI/Initializer.cpp
|
||||||
Bus/PCI/WindowedMMIOAccess.cpp
|
Bus/PCI/WindowedMMIOAccess.cpp
|
||||||
Bus/USB/UHCIController.cpp
|
Bus/USB/UHCIController.cpp
|
||||||
|
Bus/USB/USBController.cpp
|
||||||
Bus/USB/USBDevice.cpp
|
Bus/USB/USBDevice.cpp
|
||||||
|
Bus/USB/USBManagement.cpp
|
||||||
Bus/USB/USBPipe.cpp
|
Bus/USB/USBPipe.cpp
|
||||||
Bus/USB/USBTransfer.cpp
|
Bus/USB/USBTransfer.cpp
|
||||||
CMOS.cpp
|
CMOS.cpp
|
||||||
|
|
|
@ -177,6 +177,11 @@ UNMAP_AFTER_INIT bool CommandLine::disable_uhci_controller() const
|
||||||
return contains("disable_uhci_controller"sv);
|
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
|
UNMAP_AFTER_INIT bool CommandLine::disable_virtio() const
|
||||||
{
|
{
|
||||||
return contains("disable_virtio"sv);
|
return contains("disable_virtio"sv);
|
||||||
|
|
|
@ -73,6 +73,7 @@ public:
|
||||||
[[nodiscard]] bool disable_physical_storage() const;
|
[[nodiscard]] bool disable_physical_storage() const;
|
||||||
[[nodiscard]] bool disable_ps2_controller() const;
|
[[nodiscard]] bool disable_ps2_controller() const;
|
||||||
[[nodiscard]] bool disable_uhci_controller() const;
|
[[nodiscard]] bool disable_uhci_controller() const;
|
||||||
|
[[nodiscard]] bool disable_usb() const;
|
||||||
[[nodiscard]] bool disable_virtio() const;
|
[[nodiscard]] bool disable_virtio() const;
|
||||||
[[nodiscard]] AHCIResetMode ahci_reset_mode() const;
|
[[nodiscard]] AHCIResetMode ahci_reset_mode() const;
|
||||||
[[nodiscard]] String userspace_init() const;
|
[[nodiscard]] String userspace_init() const;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include <Kernel/BootInfo.h>
|
#include <Kernel/BootInfo.h>
|
||||||
#include <Kernel/Bus/PCI/Access.h>
|
#include <Kernel/Bus/PCI/Access.h>
|
||||||
#include <Kernel/Bus/PCI/Initializer.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/CMOS.h>
|
||||||
#include <Kernel/CommandLine.h>
|
#include <Kernel/CommandLine.h>
|
||||||
#include <Kernel/Devices/FullDevice.h>
|
#include <Kernel/Devices/FullDevice.h>
|
||||||
|
@ -300,7 +300,7 @@ void init_stage2(void*)
|
||||||
|
|
||||||
auto boot_profiling = kernel_command_line().is_boot_profiling_enabled();
|
auto boot_profiling = kernel_command_line().is_boot_profiling_enabled();
|
||||||
|
|
||||||
USB::UHCIController::detect();
|
USB::USBManagement::initialize();
|
||||||
|
|
||||||
BIOSSysFSDirectory::initialize();
|
BIOSSysFSDirectory::initialize();
|
||||||
ACPI::ACPISysFSDirectory::initialize();
|
ACPI::ACPISysFSDirectory::initialize();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue