Kernel: Start work on USB stack
Current code can enumerate all xHCI devices and detect their type based on the class code on device or interface descriptors.
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include <kernel/Storage/ATA/AHCI/Controller.h>
|
||||
#include <kernel/Storage/ATA/ATAController.h>
|
||||
#include <kernel/Storage/NVMe/Controller.h>
|
||||
#include <kernel/USB/USBManager.h>
|
||||
|
||||
#define INVALID_VENDOR 0xFFFF
|
||||
#define MULTI_FUNCTION 0x80
|
||||
@@ -209,6 +210,20 @@ namespace Kernel::PCI
|
||||
dprintln("{}", res.error());
|
||||
break;
|
||||
}
|
||||
case 0x0C:
|
||||
{
|
||||
switch (pci_device.subclass())
|
||||
{
|
||||
case 0x03:
|
||||
if (auto res = USBManager::get().add_controller(pci_device); res.is_error())
|
||||
dprintln("{}", res.error());
|
||||
break;
|
||||
default:
|
||||
dprintln("unsupported serail bus controller (pci {2H}.{2H}.{2H})", pci_device.class_code(), pci_device.subclass(), pci_device.prog_if());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
272
kernel/kernel/USB/Device.cpp
Normal file
272
kernel/kernel/USB/Device.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
#include <kernel/Memory/DMARegion.h>
|
||||
#include <kernel/USB/Device.h>
|
||||
|
||||
#define DEBUG_USB 0
|
||||
#define USB_DUMP_DESCRIPTORS 0
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
BAN::ErrorOr<void> USBDevice::initialize()
|
||||
{
|
||||
TRY(initialize_control_endpoint());
|
||||
|
||||
auto buffer = TRY(DMARegion::create(PAGE_SIZE));
|
||||
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Standard | USB::RequestType::Device;
|
||||
request.bRequest = USB::Request::GET_DESCRIPTOR;
|
||||
request.wValue = 0x0100;
|
||||
request.wIndex = 0;
|
||||
request.wLength = sizeof(USBDeviceDescriptor);
|
||||
TRY(send_request(request, buffer->paddr()));
|
||||
|
||||
m_descriptor.descriptor = *reinterpret_cast<const USBDeviceDescriptor*>(buffer->vaddr());
|
||||
dprintln_if(DEBUG_USB, "device has {} configurations", m_descriptor.descriptor.bNumConfigurations);
|
||||
|
||||
for (uint32_t i = 0; i < m_descriptor.descriptor.bNumConfigurations; i++)
|
||||
{
|
||||
{
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Standard | USB::RequestType::Device;
|
||||
request.bRequest = USB::Request::GET_DESCRIPTOR;
|
||||
request.wValue = 0x0200 | i;
|
||||
request.wIndex = 0;
|
||||
request.wLength = sizeof(USBConfigurationDescriptor);
|
||||
TRY(send_request(request, buffer->paddr()));
|
||||
|
||||
auto configuration = *reinterpret_cast<const USBConfigurationDescriptor*>(buffer->vaddr());
|
||||
|
||||
dprintln_if(DEBUG_USB, " configuration {} is {} bytes", i, +configuration.wTotalLength);
|
||||
if (configuration.wTotalLength > buffer->size())
|
||||
{
|
||||
dwarnln(" our buffer is only {} bytes, skipping some fields...");
|
||||
configuration.wTotalLength = buffer->size();
|
||||
}
|
||||
|
||||
if (configuration.wTotalLength > request.wLength)
|
||||
{
|
||||
request.wLength = configuration.wTotalLength;
|
||||
TRY(send_request(request, buffer->paddr()));
|
||||
}
|
||||
}
|
||||
|
||||
auto configuration = *reinterpret_cast<const USBConfigurationDescriptor*>(buffer->vaddr());
|
||||
|
||||
BAN::Vector<InterfaceDescriptor> interfaces;
|
||||
TRY(interfaces.reserve(configuration.bNumInterfaces));
|
||||
|
||||
dprintln_if(DEBUG_USB, " configuration {} has {} interfaces", i, configuration.bNumInterfaces);
|
||||
|
||||
uintptr_t offset = configuration.bLength;
|
||||
for (uint32_t j = 0; j < configuration.bNumInterfaces; j++)
|
||||
{
|
||||
if (offset + sizeof(USBInterfaceDescritor) > buffer->size())
|
||||
break;
|
||||
auto interface = *reinterpret_cast<const USBInterfaceDescritor*>(buffer->vaddr() + offset);
|
||||
|
||||
BAN::Vector<EndpointDescriptor> endpoints;
|
||||
TRY(endpoints.reserve(interface.bNumEndpoints));
|
||||
|
||||
dprintln_if(DEBUG_USB, " interface {} has {} endpoints", j, interface.bNumEndpoints);
|
||||
|
||||
offset += interface.bLength;
|
||||
for (uint32_t k = 0; k < interface.bNumEndpoints; k++)
|
||||
{
|
||||
if (offset + sizeof(USBEndpointDescriptor) > buffer->size())
|
||||
break;
|
||||
auto endpoint = *reinterpret_cast<const USBEndpointDescriptor*>(buffer->vaddr() + offset);
|
||||
offset += endpoint.bLength;
|
||||
|
||||
TRY(endpoints.emplace_back(endpoint));
|
||||
}
|
||||
|
||||
TRY(interfaces.emplace_back(interface, BAN::move(endpoints)));
|
||||
}
|
||||
|
||||
TRY(m_descriptor.configurations.emplace_back(configuration, BAN::move(interfaces)));
|
||||
}
|
||||
|
||||
#if USB_DUMP_DESCRIPTORS
|
||||
const auto& descriptor = m_descriptor.descriptor;
|
||||
dprintln("device descriptor");
|
||||
dprintln(" bLength: {}", descriptor.bLength);
|
||||
dprintln(" bDescriptorType: {}", descriptor.bDescriptorType);
|
||||
dprintln(" bcdUSB: {}", descriptor.bcdUSB);
|
||||
dprintln(" bDeviceClass: {}", descriptor.bDeviceClass);
|
||||
dprintln(" bDeviceSubClass: {}", descriptor.bDeviceSubClass);
|
||||
dprintln(" bDeviceProtocol: {}", descriptor.bDeviceProtocol);
|
||||
dprintln(" bMaxPacketSize0: {}", descriptor.bMaxPacketSize0);
|
||||
dprintln(" idVendor: {}", descriptor.idVendor);
|
||||
dprintln(" idProduct: {}", descriptor.idProduct);
|
||||
dprintln(" bcdDevice: {}", descriptor.bcdDevice);
|
||||
dprintln(" iManufacturer: {}", descriptor.iManufacturer);
|
||||
dprintln(" iProduct: {}", descriptor.iProduct);
|
||||
dprintln(" iSerialNumber: {}", descriptor.iSerialNumber);
|
||||
dprintln(" bNumConfigurations: {}", descriptor.bNumConfigurations);
|
||||
|
||||
for (const auto& configuration : m_descriptor.configurations)
|
||||
{
|
||||
const auto& descriptor = configuration.desciptor;
|
||||
dprintln(" configuration");
|
||||
dprintln(" bLength: {}", descriptor.bLength);
|
||||
dprintln(" bDescriptorType: {}", descriptor.bDescriptorType);
|
||||
dprintln(" wTotalLength: {}", descriptor.wTotalLength);
|
||||
dprintln(" bNumInterfaces: {}", descriptor.bNumInterfaces);
|
||||
dprintln(" bConfigurationValue: {}", descriptor.bConfigurationValue);
|
||||
dprintln(" iConfiguration: {}", descriptor.iConfiguration);
|
||||
dprintln(" bmAttributes: {}", descriptor.bmAttributes);
|
||||
dprintln(" bMaxPower: {}", descriptor.bMaxPower);
|
||||
|
||||
for (const auto& interface : configuration.interfaces)
|
||||
{
|
||||
const auto& descriptor = interface.descriptor;
|
||||
dprintln(" interface");
|
||||
dprintln(" bLength: {}", descriptor.bLength);
|
||||
dprintln(" bDescriptorType: {}", descriptor.bDescriptorType);
|
||||
dprintln(" bInterfaceNumber: {}", descriptor.bInterfaceNumber);
|
||||
dprintln(" bAlternateSetting: {}", descriptor.bAlternateSetting);
|
||||
dprintln(" bNumEndpoints: {}", descriptor.bNumEndpoints);
|
||||
dprintln(" bInterfaceClass: {}", descriptor.bInterfaceClass);
|
||||
dprintln(" bInterfaceSubClass: {}", descriptor.bInterfaceSubClass);
|
||||
dprintln(" bInterfaceProtocol: {}", descriptor.bInterfaceProtocol);
|
||||
dprintln(" iInterface: {}", descriptor.iInterface);
|
||||
|
||||
for (const auto& endpoint : interface.endpoints)
|
||||
{
|
||||
const auto& descriptor = endpoint.descriptor;
|
||||
dprintln(" endpoint");
|
||||
dprintln(" bLength: {}", descriptor.bLength);
|
||||
dprintln(" bDescriptorType: {}", descriptor.bDescriptorType);
|
||||
dprintln(" bEndpointAddress: {}", descriptor.bEndpointAddress);
|
||||
dprintln(" bmAttributes: {}", descriptor.bmAttributes);
|
||||
dprintln(" wMaxPacketSize: {}", +descriptor.wMaxPacketSize);
|
||||
dprintln(" bInterval: {}", descriptor.bInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_descriptor.descriptor.bDeviceClass)
|
||||
{
|
||||
switch (static_cast<USB::DeviceBaseClass>(m_descriptor.descriptor.bDeviceClass))
|
||||
{
|
||||
case USB::DeviceBaseClass::CommunicationAndCDCControl:
|
||||
dprintln_if(DEBUG_USB, "Found CommunicationAndCDCControl device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::Hub:
|
||||
dprintln_if(DEBUG_USB, "Found Hub device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::BillboardDeviceClass:
|
||||
dprintln_if(DEBUG_USB, "Found BillboardDeviceClass device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::DiagnosticDevice:
|
||||
dprintln_if(DEBUG_USB, "Found DiagnosticDevice device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::Miscellaneous:
|
||||
dprintln_if(DEBUG_USB, "Found Miscellaneous device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::VendorSpecific:
|
||||
dprintln_if(DEBUG_USB, "Found VendorSpecific device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
default:
|
||||
dprintln_if(DEBUG_USB, "Invalid device base class {2H}", m_descriptor.descriptor.bDeviceClass);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
for (const auto& configuration : m_descriptor.configurations)
|
||||
{
|
||||
for (const auto& interface : configuration.interfaces)
|
||||
{
|
||||
switch (static_cast<USB::InterfaceBaseClass>(interface.descriptor.bInterfaceClass))
|
||||
{
|
||||
case USB::InterfaceBaseClass::Audio:
|
||||
dprintln_if(DEBUG_USB, "Found Audio interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::CommunicationAndCDCControl:
|
||||
dprintln_if(DEBUG_USB, "Found CommunicationAndCDCControl interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::HID:
|
||||
dprintln_if(DEBUG_USB, "Found HID interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Physical:
|
||||
dprintln_if(DEBUG_USB, "Found Physical interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Image:
|
||||
dprintln_if(DEBUG_USB, "Found Image interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Printer:
|
||||
dprintln_if(DEBUG_USB, "Found Printer interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::MassStorage:
|
||||
dprintln_if(DEBUG_USB, "Found MassStorage interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::CDCData:
|
||||
dprintln_if(DEBUG_USB, "Found CDCData interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::SmartCard:
|
||||
dprintln_if(DEBUG_USB, "Found SmartCard interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::ContentSecurity:
|
||||
dprintln_if(DEBUG_USB, "Found ContentSecurity interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Video:
|
||||
dprintln_if(DEBUG_USB, "Found Video interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::PersonalHealthcare:
|
||||
dprintln_if(DEBUG_USB, "Found PersonalHealthcare interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::AudioVideoDevice:
|
||||
dprintln_if(DEBUG_USB, "Found AudioVideoDevice interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::USBTypeCBridgeClass:
|
||||
dprintln_if(DEBUG_USB, "Found USBTypeCBridgeClass interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::USBBulkDisplayProtocolDeviceClass:
|
||||
dprintln_if(DEBUG_USB, "Found USBBulkDisplayProtocolDeviceClass interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::MCTPOverUSBProtocolEndpointDeviceClass:
|
||||
dprintln_if(DEBUG_USB, "Found MCTPOverUSBProtocolEndpointDeviceClass interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::I3CDeviceClass:
|
||||
dprintln_if(DEBUG_USB, "Found I3CDeviceClass interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::DiagnosticDevice:
|
||||
dprintln_if(DEBUG_USB, "Found DiagnosticDevice interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::WirelessController:
|
||||
dprintln_if(DEBUG_USB, "Found WirelessController interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Miscellaneous:
|
||||
dprintln_if(DEBUG_USB, "Found Miscellaneous interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::ApplicationSpecific:
|
||||
dprintln_if(DEBUG_USB, "Found ApplicationSpecific interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::VendorSpecific:
|
||||
dprintln_if(DEBUG_USB, "Found VendorSpecific interface");
|
||||
break;
|
||||
default:
|
||||
dprintln_if(DEBUG_USB, "Invalid interface base class {2H}", interface.descriptor.bInterfaceClass);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
USB::SpeedClass USBDevice::determine_speed_class(uint64_t bits_per_second)
|
||||
{
|
||||
if (bits_per_second <= 1'500'000)
|
||||
return USB::SpeedClass::LowSpeed;
|
||||
if (bits_per_second <= 12'000'000)
|
||||
return USB::SpeedClass::FullSpeed;
|
||||
else if (bits_per_second <= 480'000'000)
|
||||
return USB::SpeedClass::HighSpeed;
|
||||
return USB::SpeedClass::SuperSpeed;
|
||||
}
|
||||
|
||||
}
|
||||
57
kernel/kernel/USB/USBManager.cpp
Normal file
57
kernel/kernel/USB/USBManager.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include <BAN/UniqPtr.h>
|
||||
|
||||
#include <kernel/USB/USBManager.h>
|
||||
#include <kernel/USB/XHCI/Controller.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
static BAN::UniqPtr<USBManager> s_instance;
|
||||
|
||||
BAN::ErrorOr<void> USBManager::initialize()
|
||||
{
|
||||
ASSERT(!s_instance);
|
||||
auto manager = TRY(BAN::UniqPtr<USBManager>::create());
|
||||
s_instance = BAN::move(manager);
|
||||
return {};
|
||||
}
|
||||
|
||||
USBManager& USBManager::get()
|
||||
{
|
||||
ASSERT(s_instance);
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> USBManager::add_controller(PCI::Device& pci_device)
|
||||
{
|
||||
ASSERT(pci_device.class_code() == 0x0C);
|
||||
ASSERT(pci_device.subclass() == 0x03);
|
||||
|
||||
BAN::UniqPtr<USBController> controller;
|
||||
switch (pci_device.prog_if())
|
||||
{
|
||||
case 0x00:
|
||||
dprintln("Unsupported UHCI controller");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case 0x10:
|
||||
dprintln("Unsupported OHCI controller");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case 0x20:
|
||||
dprintln("Unsupported EHCI controller");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case 0x30:
|
||||
if (auto ret = XHCIController::initialize(pci_device); ret.is_error())
|
||||
dprintln("Could not initialize XHCI controller: {}", ret.error());
|
||||
else
|
||||
controller = ret.release_value();
|
||||
break;
|
||||
default:
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
TRY(m_controllers.push_back(BAN::move(controller)));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
507
kernel/kernel/USB/XHCI/Controller.cpp
Normal file
507
kernel/kernel/USB/XHCI/Controller.cpp
Normal file
@@ -0,0 +1,507 @@
|
||||
#include <BAN/Bitcast.h>
|
||||
#include <BAN/StringView.h>
|
||||
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
#include <kernel/Process.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
#include <kernel/USB/XHCI/Controller.h>
|
||||
#include <kernel/USB/XHCI/Device.h>
|
||||
|
||||
#define DEBUG_XHCI 0
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
XHCIController::XHCIController(PCI::Device& pci_device)
|
||||
: m_pci_device(pci_device)
|
||||
{ }
|
||||
|
||||
XHCIController::~XHCIController()
|
||||
{
|
||||
if (m_port_updater)
|
||||
m_port_updater->exit(0, SIGKILL);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<XHCIController>> XHCIController::initialize(PCI::Device& pci_device)
|
||||
{
|
||||
auto controller = TRY(BAN::UniqPtr<XHCIController>::create(pci_device));
|
||||
TRY(controller->initialize_impl());
|
||||
return controller;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::initialize_impl()
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "XHCI controller at PCI {2H}:{2H}:{2H}", m_pci_device.bus(), m_pci_device.dev(), m_pci_device.func());
|
||||
|
||||
m_pci_device.enable_bus_mastering();
|
||||
m_pci_device.enable_memory_space();
|
||||
|
||||
m_configuration_bar = TRY(m_pci_device.allocate_bar_region(0));
|
||||
if (m_configuration_bar->type() != PCI::BarType::MEM)
|
||||
{
|
||||
dwarnln("XHCI controller with non-memory configuration space");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (auto ret = reset_controller(); ret.is_error())
|
||||
{
|
||||
dwarnln("Could not reset XHCI Controller: {}", ret.error());
|
||||
return ret.release_error();
|
||||
}
|
||||
|
||||
auto& capabilities = capability_regs();
|
||||
dprintln_if(DEBUG_XHCI, " version {H}.{H}.{H}",
|
||||
+capabilities.major_revision,
|
||||
capabilities.minor_revision >> 4,
|
||||
capabilities.minor_revision & 0x0F
|
||||
);
|
||||
dprintln_if(DEBUG_XHCI, " max slots {}", +capabilities.hcsparams1.max_slots);
|
||||
dprintln_if(DEBUG_XHCI, " max intrs {}", +capabilities.hcsparams1.max_interrupters);
|
||||
dprintln_if(DEBUG_XHCI, " max ports {}", +capabilities.hcsparams1.max_ports);
|
||||
|
||||
TRY(m_slots.resize(capabilities.hcsparams1.max_slots));
|
||||
TRY(m_ports.resize(capabilities.hcsparams1.max_ports));
|
||||
|
||||
TRY(initialize_ports());
|
||||
|
||||
auto& operational = operational_regs();
|
||||
|
||||
// allocate and program dcbaa
|
||||
m_dcbaa_region = TRY(DMARegion::create(capabilities.hcsparams1.max_slots * 8));
|
||||
memset(reinterpret_cast<void*>(m_dcbaa_region->vaddr()), 0, m_dcbaa_region->size());
|
||||
operational.dcbaap_lo = m_dcbaa_region->paddr() & 0xFFFFFFFF;
|
||||
operational.dcbaap_hi = m_dcbaa_region->paddr() >> 32;
|
||||
|
||||
// allocate and program crcr
|
||||
TRY(m_command_completions.resize(m_command_ring_trb_count));
|
||||
m_command_ring_region = TRY(DMARegion::create(m_command_ring_trb_count * sizeof(XHCI::TRB)));
|
||||
memset(reinterpret_cast<void*>(m_command_ring_region->vaddr()), 0, m_command_ring_region->size());
|
||||
operational.crcr_lo = m_command_ring_region->paddr() | XHCI::CRCR::RingCycleState;
|
||||
operational.crcr_hi = m_command_ring_region->paddr() >> 32;
|
||||
|
||||
TRY(initialize_primary_interrupter());
|
||||
|
||||
// enable the controller
|
||||
operational.usbcmd.run_stop = 1;
|
||||
while (operational.usbsts & XHCI::USBSTS::HCHalted)
|
||||
continue;
|
||||
|
||||
m_port_updater = Process::create_kernel([](void* data) { reinterpret_cast<XHCIController*>(data)->port_updater_task(); }, this);
|
||||
if (m_port_updater == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::initialize_ports()
|
||||
{
|
||||
auto& capabilities = capability_regs();
|
||||
uint8_t max_ports = capabilities.hcsparams1.max_ports;
|
||||
|
||||
ASSERT(m_ports.size() == max_ports);
|
||||
|
||||
{
|
||||
uint16_t ext_offset = capabilities.hccparams1.xhci_extended_capabilities_pointer;
|
||||
if (ext_offset == 0)
|
||||
{
|
||||
dwarnln("XHCI controller does not have extended capabilities");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
vaddr_t ext_addr = m_configuration_bar->vaddr() + ext_offset * 4;
|
||||
while (true)
|
||||
{
|
||||
auto& ext_cap = *reinterpret_cast<volatile XHCI::ExtendedCap*>(ext_addr);
|
||||
|
||||
if (ext_cap.capability_id == XHCI::ExtendedCapabilityID::SupportedProtocol)
|
||||
{
|
||||
auto& protocol = reinterpret_cast<volatile XHCI::SupportedPrococolCap&>(ext_cap);
|
||||
|
||||
if (protocol.name_string != *reinterpret_cast<const uint32_t*>("USB "))
|
||||
{
|
||||
dwarnln("Invalid port protocol name string");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
if (protocol.compatible_port_offset == 0 || protocol.compatible_port_offset + protocol.compatible_port_count - 1 > max_ports)
|
||||
{
|
||||
dwarnln("Invalid port specified in SupportedProtocols");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < protocol.compatible_port_count; i++)
|
||||
{
|
||||
auto& port = m_ports[protocol.compatible_port_offset + i - 1];
|
||||
port.revision_major = protocol.major_revision;
|
||||
port.revision_minor = protocol.minor_revision;
|
||||
port.slot_type = protocol.protocol_slot_type;
|
||||
|
||||
for (size_t j = 0; j < protocol.protocol_speed_id_count; j++)
|
||||
{
|
||||
uint32_t speed_info = reinterpret_cast<const volatile uint32_t*>(ext_addr + sizeof(XHCI::SupportedPrococolCap))[j];
|
||||
uint32_t port_speed = speed_info >> 16;
|
||||
for (size_t exp = 0; exp < ((speed_info >> 4) & 0x03); exp++)
|
||||
port_speed *= 1000;
|
||||
port.speed_id_to_speed[speed_info & 0x0F] = port_speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ext_cap.next_capability == 0)
|
||||
break;
|
||||
ext_addr += ext_cap.next_capability * 4;
|
||||
}
|
||||
}
|
||||
|
||||
// set max slots enabled
|
||||
auto& operational = operational_regs();
|
||||
operational.config.max_device_slots_enabled = capabilities.hcsparams1.max_slots;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::initialize_primary_interrupter()
|
||||
{
|
||||
TRY(m_pci_device.reserve_irqs(1));
|
||||
|
||||
auto& runtime = runtime_regs();
|
||||
|
||||
static constexpr size_t event_ring_table_offset = m_event_ring_trb_count * sizeof(XHCI::TRB);
|
||||
|
||||
m_event_ring_region = TRY(DMARegion::create(m_event_ring_trb_count * sizeof(XHCI::TRB) + sizeof(XHCI::EventRingTableEntry)));
|
||||
memset(reinterpret_cast<void*>(m_event_ring_region->vaddr()), 0, m_event_ring_region->size());
|
||||
|
||||
auto& event_ring_table_entry = *reinterpret_cast<XHCI::EventRingTableEntry*>(m_event_ring_region->vaddr() + event_ring_table_offset);
|
||||
event_ring_table_entry.rsba = m_event_ring_region->paddr();
|
||||
event_ring_table_entry.rsz = m_event_ring_trb_count;
|
||||
|
||||
auto& primary_interrupter = runtime.irs[0];
|
||||
primary_interrupter.erstsz = 1;
|
||||
primary_interrupter.erdp = m_event_ring_region->paddr();
|
||||
primary_interrupter.erstba = m_event_ring_region->paddr() + event_ring_table_offset;
|
||||
|
||||
auto& operational = operational_regs();
|
||||
operational.usbcmd.interrupter_enable = 1;
|
||||
|
||||
primary_interrupter.iman = primary_interrupter.iman | XHCI::IMAN::InterruptPending | XHCI::IMAN::InterruptEnable;
|
||||
|
||||
set_irq(m_pci_device.get_irq(0));
|
||||
enable_interrupt();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Mutex s_port_mutex;
|
||||
|
||||
void XHCIController::port_updater_task()
|
||||
{
|
||||
// allow initial pass of port iteration because controller
|
||||
// does not send Port Status Change event for already
|
||||
// attached ports
|
||||
m_port_changed = true;
|
||||
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
bool expected { true };
|
||||
while (!m_port_changed.compare_exchange(expected, false))
|
||||
{
|
||||
m_port_semaphore.block_with_timeout(100);
|
||||
expected = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_ports.size(); i++)
|
||||
{
|
||||
LockGuard _(s_port_mutex);
|
||||
|
||||
auto& my_port = m_ports[i];
|
||||
if (my_port.revision_major == 0)
|
||||
continue;
|
||||
|
||||
auto& op_port = operational_regs().ports[i];
|
||||
|
||||
if (!(op_port.portsc & XHCI::PORTSC::PP))
|
||||
continue;
|
||||
|
||||
// read and clear needed change flags
|
||||
const bool reset_change = op_port.portsc & XHCI::PORTSC::PRC;
|
||||
const bool connection_change = op_port.portsc & XHCI::PORTSC::CSC;
|
||||
op_port.portsc = XHCI::PORTSC::CSC | XHCI::PORTSC::PRC | XHCI::PORTSC::PP;
|
||||
|
||||
if (!(op_port.portsc & XHCI::PORTSC::CCS))
|
||||
{
|
||||
// if device detached, clear the port
|
||||
if (my_port.slot_id != 0)
|
||||
{
|
||||
m_slots[my_port.slot_id - 1].clear();
|
||||
my_port.slot_id = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (my_port.revision_major)
|
||||
{
|
||||
case 2:
|
||||
if (!reset_change)
|
||||
{
|
||||
if (connection_change)
|
||||
op_port.portsc = XHCI::PORTSC::PR | XHCI::PORTSC::PP;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!connection_change)
|
||||
continue;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto ret = initialize_slot(i); ret.is_error())
|
||||
{
|
||||
dwarnln("Could not initialize USB {H}.{H} device: {}",
|
||||
my_port.revision_major,
|
||||
my_port.revision_minor >> 4,
|
||||
ret.error()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::initialize_slot(int port_index)
|
||||
{
|
||||
auto& my_port = m_ports[port_index];
|
||||
|
||||
XHCI::TRB enable_slot { .enable_slot_command {} };
|
||||
enable_slot.enable_slot_command.trb_type = XHCI::TRBType::EnableSlotCommand;
|
||||
enable_slot.enable_slot_command.slot_type = my_port.slot_type;
|
||||
auto result = TRY(send_command(enable_slot));
|
||||
|
||||
uint8_t slot_id = result.command_completion_event.slot_id;
|
||||
if (slot_id == 0 || slot_id > capability_regs().hcsparams1.max_slots)
|
||||
{
|
||||
dwarnln("EnableSlot gave an invalid slot {}", slot_id);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
dprintln_if(DEBUG_XHCI, "allocated slot {} for port {}", slot_id, port_index + 1);
|
||||
|
||||
m_slots[slot_id - 1] = TRY(XHCIDevice::create(*this, port_index + 1, slot_id));
|
||||
if (auto ret = m_slots[slot_id - 1]->initialize(); ret.is_error())
|
||||
{
|
||||
dwarnln("Could not initialize device on slot {}: {}", slot_id, ret.error());
|
||||
m_slots[slot_id - 1].clear();
|
||||
return ret.release_error();
|
||||
}
|
||||
|
||||
my_port.slot_id = slot_id;
|
||||
|
||||
dprintln_if(DEBUG_XHCI, "device on slot {} initialized", slot_id);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::reset_controller()
|
||||
{
|
||||
auto& operational = operational_regs();
|
||||
|
||||
const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + 500;
|
||||
|
||||
// wait until controller not ready clears
|
||||
while (operational.usbsts & XHCI::USBSTS::ControllerNotReady)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
|
||||
// issue software reset and wait for it to clear
|
||||
operational.usbcmd.host_controller_reset = 1;
|
||||
while (operational.usbcmd.host_controller_reset)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<XHCI::TRB> XHCIController::send_command(const XHCI::TRB& trb)
|
||||
{
|
||||
LockGuard _(m_mutex);
|
||||
|
||||
auto& command_trb = reinterpret_cast<volatile XHCI::TRB*>(m_command_ring_region->vaddr())[m_command_enqueue];
|
||||
command_trb.raw.dword0 = trb.raw.dword0;
|
||||
command_trb.raw.dword1 = trb.raw.dword1;
|
||||
command_trb.raw.dword2 = trb.raw.dword2;
|
||||
command_trb.raw.dword3 = trb.raw.dword3;
|
||||
command_trb.cycle = m_command_cycle;
|
||||
|
||||
auto& completion_trb = const_cast<volatile XHCI::TRB&>(m_command_completions[m_command_enqueue]);
|
||||
completion_trb.raw.dword0 = 0;
|
||||
completion_trb.raw.dword1 = 0;
|
||||
completion_trb.raw.dword2 = 0;
|
||||
completion_trb.raw.dword3 = 0;
|
||||
|
||||
advance_command_enqueue();
|
||||
|
||||
doorbell_reg(0) = 0;
|
||||
|
||||
uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + 1000;
|
||||
while ((__atomic_load_n(&completion_trb.raw.dword2, __ATOMIC_SEQ_CST) >> 24) == 0)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
|
||||
if (completion_trb.command_completion_event.completion_code != 1)
|
||||
{
|
||||
dwarnln("Completion error: {}", +completion_trb.command_completion_event.completion_code);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
return BAN::bit_cast<XHCI::TRB>(completion_trb);
|
||||
}
|
||||
|
||||
void XHCIController::advance_command_enqueue()
|
||||
{
|
||||
m_command_enqueue++;
|
||||
if (m_command_enqueue < m_command_ring_trb_count - 1)
|
||||
return;
|
||||
|
||||
auto& link_trb = reinterpret_cast<volatile XHCI::TRB*>(m_command_ring_region->vaddr())[m_command_enqueue].link_trb;
|
||||
link_trb.trb_type = XHCI::TRBType::Link;
|
||||
link_trb.ring_segment_ponter = m_command_ring_region->paddr();
|
||||
link_trb.interrupter_target = 0;
|
||||
link_trb.cycle_bit = m_command_cycle;
|
||||
link_trb.toggle_cycle = 1;
|
||||
link_trb.chain_bit = 0;
|
||||
link_trb.interrupt_on_completion = 0;
|
||||
|
||||
m_command_enqueue = 0;
|
||||
m_command_cycle = !m_command_cycle;
|
||||
}
|
||||
|
||||
void XHCIController::handle_irq()
|
||||
{
|
||||
auto& operational = operational_regs();
|
||||
if (!(operational.usbsts & XHCI::USBSTS::EventInterrupt))
|
||||
return;
|
||||
operational.usbsts = XHCI::USBSTS::EventInterrupt;
|
||||
|
||||
auto& primary_interrupter = runtime_regs().irs[0];
|
||||
primary_interrupter.iman = primary_interrupter.iman | XHCI::IMAN::InterruptPending | XHCI::IMAN::InterruptEnable;
|
||||
|
||||
if (current_event_trb().cycle == m_event_cycle)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
auto& trb = current_event_trb();
|
||||
if (trb.cycle != m_event_cycle)
|
||||
break;
|
||||
|
||||
switch (trb.trb_type)
|
||||
{
|
||||
case XHCI::TRBType::TransferEvent:
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "TransferEvent");
|
||||
|
||||
const uint32_t slot_id = trb.transfer_event.slot_id;
|
||||
if (slot_id == 0 || slot_id > m_slots.size() || !m_slots[slot_id - 1])
|
||||
{
|
||||
dwarnln("TransferEvent for invalid slot {}", slot_id);
|
||||
dwarnln("Completion error: {}", +trb.transfer_event.completion_code);
|
||||
break;
|
||||
}
|
||||
|
||||
m_slots[slot_id - 1]->on_transfer_event(trb);
|
||||
|
||||
break;
|
||||
}
|
||||
case XHCI::TRBType::CommandCompletionEvent:
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "CommandCompletionEvent");
|
||||
|
||||
const uint32_t trb_index = (trb.command_completion_event.command_trb_pointer - m_command_ring_region->paddr()) / sizeof(XHCI::TRB);
|
||||
|
||||
// NOTE: dword2 is last (and atomic) as that is what send_command is waiting for
|
||||
auto& completion_trb = const_cast<volatile XHCI::TRB&>(m_command_completions[trb_index]);
|
||||
completion_trb.raw.dword0 = trb.raw.dword0;
|
||||
completion_trb.raw.dword1 = trb.raw.dword1;
|
||||
completion_trb.raw.dword3 = trb.raw.dword3;
|
||||
__atomic_store_n(&completion_trb.raw.dword2, trb.raw.dword2, __ATOMIC_SEQ_CST);
|
||||
|
||||
break;
|
||||
}
|
||||
case XHCI::TRBType::PortStatusChangeEvent:
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "PortStatusChangeEvent");
|
||||
uint8_t port_id = trb.port_status_chage_event.port_id;
|
||||
if (port_id > capability_regs().hcsparams1.max_ports)
|
||||
{
|
||||
dwarnln("PortStatusChangeEvent on non-existent port {}", port_id);
|
||||
break;
|
||||
}
|
||||
m_port_changed = true;
|
||||
m_port_semaphore.unblock();
|
||||
break;
|
||||
}
|
||||
case XHCI::TRBType::BandwidthRequestEvent:
|
||||
dwarnln("Unhandled BandwidthRequestEvent");
|
||||
break;
|
||||
case XHCI::TRBType::DoorbellEvent:
|
||||
dwarnln("Unhandled DoorbellEvent");
|
||||
break;
|
||||
case XHCI::TRBType::HostControllerEvent:
|
||||
dwarnln("Unhandled HostControllerEvent");
|
||||
break;
|
||||
case XHCI::TRBType::DeviceNotificationEvent:
|
||||
dwarnln("Unhandled DeviceNotificationEvent");
|
||||
break;
|
||||
case XHCI::TRBType::MFINDEXWrapEvent:
|
||||
dwarnln("Unhandled MFINDEXWrapEvent");
|
||||
break;
|
||||
default:
|
||||
dwarnln("Unrecognized event TRB type {}", +trb.trb_type);
|
||||
break;
|
||||
}
|
||||
|
||||
m_event_dequeue++;
|
||||
if (m_event_dequeue >= m_event_ring_trb_count)
|
||||
{
|
||||
m_event_dequeue = 0;
|
||||
m_event_cycle = !m_event_cycle;
|
||||
|
||||
derrorln("WRAP");
|
||||
}
|
||||
}
|
||||
|
||||
primary_interrupter.erdp = (m_event_ring_region->paddr() + (m_event_dequeue * sizeof(XHCI::TRB))) | XHCI::ERDP::EventHandlerBusy;
|
||||
}
|
||||
}
|
||||
|
||||
volatile XHCI::CapabilityRegs& XHCIController::capability_regs()
|
||||
{
|
||||
return *reinterpret_cast<volatile XHCI::CapabilityRegs*>(m_configuration_bar->vaddr());
|
||||
}
|
||||
|
||||
volatile XHCI::OperationalRegs& XHCIController::operational_regs()
|
||||
{
|
||||
return *reinterpret_cast<volatile XHCI::OperationalRegs*>(m_configuration_bar->vaddr() + capability_regs().caplength);
|
||||
}
|
||||
|
||||
volatile XHCI::RuntimeRegs& XHCIController::runtime_regs()
|
||||
{
|
||||
return *reinterpret_cast<volatile XHCI::RuntimeRegs*>(m_configuration_bar->vaddr() + (capability_regs().rstoff & ~0x1Fu));
|
||||
}
|
||||
|
||||
volatile uint32_t& XHCIController::doorbell_reg(uint32_t slot_id)
|
||||
{
|
||||
return reinterpret_cast<volatile uint32_t*>(m_configuration_bar->vaddr() + capability_regs().dboff)[slot_id];
|
||||
}
|
||||
|
||||
const volatile XHCI::TRB& XHCIController::current_event_trb()
|
||||
{
|
||||
return reinterpret_cast<const volatile XHCI::TRB*>(m_event_ring_region->vaddr())[m_event_dequeue];;
|
||||
}
|
||||
|
||||
volatile uint64_t& XHCIController::dcbaa_reg(uint32_t slot_id)
|
||||
{
|
||||
return reinterpret_cast<volatile uint64_t*>(m_dcbaa_region->vaddr())[slot_id];
|
||||
}
|
||||
|
||||
}
|
||||
278
kernel/kernel/USB/XHCI/Device.cpp
Normal file
278
kernel/kernel/USB/XHCI/Device.cpp
Normal file
@@ -0,0 +1,278 @@
|
||||
#include <BAN/Bitcast.h>
|
||||
#include <BAN/ByteSpan.h>
|
||||
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
#include <kernel/USB/XHCI/Device.h>
|
||||
|
||||
#define DEBUG_XHCI 0
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<XHCIDevice>> XHCIDevice::create(XHCIController& controller, uint32_t port_id, uint32_t slot_id)
|
||||
{
|
||||
return TRY(BAN::UniqPtr<XHCIDevice>::create(controller, port_id, slot_id));
|
||||
}
|
||||
|
||||
XHCIDevice::~XHCIDevice()
|
||||
{
|
||||
XHCI::TRB disable_slot { .disable_slot_command {} };
|
||||
disable_slot.disable_slot_command.trb_type = XHCI::TRBType::DisableSlotCommand;
|
||||
disable_slot.disable_slot_command.slot_id = m_slot_id;
|
||||
if (auto ret = m_controller.send_command(disable_slot); ret.is_error())
|
||||
dwarnln("Could not disable slot {}: {}", m_slot_id, ret.error());
|
||||
else
|
||||
dprintln_if(DEBUG_XHCI, "Slot {} disabled", m_slot_id);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIDevice::initialize_control_endpoint()
|
||||
{
|
||||
const uint32_t context_size = m_controller.context_size_set() ? 64 : 32;
|
||||
|
||||
const uint32_t portsc = m_controller.operational_regs().ports[m_port_id - 1].portsc;
|
||||
const uint32_t speed_id = (portsc >> XHCI::PORTSC::PORT_SPEED_SHIFT) & XHCI::PORTSC::PORT_SPEED_MASK;
|
||||
const uint32_t bits_per_second = m_controller.port(m_port_id).speed_id_to_speed[speed_id];
|
||||
const auto speed_class = determine_speed_class(bits_per_second);
|
||||
|
||||
m_max_packet_size = 0;
|
||||
switch (speed_class)
|
||||
{
|
||||
case USB::SpeedClass::LowSpeed:
|
||||
case USB::SpeedClass::FullSpeed:
|
||||
m_max_packet_size = 8;
|
||||
break;
|
||||
case USB::SpeedClass::HighSpeed:
|
||||
m_max_packet_size = 64;
|
||||
break;
|
||||
case USB::SpeedClass::SuperSpeed:
|
||||
m_max_packet_size = 512;
|
||||
break;
|
||||
default: ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
m_input_context = TRY(DMARegion::create(33 * context_size));
|
||||
memset(reinterpret_cast<void*>(m_input_context->vaddr()), 0, m_input_context->size());
|
||||
|
||||
m_output_context = TRY(DMARegion::create(32 * context_size));
|
||||
memset(reinterpret_cast<void*>(m_output_context->vaddr()), 0, m_output_context->size());
|
||||
|
||||
m_endpoints[0].transfer_ring = TRY(DMARegion::create(m_transfer_ring_trb_count * sizeof(XHCI::TRB)));
|
||||
memset(reinterpret_cast<void*>(m_endpoints[0].transfer_ring->vaddr()), 0, m_endpoints[0].transfer_ring->size());
|
||||
|
||||
{
|
||||
auto& input_control_context = *reinterpret_cast<XHCI::InputControlContext*>(m_input_context->vaddr() + 0 * context_size);
|
||||
auto& slot_context = *reinterpret_cast<XHCI::SlotContext*> (m_input_context->vaddr() + 1 * context_size);
|
||||
auto& endpoint0_context = *reinterpret_cast<XHCI::EndpointContext*> (m_input_context->vaddr() + 2 * context_size);
|
||||
|
||||
input_control_context.add_context_flags = 0b11;
|
||||
|
||||
slot_context.root_hub_port_number = m_port_id;
|
||||
slot_context.route_string = 0;
|
||||
slot_context.context_entries = 1;
|
||||
slot_context.interrupter_target = 0;
|
||||
slot_context.speed = speed_id;
|
||||
|
||||
endpoint0_context.endpoint_type = XHCI::EndpointType::Control;
|
||||
endpoint0_context.max_packet_size = m_max_packet_size;
|
||||
endpoint0_context.max_burst_size = 0;
|
||||
endpoint0_context.tr_dequeue_pointer = m_endpoints[0].transfer_ring->paddr() | 1;
|
||||
endpoint0_context.interval = 0;
|
||||
endpoint0_context.max_primary_streams = 0;
|
||||
endpoint0_context.mult = 0;
|
||||
endpoint0_context.error_count = 3;
|
||||
}
|
||||
|
||||
m_controller.dcbaa_reg(m_slot_id) = m_output_context->paddr();
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
XHCI::TRB address_device { .address_device_command = {} };
|
||||
address_device.address_device_command.trb_type = XHCI::TRBType::AddressDeviceCommand;
|
||||
address_device.address_device_command.input_context_pointer = m_input_context->paddr();
|
||||
// NOTE: some legacy devices require sending request with BSR=1 before actual BSR=0
|
||||
address_device.address_device_command.block_set_address_request = (i == 0);
|
||||
address_device.address_device_command.slot_id = m_slot_id;
|
||||
TRY(m_controller.send_command(address_device));
|
||||
}
|
||||
|
||||
// NOTE: Full speed devices can have other max packet sizes than 8
|
||||
if (speed_class == USB::SpeedClass::FullSpeed)
|
||||
TRY(update_actual_max_packet_size());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIDevice::update_actual_max_packet_size()
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "Retrieving actual max packet size of full speed device");
|
||||
|
||||
BAN::Vector<uint8_t> buffer;
|
||||
TRY(buffer.resize(8, 0));
|
||||
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Standard | USB::RequestType::Device;
|
||||
request.bRequest = USB::Request::GET_DESCRIPTOR;
|
||||
request.wValue = 0x0100;
|
||||
request.wIndex = 0;
|
||||
request.wLength = 8;
|
||||
TRY(send_request(request, kmalloc_paddr_of((vaddr_t)buffer.data()).value()));
|
||||
|
||||
m_max_packet_size = buffer.back();
|
||||
|
||||
const uint32_t context_size = m_controller.context_size_set() ? 64 : 32;
|
||||
|
||||
{
|
||||
auto& input_control_context = *reinterpret_cast<XHCI::InputControlContext*>(m_input_context->vaddr() + 0 * context_size);
|
||||
auto& endpoint0_context = *reinterpret_cast<XHCI::EndpointContext*> (m_input_context->vaddr() + 2 * context_size);
|
||||
|
||||
input_control_context.add_context_flags = 0b10;
|
||||
|
||||
endpoint0_context.endpoint_type = XHCI::EndpointType::Control;
|
||||
endpoint0_context.max_packet_size = m_max_packet_size;
|
||||
endpoint0_context.max_burst_size = 0;
|
||||
endpoint0_context.tr_dequeue_pointer = (m_endpoints[0].transfer_ring->paddr() + (m_endpoints[0].enqueue_index * sizeof(XHCI::TRB))) | 1;
|
||||
endpoint0_context.interval = 0;
|
||||
endpoint0_context.max_primary_streams = 0;
|
||||
endpoint0_context.mult = 0;
|
||||
endpoint0_context.error_count = 3;
|
||||
}
|
||||
|
||||
XHCI::TRB evaluate_context { .address_device_command = {} };
|
||||
evaluate_context.address_device_command.trb_type = XHCI::TRBType::EvaluateContextCommand;
|
||||
evaluate_context.address_device_command.input_context_pointer = m_input_context->paddr();
|
||||
evaluate_context.address_device_command.block_set_address_request = 0;
|
||||
evaluate_context.address_device_command.slot_id = m_slot_id;
|
||||
TRY(m_controller.send_command(evaluate_context));
|
||||
|
||||
dprintln_if(DEBUG_XHCI, "successfully updated max packet size to {}", m_max_packet_size);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void XHCIDevice::on_transfer_event(const volatile XHCI::TRB& trb)
|
||||
{
|
||||
ASSERT(trb.trb_type == XHCI::TRBType::TransferEvent);
|
||||
if (trb.transfer_event.endpoint_id == 0)
|
||||
{
|
||||
dwarnln("TransferEvent for endpoint id 0");
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: dword2 is last (and atomic) as that is what send_request is waiting for
|
||||
auto& completion_trb = m_endpoints[trb.transfer_event.endpoint_id - 1].completion_trb;
|
||||
completion_trb.raw.dword0 = trb.raw.dword0;
|
||||
completion_trb.raw.dword1 = trb.raw.dword1;
|
||||
completion_trb.raw.dword3 = trb.raw.dword3;
|
||||
__atomic_store_n(&completion_trb.raw.dword2, trb.raw.dword2, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIDevice::send_request(const USBDeviceRequest& request, paddr_t buffer_paddr)
|
||||
{
|
||||
// minus 3: Setup, Status, Link
|
||||
if (request.wLength > (m_transfer_ring_trb_count - 3) * m_max_packet_size)
|
||||
return BAN::Error::from_errno((ENOBUFS));
|
||||
|
||||
auto& endpoint = m_endpoints[0];
|
||||
LockGuard _(endpoint.mutex);
|
||||
|
||||
auto* transfer_trb_arr = reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr());
|
||||
|
||||
{
|
||||
auto& trb = transfer_trb_arr[endpoint.enqueue_index];
|
||||
memset((void*)&trb, 0, sizeof(XHCI::TRB));
|
||||
|
||||
trb.setup_stage.trb_type = XHCI::TRBType::SetupStage;
|
||||
trb.setup_stage.transfer_type = 3;
|
||||
trb.setup_stage.trb_transfer_length = 8;
|
||||
trb.setup_stage.interrupt_on_completion = 0;
|
||||
trb.setup_stage.immediate_data = 1;
|
||||
trb.setup_stage.cycle_bit = endpoint.cycle_bit;
|
||||
|
||||
trb.setup_stage.bmRequestType = request.bmRequestType;
|
||||
trb.setup_stage.bRequest = request.bRequest;
|
||||
trb.setup_stage.wValue = request.wValue;
|
||||
trb.setup_stage.wIndex = request.wIndex;
|
||||
trb.setup_stage.wLength = request.wLength;
|
||||
|
||||
advance_endpoint_enqueue(endpoint, false);
|
||||
}
|
||||
|
||||
uint32_t bytes_handled = 0;
|
||||
while (bytes_handled < request.wLength)
|
||||
{
|
||||
const uint32_t to_handle = BAN::Math::min<uint32_t>(m_max_packet_size, request.wLength - bytes_handled);
|
||||
|
||||
auto& trb = transfer_trb_arr[endpoint.enqueue_index];
|
||||
memset((void*)&trb, 0, sizeof(XHCI::TRB));
|
||||
|
||||
trb.data_stage.trb_type = XHCI::TRBType::DataStage;
|
||||
trb.data_stage.direction = 1;
|
||||
trb.data_stage.trb_transfer_length = to_handle;
|
||||
trb.data_stage.chain_bit = (bytes_handled + to_handle < request.wLength);
|
||||
trb.data_stage.interrupt_on_completion = 0;
|
||||
trb.data_stage.immediate_data = 0;
|
||||
trb.data_stage.data_buffer_pointer = buffer_paddr + bytes_handled;
|
||||
trb.data_stage.cycle_bit = endpoint.cycle_bit;
|
||||
|
||||
bytes_handled += to_handle;
|
||||
|
||||
advance_endpoint_enqueue(endpoint, false);
|
||||
}
|
||||
|
||||
{
|
||||
auto& trb = transfer_trb_arr[endpoint.enqueue_index];
|
||||
memset((void*)&trb, 0, sizeof(XHCI::TRB));
|
||||
|
||||
trb.status_stage.trb_type = XHCI::TRBType::StatusStage;
|
||||
trb.status_stage.direction = 0;
|
||||
trb.status_stage.chain_bit = 0;
|
||||
trb.status_stage.interrupt_on_completion = 1;
|
||||
trb.data_stage.cycle_bit = endpoint.cycle_bit;
|
||||
|
||||
advance_endpoint_enqueue(endpoint, false);
|
||||
}
|
||||
|
||||
auto& completion_trb = endpoint.completion_trb;
|
||||
completion_trb.raw.dword0 = 0;
|
||||
completion_trb.raw.dword1 = 0;
|
||||
completion_trb.raw.dword2 = 0;
|
||||
completion_trb.raw.dword3 = 0;
|
||||
|
||||
m_controller.doorbell_reg(m_slot_id) = 1;
|
||||
|
||||
const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + 1000;
|
||||
while ((__atomic_load_n(&completion_trb.raw.dword2, __ATOMIC_SEQ_CST) >> 24) == 0)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
|
||||
if (completion_trb.transfer_event.completion_code != 1)
|
||||
{
|
||||
dwarnln("Completion error: {}", +completion_trb.transfer_event.completion_code);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void XHCIDevice::advance_endpoint_enqueue(Endpoint& endpoint, bool chain)
|
||||
{
|
||||
endpoint.enqueue_index++;
|
||||
if (endpoint.enqueue_index < m_transfer_ring_trb_count - 1)
|
||||
return;
|
||||
|
||||
// This is the last TRB in transfer ring. Make it link to the beginning of the ring
|
||||
auto& link_trb = reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr())[endpoint.enqueue_index].link_trb;
|
||||
link_trb.trb_type = XHCI::TRBType::Link;
|
||||
link_trb.ring_segment_ponter = endpoint.transfer_ring->paddr();
|
||||
link_trb.interrupter_target = 0;
|
||||
link_trb.cycle_bit = endpoint.cycle_bit;
|
||||
link_trb.toggle_cycle = 1;
|
||||
link_trb.chain_bit = chain;
|
||||
link_trb.interrupt_on_completion = 0;
|
||||
|
||||
endpoint.enqueue_index = 0;
|
||||
endpoint.cycle_bit = !endpoint.cycle_bit;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <kernel/Terminal/Serial.h>
|
||||
#include <kernel/Terminal/VirtualTTY.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
#include <kernel/USB/USBManager.h>
|
||||
|
||||
#include <LibInput/KeyboardLayout.h>
|
||||
|
||||
@@ -205,6 +206,8 @@ static void init2(void*)
|
||||
|
||||
MUST(NetworkManager::initialize());
|
||||
|
||||
MUST(USBManager::initialize());
|
||||
|
||||
// NOTE: PCI devices are the last ones to be initialized
|
||||
// so other devices can reserve predefined interrupts
|
||||
PCI::PCIManager::get().initialize_devices();
|
||||
|
||||
Reference in New Issue
Block a user