Files
serenity/Kernel/Bus/USB/USBConfiguration.cpp
Sönke Holz 137cb0029b Kernel/USB: Use a Stream to parse USB descriptors
This fixes incorrect assumptions about the layout of descriptors and
gets rid of all the pointer arithmetic.
The USB spec doesn't define a strict order for all descriptors to appear
in.
It just says that endpoint descriptors follow its interface descriptor.
We also have to skip all unknown descriptors (which also can appear
anywhere), not just HID descriptors.
2024-07-27 13:28:15 -04:00

114 lines
4.7 KiB
C++

/*
* Copyright (c) 2022, Jesse Buhagiar <jesse.buhagiar@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/FixedArray.h>
#include <AK/MemoryStream.h>
#include <Kernel/Bus/USB/USBClasses.h>
#include <Kernel/Bus/USB/USBConfiguration.h>
#include <Kernel/Bus/USB/USBInterface.h>
#include <Kernel/Bus/USB/USBRequest.h>
#include <Kernel/Library/StdLib.h>
namespace Kernel::USB {
ErrorOr<void> USBConfiguration::enumerate_interfaces()
{
if (m_descriptor.total_length < sizeof(USBConfigurationDescriptor))
return EINVAL;
auto descriptor_hierarchy_buffer = TRY(FixedArray<u8>::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) | m_descriptor_index, 0, m_descriptor.total_length, descriptor_hierarchy_buffer.data()));
FixedMemoryStream stream { descriptor_hierarchy_buffer.span() };
// FIXME: Why does transfer length return the actual size +8 bytes?
if (transfer_length < m_descriptor.total_length)
return EIO;
auto const configuration_descriptor = TRY(stream.read_value<USBConfigurationDescriptor>());
if (configuration_descriptor.descriptor_header.length < sizeof(USBConfigurationDescriptor))
return EINVAL;
if (configuration_descriptor.total_length != m_descriptor.total_length)
return EINVAL;
USBInterface* current_interface = nullptr;
TRY(m_interfaces.try_ensure_capacity(m_descriptor.number_of_interfaces));
auto read_descriptor = [&stream]<typename T>(USBDescriptorCommon const& header) -> ErrorOr<T> {
if (header.length < sizeof(T))
return EINVAL;
auto descriptor = TRY(stream.read_value<T>());
// Skip additional bytes.
TRY(stream.seek(header.length - sizeof(T), SeekMode::FromCurrentPosition));
return descriptor;
};
while (!stream.is_eof()) {
// Peek the descriptor header.
auto const descriptor_header = TRY(stream.read_value<USBDescriptorCommon>());
MUST(stream.seek(-sizeof(USBDescriptorCommon), SeekMode::FromCurrentPosition));
switch (descriptor_header.descriptor_type) {
case DESCRIPTOR_TYPE_INTERFACE: {
auto interface_descriptor = TRY(read_descriptor.operator()<USBInterfaceDescriptor>(descriptor_header));
if constexpr (USB_DEBUG) {
dbgln("Interface Descriptor:");
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);
}
TRY(m_interfaces.try_empend(*this, interface_descriptor));
current_interface = &m_interfaces.last();
break;
}
case DESCRIPTOR_TYPE_ENDPOINT: {
auto endpoint_descriptor = TRY(read_descriptor.operator()<USBEndpointDescriptor>(descriptor_header));
if constexpr (USB_DEBUG) {
dbgln("Endpoint Descriptor:");
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);
}
if (current_interface == nullptr)
return EINVAL;
TRY(current_interface->add_endpoint_descriptor({}, endpoint_descriptor));
break;
}
default:
dbgln_if(USB_DEBUG, "Skipping descriptor of unknown type {}", descriptor_header.descriptor_type);
TRY(stream.seek(descriptor_header.length, SeekMode::FromCurrentPosition));
break;
}
}
return {};
}
}