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;