From ad143c184f9a1f4f1d8fb3ea504a795bc5ff94f4 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Mon, 10 Feb 2025 22:56:25 +0200 Subject: [PATCH] Kernel: Add basic support for USB hubs This is still buggy and some hubs lead to usb transaction errors. I'll have to debug this but this shouldn't prevent any already working device from working --- kernel/CMakeLists.txt | 2 + kernel/include/kernel/Debug.h | 1 + kernel/include/kernel/USB/Controller.h | 16 + kernel/include/kernel/USB/Device.h | 26 +- kernel/include/kernel/USB/Hub/Definitions.h | 95 +++++ kernel/include/kernel/USB/Hub/HubDriver.h | 57 +++ kernel/include/kernel/USB/XHCI/Controller.h | 4 +- kernel/include/kernel/USB/XHCI/Device.h | 10 +- kernel/kernel/USB/Controller.cpp | 40 ++ kernel/kernel/USB/Device.cpp | 23 +- kernel/kernel/USB/Hub/HubDriver.cpp | 451 ++++++++++++++++++++ kernel/kernel/USB/XHCI/Controller.cpp | 20 +- kernel/kernel/USB/XHCI/Device.cpp | 48 ++- 13 files changed, 780 insertions(+), 13 deletions(-) create mode 100644 kernel/include/kernel/USB/Hub/Definitions.h create mode 100644 kernel/include/kernel/USB/Hub/HubDriver.h create mode 100644 kernel/kernel/USB/Controller.cpp create mode 100644 kernel/kernel/USB/Hub/HubDriver.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 36155f89..daf5b738 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -96,10 +96,12 @@ set(KERNEL_SOURCES kernel/Timer/PIT.cpp kernel/Timer/RTC.cpp kernel/Timer/Timer.cpp + kernel/USB/Controller.cpp kernel/USB/Device.cpp kernel/USB/HID/HIDDriver.cpp kernel/USB/HID/Keyboard.cpp kernel/USB/HID/Mouse.cpp + kernel/USB/Hub/HubDriver.cpp kernel/USB/MassStorage/MassStorageDriver.cpp kernel/USB/MassStorage/SCSIDevice.cpp kernel/USB/USBManager.cpp diff --git a/kernel/include/kernel/Debug.h b/kernel/include/kernel/Debug.h index 78e1a8e0..d36e9b3b 100644 --- a/kernel/include/kernel/Debug.h +++ b/kernel/include/kernel/Debug.h @@ -65,6 +65,7 @@ #define DEBUG_XHCI 0 #define DEBUG_USB 0 #define DEBUG_USB_HID 0 +#define DEBUG_USB_HUB 0 #define DEBUG_USB_KEYBOARD 0 #define DEBUG_USB_MOUSE 0 #define DEBUG_USB_MASS_STORAGE 0 diff --git a/kernel/include/kernel/USB/Controller.h b/kernel/include/kernel/USB/Controller.h index 252684d1..b9d8bfee 100644 --- a/kernel/include/kernel/USB/Controller.h +++ b/kernel/include/kernel/USB/Controller.h @@ -1,10 +1,26 @@ #pragma once +#include +#include + namespace Kernel { class USBController { + // NOTE: Tier 0 == Root Hub + + public: + USBController(); + + uint8_t current_hub_init_tier() const; + void register_hub_to_init(uint8_t tier); + void mark_hub_init_done(uint8_t tier); + + private: + mutable SpinLock m_hub_init_lock; + uint8_t m_current_hub_init_tier { 0 }; + BAN::Array m_hubs_to_init_per_tier; }; } diff --git a/kernel/include/kernel/USB/Device.h b/kernel/include/kernel/USB/Device.h index fc1e1b46..b63d5de9 100644 --- a/kernel/include/kernel/USB/Device.h +++ b/kernel/include/kernel/USB/Device.h @@ -55,9 +55,18 @@ namespace Kernel BAN::Vector configurations; }; + struct HubInfo + { + uint8_t number_of_ports; + bool multi_tt; + uint8_t tt_think_time; + }; + public: - USBDevice(USB::SpeedClass speed_class) - : m_speed_class(speed_class) + USBDevice(USBController& controller, USB::SpeedClass speed_class, uint8_t depth) + : m_controller(controller) + , m_speed_class(speed_class) + , m_depth(depth) {} virtual ~USBDevice() = default; @@ -68,13 +77,20 @@ namespace Kernel const BAN::Vector& configurations() { return m_descriptor.configurations; } - virtual BAN::ErrorOr configure_endpoint(const USBEndpointDescriptor&) = 0; + virtual BAN::ErrorOr initialize_device_on_hub_port(uint8_t port_id, USB::SpeedClass) = 0; + virtual void deinitialize_device_slot(uint8_t slot_id) = 0; + + virtual BAN::ErrorOr configure_endpoint(const USBEndpointDescriptor&, const HubInfo& = {}) = 0; virtual BAN::ErrorOr send_request(const USBDeviceRequest&, paddr_t buffer) = 0; virtual void send_data_buffer(uint8_t endpoint_id, paddr_t buffer, size_t buffer_len) = 0; USB::SpeedClass speed_class() const { return m_speed_class; } uint8_t depth() const { return m_depth; } + bool can_start_hub_init() const { return m_controller.current_hub_init_tier() >= m_depth + 1; } + void register_hub_to_init() { m_controller.register_hub_to_init(m_depth + 1); }; + void mark_hub_init_done() { m_controller.mark_hub_init_done(m_depth + 1); }; + protected: void handle_stall(uint8_t endpoint_id); void handle_input_data(size_t byte_count, uint8_t endpoint_id); @@ -83,8 +99,12 @@ namespace Kernel private: BAN::ErrorOr parse_configuration(size_t index); + private: + USBController& m_controller; + protected: const USB::SpeedClass m_speed_class; + const uint8_t m_depth; private: DeviceDescriptor m_descriptor; diff --git a/kernel/include/kernel/USB/Hub/Definitions.h b/kernel/include/kernel/USB/Hub/Definitions.h new file mode 100644 index 00000000..01d3ff09 --- /dev/null +++ b/kernel/include/kernel/USB/Hub/Definitions.h @@ -0,0 +1,95 @@ +#pragma once + +#include + +namespace Kernel::USBHub +{ + + struct HubDescriptor + { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bNbrPorts; + uint16_t wHubCharacteristics; + uint8_t bPowerOnGood; + uint8_t bHubContrCurrent; + uint8_t bitmaps[]; + } __attribute__((packed)); + static_assert(sizeof(HubDescriptor) == 7); + + struct PortStatus + { + union + { + uint16_t raw; + struct { + uint16_t connection : 1; // bit 0 + uint16_t enable : 1; // bit 1 + uint16_t : 1; + uint16_t over_current : 1; // bit 3 + uint16_t reset : 1; // bit 4 + }; + struct { + uint16_t : 2; + uint16_t suspend : 1; // bit 2 + uint16_t : 5; + uint16_t power : 1; // bit 8 + uint16_t low_speed : 1; // bit 9 + uint16_t high_speed : 1; // bit 10 + } usb2; + struct { + uint16_t : 5; + uint16_t link_state : 4; // bit 5-8 + uint16_t power : 1; // bit 9 + uint16_t speed : 3; // bit 10-12 + } usb3; + } wPortStatus; + union + { + uint16_t raw; + struct { + uint16_t connection : 1; // bit 0 + uint16_t : 2; + uint16_t over_current : 1; // bit 3 + uint16_t reset : 1; // bit 4 + }; + struct { + uint16_t : 1; + uint16_t enable : 1; // bit 1 + uint16_t suspend : 1; // bit 2 + } usb2; + struct { + uint16_t : 5; + uint16_t bh_reset : 1; // bit 5 + uint16_t link_state : 1; // bit 6 + uint16_t config_error : 1; // bit 7 + } usb3; + } wPortChanged; + }; + static_assert(sizeof(PortStatus) == 4); + + enum HubSelector + { + C_HUB_LOCAL_POWER = 0, + C_HUB_OVER_CURRENT = 1, + PORT_CONNECTION = 0, + PORT_ENABLE = 1, + PORT_SUSPEND = 2, + PORT_OVER_CURRENT = 3, + PORT_RESET = 4, + PORT_LINK_STATE = 5, + PORT_POWER = 8, + PORT_LOW_SPEED = 9, + C_PORT_CONNECTION = 16, + C_PORT_ENABLE = 17, + C_PORT_SUSPEND = 18, + C_PORT_OVER_CURRENT = 19, + C_PORT_RESET = 20, + PORT_TEST = 21, + PORT_INDICATOR = 22, + C_PORT_LINK_STATE = 25, + C_PORT_CONFIG_ERROR = 26, + C_BH_PORT_RESET = 29, + }; + +} diff --git a/kernel/include/kernel/USB/Hub/HubDriver.h b/kernel/include/kernel/USB/Hub/HubDriver.h new file mode 100644 index 00000000..838ec70f --- /dev/null +++ b/kernel/include/kernel/USB/Hub/HubDriver.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +namespace Kernel +{ + + class USBHubDriver final : public USBClassDriver + { + public: + BAN::ErrorOr initialize() override; + + void handle_stall(uint8_t endpoint_id) override; + void handle_input_data(size_t byte_count, uint8_t endpoint_id) override; + + private: + USBHubDriver(USBDevice&, const USBDevice::DeviceDescriptor&); + ~USBHubDriver(); + + void port_updater_task(); + + BAN::ErrorOr clear_port_feature(uint8_t port, uint8_t feature); + BAN::ErrorOr set_port_feature(uint8_t port, uint8_t feature); + + private: + USBDevice& m_device; + const USBDevice::DeviceDescriptor& m_descriptor; + + BAN::UniqPtr m_data_region; + uint8_t m_endpoint_id { 0 }; + + uint8_t m_port_count { 0 }; + + BAN::Atomic m_changed_ports { 0 }; + ThreadBlocker m_changed_port_blocker; + BAN::Atomic m_port_updater { nullptr }; + + struct PortInfo + { + uint8_t slot { 0 }; + bool failed { false }; + }; + BAN::Vector m_ports; + + BAN::Atomic m_running { true }; + + bool m_is_usb2 { false }; + bool m_is_usb3 { false }; + bool m_is_multi_tt { false }; + + bool m_is_init_done { false }; + + friend class BAN::UniqPtr; + }; + +} diff --git a/kernel/include/kernel/USB/XHCI/Controller.h b/kernel/include/kernel/USB/XHCI/Controller.h index 93af6232..76861432 100644 --- a/kernel/include/kernel/USB/XHCI/Controller.h +++ b/kernel/include/kernel/USB/XHCI/Controller.h @@ -46,7 +46,7 @@ namespace Kernel void port_updater_task(); - BAN::ErrorOr initialize_device(uint32_t route_string, USB::SpeedClass speed_class); + BAN::ErrorOr initialize_device(uint32_t route_string, uint8_t depth, USB::SpeedClass speed_class, XHCIDevice* parent_hub, uint8_t parent_port_id); void deinitialize_slot(uint8_t slot_id); BAN::ErrorOr send_command(const XHCI::TRB&); @@ -77,6 +77,8 @@ namespace Kernel ThreadBlocker m_port_thread_blocker; BAN::Atomic m_port_changed { false }; + bool m_ports_initialized { false }; + PCI::Device& m_pci_device; BAN::UniqPtr m_configuration_bar; BAN::UniqPtr m_dcbaa_region; diff --git a/kernel/include/kernel/USB/XHCI/Device.h b/kernel/include/kernel/USB/XHCI/Device.h index 09c5fc06..3f189f2b 100644 --- a/kernel/include/kernel/USB/XHCI/Device.h +++ b/kernel/include/kernel/USB/XHCI/Device.h @@ -32,15 +32,21 @@ namespace Kernel struct Info { + XHCIDevice* parent_hub; + uint8_t parent_port_id; USB::SpeedClass speed_class; uint8_t slot_id; uint32_t route_string; + uint8_t depth; }; public: static BAN::ErrorOr> create(XHCIController&, const Info& info); - BAN::ErrorOr configure_endpoint(const USBEndpointDescriptor&) override; + BAN::ErrorOr initialize_device_on_hub_port(uint8_t port_id, USB::SpeedClass) override; + void deinitialize_device_slot(uint8_t slot_id) override; + + BAN::ErrorOr configure_endpoint(const USBEndpointDescriptor&, const HubInfo&) override; BAN::ErrorOr send_request(const USBDeviceRequest&, paddr_t buffer) override; void send_data_buffer(uint8_t endpoint_id, paddr_t buffer, size_t buffer_size) override; @@ -55,6 +61,8 @@ namespace Kernel BAN::ErrorOr update_actual_max_packet_size(); + bool is_multi_tt() const; + void on_interrupt_or_bulk_endpoint_event(XHCI::TRB); void advance_endpoint_enqueue(Endpoint&, bool chain); diff --git a/kernel/kernel/USB/Controller.cpp b/kernel/kernel/USB/Controller.cpp new file mode 100644 index 00000000..3dabf9fd --- /dev/null +++ b/kernel/kernel/USB/Controller.cpp @@ -0,0 +1,40 @@ +#include + +namespace Kernel +{ + + USBController::USBController() + { + for (auto& count : m_hubs_to_init_per_tier) + count = 0; + m_hubs_to_init_per_tier[0] = 1; + } + + uint8_t USBController::current_hub_init_tier() const + { + SpinLockGuard _(m_hub_init_lock); + return m_current_hub_init_tier; + } + + void USBController::register_hub_to_init(uint8_t tier) + { + ASSERT(tier >= 1); + ASSERT(tier <= m_hubs_to_init_per_tier.size()); + + SpinLockGuard _(m_hub_init_lock); + m_hubs_to_init_per_tier[tier]++; + if (tier < m_current_hub_init_tier) + m_current_hub_init_tier = tier; + } + + void USBController::mark_hub_init_done(uint8_t tier) + { + ASSERT(tier < m_hubs_to_init_per_tier.size()); + + SpinLockGuard _(m_hub_init_lock); + m_hubs_to_init_per_tier[tier]--; + if (tier == m_current_hub_init_tier && m_hubs_to_init_per_tier[tier] == 0) + m_current_hub_init_tier++; + } + +} diff --git a/kernel/kernel/USB/Device.cpp b/kernel/kernel/USB/Device.cpp index 1ba3ec1f..0f219db6 100644 --- a/kernel/kernel/USB/Device.cpp +++ b/kernel/kernel/USB/Device.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #define USB_DUMP_DESCRIPTORS 0 @@ -107,8 +108,26 @@ namespace Kernel 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); + { + if (auto ret = BAN::UniqPtr::create(*this, m_descriptor); !ret.is_error()) + TRY(m_class_drivers.push_back(ret.release_value())); + else + { + dwarnln("Failed to create USB Hub: {}", ret.error()); + return ret.release_error(); + } + + if (auto ret = m_class_drivers[0]->initialize(); ret.is_error()) + { + dwarnln("Failed to initialize USB Hub: {}", ret.error()); + m_class_drivers.clear(); + return ret.release_error(); + } + + dprintln_if(DEBUG_USB, "Successfully initialized USB hub"); + + return {}; + } case USB::DeviceBaseClass::BillboardDeviceClass: dprintln_if(DEBUG_USB, "Found BillboardDeviceClass device"); return BAN::Error::from_errno(ENOTSUP); diff --git a/kernel/kernel/USB/Hub/HubDriver.cpp b/kernel/kernel/USB/Hub/HubDriver.cpp new file mode 100644 index 00000000..1253d1f9 --- /dev/null +++ b/kernel/kernel/USB/Hub/HubDriver.cpp @@ -0,0 +1,451 @@ +#include +#include +#include +#include + +namespace Kernel +{ + + USBHubDriver::USBHubDriver(USBDevice& device, const USBDevice::DeviceDescriptor& desciptor) + : m_device(device) + , m_descriptor(desciptor) + {} + + USBHubDriver::~USBHubDriver() + { + m_running = false; + m_changed_port_blocker.unblock(); + while (m_port_updater) + continue; + + for (const auto info : m_ports) + { + if (info.slot == 0) + continue; + m_device.deinitialize_device_slot(info.slot); + } + } + + BAN::ErrorOr USBHubDriver::initialize() + { +#if DEBUG_USB_HUB + switch (m_device.speed_class()) + { + case USB::SpeedClass::LowSpeed: + dprintln("configuring low speed hub"); + break; + case USB::SpeedClass::FullSpeed: + dprintln("configuring full speed hub"); + break; + case USB::SpeedClass::HighSpeed: + dprintln("configuring high speed hub"); + break; + case USB::SpeedClass::SuperSpeed: + dprintln("configuring super speed hub"); + break; + } + #endif + + m_data_region = TRY(DMARegion::create(PAGE_SIZE)); + + if (m_descriptor.configurations.empty()) + { + dwarnln("USB hub does not have any configurations"); + return BAN::Error::from_errno(EFAULT); + } + const auto& configuration = m_descriptor.configurations[0]; + + { + dprintln_if(DEBUG_USB_HUB, "setting configuration"); + USBDeviceRequest request; + request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Standard | USB::RequestType::Device; + request.bRequest = USB::SET_CONFIGURATION; + request.wValue = configuration.desciptor.bConfigurationValue; + request.wIndex = 0; + request.wLength = 0; + TRY(m_device.send_request(request, 0)); + dprintln_if(DEBUG_USB_HUB, " -> done"); + } + + if (configuration.interfaces.empty()) + { + dwarnln("USB hub does not have any interfaces"); + return BAN::Error::from_errno(EFAULT); + } + const auto* interface = &configuration.interfaces[0]; + + // High speed hubs may have alternate multi tt interface + if (m_device.speed_class() == USB::SpeedClass::HighSpeed) + { + for (size_t i = 0; i < configuration.interfaces.size(); i++) + { + if (configuration.interfaces[i].descriptor.bInterfaceProtocol != 2) + continue; + interface = &configuration.interfaces[i]; + break; + } + + if (interface->descriptor.bAlternateSetting != 0) + { + dprintln_if(DEBUG_USB_HUB, "enabling multi tt interface"); + USBDeviceRequest request; + request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Standard | USB::RequestType::Interface; + request.bRequest = USB::SET_INTERFACE; + request.wValue = interface->descriptor.bAlternateSetting; + request.wIndex = interface->descriptor.iInterface; + request.wLength = 0; + TRY(m_device.send_request(request, 0)); + dprintln_if(DEBUG_USB_HUB, " -> done"); + } + + m_is_multi_tt = (interface->descriptor.bInterfaceProtocol == 2); + } + + if (interface->endpoints.size() != 1) + { + dwarnln("USB hub has {} endpoints", interface->endpoints.size()); + return BAN::Error::from_errno(EFAULT); + } + const auto& endpoint = interface->endpoints[0]; + + m_is_usb3 = (m_device.speed_class() == USB::SpeedClass::SuperSpeed); + m_is_usb2 = !m_is_usb3; + ASSERT(m_is_usb2 ^ m_is_usb3); + + // Set multi tt to false until we know tt think time + dprintln_if(DEBUG_USB_HUB, "configuring endpoint with 4 ports"); + TRY(m_device.configure_endpoint(endpoint.descriptor, { + .number_of_ports = m_port_count, + .multi_tt = false, + .tt_think_time = 0 + })); + dprintln_if(DEBUG_USB_HUB, " -> done"); + + // USB 3 devices use route_string and hub depth for routing packets + if (m_is_usb3) + { + dprintln_if(DEBUG_USB_HUB, "setting hub depth"); + USBDeviceRequest request; + request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Class | USB::RequestType::Device; + request.bRequest = 12; // SET_HUB_DEPTH + request.wValue = m_device.depth(); + request.wIndex = 0; + request.wLength = 0; + TRY(m_device.send_request(request, 0)); + dprintln_if(DEBUG_USB_HUB, " -> done"); + } + + uint8_t tt_think_time = 0; + + // Hub descriptor has to be requested after device is configured + { + dprintln_if(DEBUG_USB_HUB, "getting hub descriptor"); + USBDeviceRequest request; + request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Class | USB::RequestType::Device; + request.bRequest = USB::Request::GET_DESCRIPTOR; + request.wValue = m_is_usb3 ? 0x2A00 : 0x2900; + request.wIndex = 0; + request.wLength = 8; + if (TRY(m_device.send_request(request, m_data_region->paddr())) < 8) + { + dwarnln("USB hub did not respond with full hub descriptor"); + return BAN::Error::from_errno(EFAULT); + } + dprintln_if(DEBUG_USB_HUB, " -> done"); + + const auto& hub_descriptor = *reinterpret_cast(m_data_region->vaddr()); + + m_port_count = hub_descriptor.bNbrPorts; + + if (m_device.speed_class() == USB::SpeedClass::HighSpeed) + tt_think_time = (hub_descriptor.wHubCharacteristics >> 5) & 0x03; + } + + if (m_port_count > 31) + { + dwarnln("USB hubs only support up to 31 ports"); + m_port_count = 31; + } + + TRY(m_ports.resize(m_port_count)); + + dprintln_if(DEBUG_USB_HUB, "re-configuring endpoint with {} ports", m_port_count); + TRY(m_device.configure_endpoint(endpoint.descriptor, { + .number_of_ports = m_port_count, + .multi_tt = m_is_multi_tt, + .tt_think_time = tt_think_time + })); + dprintln_if(DEBUG_USB_HUB, " -> done"); + + m_endpoint_id = (endpoint.descriptor.bEndpointAddress & 0x0F) * 2 + !!(endpoint.descriptor.bEndpointAddress & 0x80); + m_device.send_data_buffer(m_endpoint_id, m_data_region->paddr() + sizeof(USBHub::PortStatus), m_port_count / 8 + 1); + + // Reset all ports in powered off state + for (size_t port_id = 1; port_id <= m_port_count; port_id++) + { + USBDeviceRequest request; + request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Class | USB::RequestType::Other; + request.bRequest = USB::Request::GET_STATUS; + request.wValue = 0; + request.wIndex = port_id; + request.wLength = sizeof(USBHub::PortStatus); + auto result = m_device.send_request(request, m_data_region->paddr()); + if (result.is_error() || result.value() != sizeof(USBHub::PortStatus)) + { + dwarnln("Failed to get port {} status"); + continue; + } + + const auto& port_status = *reinterpret_cast(m_data_region->vaddr()); + + const bool is_powered = + (m_is_usb3 && port_status.wPortStatus.usb3.power) || + (m_is_usb2 && port_status.wPortStatus.usb2.power); + + if (!is_powered) + if (auto ret = set_port_feature(port_id, USBHub::PORT_POWER); ret.is_error()) + dwarnln("Failed to power on USB hub port {}: {}", port_id, ret.error()); + + dprintln_if(DEBUG_USB_HUB, "port {}, status {4H}, changed {4H}", + port_id, + *(uint16_t*)(m_data_region->vaddr() + 0), + *(uint16_t*)(m_data_region->vaddr() + 2) + ); + + m_changed_ports |= 1u << port_id; + } + + m_port_updater = Process::create_kernel([](void* data) { reinterpret_cast(data)->port_updater_task(); }, this); + if (m_port_updater == nullptr) + return BAN::Error::from_errno(ENOMEM); + + return {}; + } + + BAN::ErrorOr USBHubDriver::clear_port_feature(uint8_t port, uint8_t feature) + { + USBDeviceRequest request; + request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Class | USB::RequestType::Other; + request.bRequest = USB::Request::CLEAR_FEATURE; + request.wValue = feature; + request.wIndex = port; + request.wLength = 0; + TRY(m_device.send_request(request, 0)); + return {}; + } + + BAN::ErrorOr USBHubDriver::set_port_feature(uint8_t port, uint8_t feature) + { + USBDeviceRequest request; + request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Class | USB::RequestType::Other; + request.bRequest = USB::Request::SET_FEATURE; + request.wValue = feature; + request.wIndex = port; + request.wLength = 0; + TRY(m_device.send_request(request, 0)); + return {}; + } + + void USBHubDriver::port_updater_task() + { + m_device.register_hub_to_init(); + + while (m_running && !m_device.can_start_hub_init()) + Processor::yield(); + + uint64_t last_port_update_ms = SystemTimer::get().ms_since_boot(); + while (m_running) + { + uint8_t changed_port = 0xFF; + + { + const auto temp = m_changed_ports.exchange(0); + if (temp == 0) + { + // If there has been no changed ports in 100ms, this hub has initialized its devices + if (!m_is_init_done && SystemTimer::get().ms_since_boot() - last_port_update_ms >= 100) + { + m_device.mark_hub_init_done(); + m_is_init_done = true; + } + + m_changed_port_blocker.block_with_timeout_ms(100); + continue; + } + + last_port_update_ms = SystemTimer::get().ms_since_boot(); + + for (size_t i = 0; i <= m_ports.size(); i++) + { + if (!(temp & (1u << i))) + continue; + changed_port = i; + break; + } + + m_changed_ports |= temp & ~(1u << changed_port); + } + + if (changed_port == 0) + { + dprintln_if(DEBUG_USB_HUB, "TODO: USB Hub changed"); + continue; + } + + { + USBDeviceRequest request; + request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Class | USB::RequestType::Other; + request.bRequest = USB::Request::GET_STATUS; + request.wValue = 0; + request.wIndex = changed_port; + request.wLength = sizeof(USBHub::PortStatus); + auto result = m_device.send_request(request, m_data_region->paddr()); + if (result.is_error() || result.value() != sizeof(USBHub::PortStatus)) + { + dwarnln("Failed to get port {} status", changed_port); + continue; + } + } + + const USBHub::PortStatus port_status { + .wPortStatus = { .raw = reinterpret_cast(m_data_region->vaddr())->wPortStatus.raw }, + .wPortChanged = { .raw = reinterpret_cast(m_data_region->vaddr())->wPortChanged.raw }, + }; + + if (port_status.wPortChanged.connection) + { + (void)clear_port_feature(changed_port, USBHub::C_PORT_CONNECTION); + + if (port_status.wPortStatus.connection) + { + // USB 2 devices have to be reset to enter enabled state + if (auto ret = set_port_feature(changed_port, USBHub::PORT_RESET); ret.is_error()) + dwarnln("Failed to reset USB hub port {}: {}", changed_port, ret.error()); + } + else + { + // Cleanup port on disconnection + auto& port_info = m_ports[changed_port - 1]; + if (port_info.slot != 0) + m_device.deinitialize_device_slot(port_info.slot); + port_info = {}; + } + } + + if (port_status.wPortChanged.reset) + { + // If this is USB3 device and the port is not in enabled state don't + // clear reset changed so hub will trigger another port changed interrupt + if (!port_status.wPortStatus.connection || !m_is_usb3 || port_status.wPortStatus.enable) + (void)clear_port_feature(changed_port, USBHub::C_PORT_RESET); + } + + if (port_status.wPortChanged.over_current) + { + (void)clear_port_feature(changed_port, USBHub::C_PORT_OVER_CURRENT); + dwarnln_if(DEBUG_USB_HUB, "TODO: USB hub port over current change"); + } + + if (m_is_usb2) + { + if (port_status.wPortChanged.usb2.enable) + (void)clear_port_feature(changed_port, USBHub::C_PORT_ENABLE); + + if (port_status.wPortChanged.usb2.suspend) + { + (void)clear_port_feature(changed_port, USBHub::C_PORT_SUSPEND); + dwarnln_if(DEBUG_USB_HUB, "TODO: USB hub port suspend change"); + } + } + else if (m_is_usb3) + { + if (port_status.wPortChanged.usb3.link_state) + (void)clear_port_feature(changed_port, USBHub::C_PORT_LINK_STATE); + + if (port_status.wPortChanged.usb3.bh_reset) + { + (void)clear_port_feature(changed_port, USBHub::C_BH_PORT_RESET); + dwarnln_if(DEBUG_USB_HUB, "TODO: USB hub bh port reset change"); + } + + if (port_status.wPortChanged.usb3.config_error) + { + (void)clear_port_feature(changed_port, USBHub::C_PORT_CONFIG_ERROR); + dwarnln_if(DEBUG_USB_HUB, "TODO: USB hub port config error chage"); + } + } + else + { + ASSERT_NOT_REACHED(); + } + + // Initialize new devices that have not failed initialization + if (port_status.wPortStatus.enable && !m_ports[changed_port - 1].slot && !m_ports[changed_port - 1].failed) + { + USB::SpeedClass speed_class; + if (m_is_usb3) + speed_class = USB::SpeedClass::SuperSpeed; + else if (port_status.wPortStatus.usb2.low_speed) + speed_class = USB::SpeedClass::LowSpeed; + else if (port_status.wPortStatus.usb2.high_speed) + speed_class = USB::SpeedClass::HighSpeed; + else + speed_class = USB::SpeedClass::FullSpeed; + +#if DEBUG_USB_HUB + const char* speed_str = ""; + switch (speed_class) + { + case USB::SpeedClass::LowSpeed: speed_str = "low"; break; + case USB::SpeedClass::FullSpeed: speed_str = "full"; break; + case USB::SpeedClass::HighSpeed: speed_str = "high"; break; + case USB::SpeedClass::SuperSpeed: speed_str = "super"; break; + } + dprintln("Initializing {} speed device on hub port {}", speed_str, changed_port); +#endif + + auto result = m_device.initialize_device_on_hub_port(changed_port, speed_class); + if (!result.is_error()) + m_ports[changed_port - 1].slot = result.value(); + else + { + m_ports[changed_port - 1].failed = true; + dwarnln("Failed to initialize USB hub port {}: {}", changed_port, result.error()); + } + } + } + + m_port_updater = nullptr; + } + + void USBHubDriver::handle_stall(uint8_t endpoint_id) + { + (void)endpoint_id; + dwarnln("TODO: USB hub handle stall"); + } + + void USBHubDriver::handle_input_data(size_t byte_count, uint8_t endpoint_id) + { + if (endpoint_id != m_endpoint_id) + return; + + BAN::ScopeGuard query_changes([&] { + m_device.send_data_buffer(m_endpoint_id, m_data_region->paddr() + sizeof(USBHub::PortStatus), m_port_count / 8 + 1); + }); + + if (m_ports.size() / 8 + 1 < byte_count) + byte_count = m_ports.size() / 8 + 1; + + uint32_t new_ports = 0; + const auto* bitmap = reinterpret_cast(m_data_region->vaddr() + sizeof(USBHub::PortStatus)); + for (size_t i = 0; i < byte_count; i++) + new_ports |= static_cast(bitmap[i]) << (i * 8); + + if (new_ports) + { + m_changed_ports |= new_ports; + m_changed_port_blocker.unblock(); + } + } + +} diff --git a/kernel/kernel/USB/XHCI/Controller.cpp b/kernel/kernel/USB/XHCI/Controller.cpp index de9eab94..9000d338 100644 --- a/kernel/kernel/USB/XHCI/Controller.cpp +++ b/kernel/kernel/USB/XHCI/Controller.cpp @@ -306,17 +306,28 @@ namespace Kernel // attached ports m_port_changed = true; + uint64_t last_port_update = SystemTimer::get().ms_since_boot(); + while (true) { { bool expected { true }; while (!m_port_changed.compare_exchange(expected, false)) { + // If there has been no port activity in 100 ms, mark root hub as initialized + if (!m_ports_initialized && SystemTimer::get().ms_since_boot() - last_port_update >= 100) + { + mark_hub_init_done(0); + m_ports_initialized = true; + } + m_port_thread_blocker.block_with_timeout_ms(100); expected = true; } } + last_port_update = SystemTimer::get().ms_since_boot();; + for (size_t i = 0; i < m_ports.size(); i++) { auto& my_port = m_ports[i]; @@ -364,7 +375,9 @@ namespace Kernel } const uint8_t speed_id = (op_port.portsc >> XHCI::PORTSC::PORT_SPEED_SHIFT) & XHCI::PORTSC::PORT_SPEED_MASK; - if (auto ret = initialize_device(i + 1, speed_id_to_class(speed_id)); !ret.is_error()) + if (auto temp = speed_class_to_id(speed_id_to_class(speed_id)); temp != speed_id) + dwarnln("FIXME: Using incorrect speed id, {} instead of {}", temp, speed_id); + if (auto ret = initialize_device(i + 1, 0, speed_id_to_class(speed_id), nullptr, 0); !ret.is_error()) my_port.slot_id = ret.value(); else { @@ -378,7 +391,7 @@ namespace Kernel } } - BAN::ErrorOr XHCIController::initialize_device(uint32_t route_string, USB::SpeedClass speed_class) + BAN::ErrorOr XHCIController::initialize_device(uint32_t route_string, uint8_t depth, USB::SpeedClass speed_class, XHCIDevice* parent_hub, uint8_t parent_port_id) { XHCI::TRB enable_slot { .enable_slot_command {} }; enable_slot.enable_slot_command.trb_type = XHCI::TRBType::EnableSlotCommand; @@ -404,9 +417,12 @@ namespace Kernel #endif const XHCIDevice::Info info { + .parent_hub = parent_hub, + .parent_port_id = parent_port_id, .speed_class = speed_class, .slot_id = slot_id, .route_string = route_string, + .depth = depth }; m_slots[slot_id - 1] = TRY(XHCIDevice::create(*this, info)); diff --git a/kernel/kernel/USB/XHCI/Device.cpp b/kernel/kernel/USB/XHCI/Device.cpp index 43d6e57d..dc37887f 100644 --- a/kernel/kernel/USB/XHCI/Device.cpp +++ b/kernel/kernel/USB/XHCI/Device.cpp @@ -14,7 +14,7 @@ namespace Kernel } XHCIDevice::XHCIDevice(XHCIController& controller, const Info& info) - : USBDevice(info.speed_class) + : USBDevice(controller, info.speed_class, info.depth) , m_controller(controller) , m_info(info) {} @@ -34,6 +34,7 @@ namespace Kernel { const uint32_t context_size = m_controller.context_size_set() ? 64 : 32; + bool is_ls_or_fs_device_on_hs_hub = false; m_endpoints[0].max_packet_size = 0; switch (m_speed_class) @@ -41,6 +42,7 @@ namespace Kernel case USB::SpeedClass::LowSpeed: case USB::SpeedClass::FullSpeed: m_endpoints[0].max_packet_size = 8; + is_ls_or_fs_device_on_hs_hub = m_info.parent_hub && (m_info.parent_hub->speed_class() == USB::SpeedClass::HighSpeed); break; case USB::SpeedClass::HighSpeed: m_endpoints[0].max_packet_size = 64; @@ -72,7 +74,12 @@ namespace Kernel slot_context.context_entries = 1; slot_context.interrupter_target = 0; slot_context.speed = m_controller.speed_class_to_id(m_info.speed_class); - // FIXME: 4.5.2 hub + if (is_ls_or_fs_device_on_hs_hub) + { + slot_context.parent_hub_slot_id = m_info.parent_hub->m_info.slot_id; + slot_context.parent_port_number = m_info.parent_port_id; + slot_context.multi_tt = m_info.parent_hub->is_multi_tt(); + } endpoint0_context.endpoint_type = XHCI::EndpointType::Control; endpoint0_context.max_packet_size = m_endpoints[0].max_packet_size; @@ -120,6 +127,8 @@ namespace Kernel request.wLength = 8; TRY(send_request(request, kmalloc_paddr_of((vaddr_t)buffer.data()).value())); + dprintln_if(DEBUG_XHCI, "Got device descriptor"); + const bool is_usb3 = (m_speed_class == USB::SpeedClass::SuperSpeed); const uint32_t new_max_packet_size = is_usb3 ? 1u << buffer.back() : buffer.back(); @@ -153,6 +162,13 @@ namespace Kernel return {}; } + bool XHCIDevice::is_multi_tt() const + { + const uint32_t context_size = m_controller.context_size_set() ? 64 : 32; + const auto& slot_context = *reinterpret_cast(m_input_context->vaddr() + context_size); + return slot_context.multi_tt; + } + // 6.2.3.6 Interval static uint32_t determine_interval(const USBEndpointDescriptor& endpoint_descriptor, USB::SpeedClass speed_class) { @@ -186,7 +202,25 @@ namespace Kernel ASSERT_NOT_REACHED(); } - BAN::ErrorOr XHCIDevice::configure_endpoint(const USBEndpointDescriptor& endpoint_descriptor) + BAN::ErrorOr XHCIDevice::initialize_device_on_hub_port(uint8_t port_id, USB::SpeedClass speed_class) + { + if (m_info.depth + 1 > 5) + { + dwarnln("Invalid USB hub on depth {}", m_info.depth); + return BAN::Error::from_errno(EFAULT); + } + + uint32_t route_string = m_info.route_string; + route_string |= static_cast(port_id) << ((m_info.depth + 1) * 4); + return m_controller.initialize_device(route_string, m_info.depth + 1, speed_class, this, port_id); + } + + void XHCIDevice::deinitialize_device_slot(uint8_t slot_id) + { + m_controller.deinitialize_slot(slot_id); + } + + BAN::ErrorOr XHCIDevice::configure_endpoint(const USBEndpointDescriptor& endpoint_descriptor, const HubInfo& hub_info) { const bool is_control { (endpoint_descriptor.bmAttributes & 0x03) == 0x00 }; const bool is_isoch { (endpoint_descriptor.bmAttributes & 0x03) == 0x01 }; @@ -254,7 +288,13 @@ namespace Kernel input_control_context.add_context_flags = (1u << endpoint_id) | (1 << 0); slot_context.context_entries = last_valid_endpoint_id; - // FIXME: 4.5.2 hub + slot_context.hub = (hub_info.number_of_ports > 0); + slot_context.number_of_ports = hub_info.number_of_ports; + if (m_speed_class == USB::SpeedClass::HighSpeed) + { + slot_context.multi_tt = hub_info.multi_tt; + slot_context.tt_think_time = hub_info.tt_think_time; + } memset(&endpoint_context, 0, context_size); endpoint_context.endpoint_type = endpoint_type;