From 300dcb6f5e2c548fa3339947ff531d3fa17cbd38 Mon Sep 17 00:00:00 2001 From: Jesse Buhagiar Date: Fri, 15 Apr 2022 01:11:15 +1000 Subject: Kernel/USB: Get all interface descriptors on enumeration This creates all interfaces when the device is enumerated, with a link to the configuration that it is a part of. As such, a new class, `USBInterface` has been introduced to express this state. --- Kernel/Bus/USB/USBConfiguration.cpp | 77 +++++++++++++++++++++++++++++++++++++ Kernel/Bus/USB/USBConfiguration.h | 14 +++++-- Kernel/Bus/USB/USBDevice.cpp | 1 + Kernel/Bus/USB/USBInterface.h | 32 +++++++++++++++ Kernel/CMakeLists.txt | 1 + 5 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 Kernel/Bus/USB/USBConfiguration.cpp create mode 100644 Kernel/Bus/USB/USBInterface.h diff --git a/Kernel/Bus/USB/USBConfiguration.cpp b/Kernel/Bus/USB/USBConfiguration.cpp new file mode 100644 index 0000000000..572cf2a581 --- /dev/null +++ b/Kernel/Bus/USB/USBConfiguration.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +namespace Kernel::USB { + +ErrorOr USBConfiguration::get_interfaces() +{ + auto descriptor_hierarchy_buffer = TRY(FixedArray::try_create(m_descriptor.total_length)); // Buffer for us to store the entire hierarchy into + + // The USB spec is a little bit janky here... Interface and Endpoint descriptors aren't fetched + // through a `GET_DESCRIPTOR` request to the device. Instead, the _entire_ hierarchy is returned + // to us in one go. + auto transfer_length = TRY(m_device.control_transfer(USB_REQUEST_TRANSFER_DIRECTION_DEVICE_TO_HOST, USB_REQUEST_GET_DESCRIPTOR, (DESCRIPTOR_TYPE_CONFIGURATION << 8), 0, m_descriptor.total_length, descriptor_hierarchy_buffer.data())); + + // FIXME: Why does transfer length return the actual size +8 bytes? + if (transfer_length < m_descriptor.total_length) + return EIO; + + u8* interface_descriptors_base = descriptor_hierarchy_buffer.data() + sizeof(USBConfigurationDescriptor); + USBInterfaceDescriptor* interface_descriptor = reinterpret_cast(interface_descriptors_base); + Vector endpoint_descriptors; + for (auto interface = 0u; interface < m_descriptor.number_of_interfaces; interface++) { + endpoint_descriptors.ensure_capacity(interface_descriptor->number_of_endpoints); + + if constexpr (USB_DEBUG) { + dbgln("Interface Descriptor {}", interface); + dbgln("interface_id: {:02x}", interface_descriptor->interface_id); + dbgln("alternate_setting: {:02x}", interface_descriptor->alternate_setting); + dbgln("number_of_endpoints: {:02x}", interface_descriptor->number_of_endpoints); + dbgln("interface_class_code: {:02x}", interface_descriptor->interface_class_code); + dbgln("interface_sub_class_code: {:02x}", interface_descriptor->interface_sub_class_code); + dbgln("interface_protocol: {:02x}", interface_descriptor->interface_protocol); + dbgln("interface_string_descriptor_index: {}", interface_descriptor->interface_string_descriptor_index); + } + + // Get all the endpoint descriptors + for (auto endpoint = 0u; endpoint < interface_descriptor->number_of_endpoints; endpoint++) { + u8* raw_endpoint_descriptor_offset = interface_descriptors_base + sizeof(USBInterfaceDescriptor) + (endpoint * sizeof(USBEndpointDescriptor)); + + // FIXME: It looks like HID descriptors come BEFORE the endpoint descriptors for a HID device, so we should load + // these too eventually. + // See here: https://www.usb.org/defined-class-codes + if (interface_descriptor->interface_class_code == USB_CLASS_HID) + raw_endpoint_descriptor_offset += sizeof(USBHIDDescriptor); // Skip the HID descriptor (this was worked out via buffer inspection) + + USBEndpointDescriptor endpoint_descriptor; + memcpy(&endpoint_descriptor, raw_endpoint_descriptor_offset, sizeof(USBEndpointDescriptor)); + + if constexpr (USB_DEBUG) { + dbgln("Endpoint Descriptor {}", endpoint); + dbgln("Endpoint Address: {}", endpoint_descriptor.endpoint_address); + dbgln("Endpoint Attribute Bitmap: {:08b}", endpoint_descriptor.endpoint_attributes_bitmap); + dbgln("Endpoint Maximum Packet Size: {}", endpoint_descriptor.max_packet_size); + dbgln("Endpoint Poll Interval (in frames): {}", endpoint_descriptor.poll_interval_in_frames); + } + + endpoint_descriptors.append(endpoint_descriptor); + } + + USBInterface device_interface(*this, *interface_descriptor, endpoint_descriptors); + m_interfaces.append(device_interface); + interface_descriptor += interface_descriptor->number_of_endpoints * sizeof(USBEndpointDescriptor); + } + + return {}; +} + +} diff --git a/Kernel/Bus/USB/USBConfiguration.h b/Kernel/Bus/USB/USBConfiguration.h index 3fba019ef8..7173d93d74 100644 --- a/Kernel/Bus/USB/USBConfiguration.h +++ b/Kernel/Bus/USB/USBConfiguration.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace Kernel::USB { @@ -17,22 +18,27 @@ class Device; class USBConfiguration { public: USBConfiguration() = delete; - USBConfiguration(Device& device, USBConfigurationDescriptor const configuration_descriptor) + USBConfiguration(Device& device, USBConfigurationDescriptor const descriptor) : m_device(device) - , m_descriptor(configuration_descriptor) + , m_descriptor(descriptor) { + m_interfaces.ensure_capacity(descriptor.number_of_interfaces); } Device const& device() const { return m_device; } + USBConfigurationDescriptor const& descriptor() const { return m_descriptor; } u8 interface_count() const { return m_descriptor.number_of_interfaces; } u8 configuration_id() const { return m_descriptor.configuration_value; } u8 attributes() const { return m_descriptor.attributes_bitmap; } u16 max_power_ma() const { return m_descriptor.max_power_in_ma * 2u; } // Note: "Power" is used incorrectly here, however it's what it's called in the descriptor/documentation + ErrorOr get_interfaces(); + private: - Device& m_device; // Reference to the device linked to this configuration - USBConfigurationDescriptor m_descriptor; // Descriptor that backs this configuration + Device& m_device; // Reference to the device linked to this configuration + USBConfigurationDescriptor const m_descriptor; // Descriptor that backs this configuration + Vector m_interfaces; // Interfaces for this device }; } diff --git a/Kernel/Bus/USB/USBDevice.cpp b/Kernel/Bus/USB/USBDevice.cpp index 695b9c4b3d..4db695f129 100644 --- a/Kernel/Bus/USB/USBDevice.cpp +++ b/Kernel/Bus/USB/USBDevice.cpp @@ -132,6 +132,7 @@ ErrorOr Device::enumerate_device() } USBConfiguration device_configuration(*this, configuration_descriptor); + TRY(device_configuration.get_interfaces()); m_configurations.append(device_configuration); } diff --git a/Kernel/Bus/USB/USBInterface.h b/Kernel/Bus/USB/USBInterface.h new file mode 100644 index 0000000000..d0131464cd --- /dev/null +++ b/Kernel/Bus/USB/USBInterface.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Kernel::USB { + +class USBConfiguration; + +class USBInterface final { +public: + USBInterface() = delete; + USBInterface(USBConfiguration const& configuration, USBInterfaceDescriptor const descriptor, Vector const& endpoint_descriptors) + : m_configuration(configuration) + , m_descriptor(descriptor) + , m_endpoint_descriptors(endpoint_descriptors) + { + m_endpoint_descriptors.ensure_capacity(descriptor.number_of_endpoints); + } + +private: + USBConfiguration const& m_configuration; // Configuration that this interface belongs to + USBInterfaceDescriptor const m_descriptor; // Descriptor backing this interface + Vector m_endpoint_descriptors; // Endpoint descriptors for this interface (that we can use to open an endpoint) +}; + +} diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 8461f7e715..002b977a2e 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -30,6 +30,7 @@ set(KERNEL_SOURCES Bus/USB/SysFSUSB.cpp Bus/USB/UHCI/UHCIController.cpp Bus/USB/UHCI/UHCIRootHub.cpp + Bus/USB/USBConfiguration.cpp Bus/USB/USBController.cpp Bus/USB/USBDevice.cpp Bus/USB/USBHub.cpp -- cgit v1.2.3