Skip to content

Commit

Permalink
Kernel/USB: Create controller base class and introduce USBManagement
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Lubrsi authored and awesomekling committed Aug 9, 2021
1 parent c5974d3 commit 7a86a8d
Show file tree
Hide file tree
Showing 14 changed files with 258 additions and 71 deletions.
68 changes: 36 additions & 32 deletions Kernel/Bus/USB/UHCIController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
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()));

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();
}
}
});
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::ID id)
UNMAP_AFTER_INIT UHCIController::UHCIController(PCI::Address address)
: 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: Interrupt line: {}", PCI::get_interrupt_line(pci_address()));

reset();
start();
}

UNMAP_AFTER_INIT UHCIController::~UHCIController()
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -464,17 +466,18 @@ 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
for (;;) {
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
Expand All @@ -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)
Expand Down Expand Up @@ -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());
Expand All @@ -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");
Expand All @@ -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());
Expand Down
26 changes: 13 additions & 13 deletions Kernel/Bus/USB/UHCIController.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,39 @@
#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>
#include <Kernel/Time/TimeManagement.h>

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>(); }
Expand Down
17 changes: 17 additions & 0 deletions Kernel/Bus/USB/USBController.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright (c) 2021, Luke Wilde <[email protected]>
*
* 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 changes: 42 additions & 0 deletions Kernel/Bus/USB/USBController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2021, Luke Wilde <[email protected]>
*
* 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>;
};

}
20 changes: 11 additions & 9 deletions Kernel/Bus/USB/USBDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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))
{
}
Expand Down Expand Up @@ -83,16 +84,17 @@ 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();

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;
Expand Down
7 changes: 5 additions & 2 deletions Kernel/Bus/USB/USBDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

namespace Kernel::USB {

class USBController;

//
// Some nice info from FTDI on device enumeration and how some of this
// glues together:
Expand All @@ -30,9 +32,9 @@ class Device : public RefCounted<Device> {
};

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();
Expand All @@ -54,6 +56,7 @@ class Device : public RefCounted<Device> {
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
};
}
Loading

0 comments on commit 7a86a8d

Please sign in to comment.