/* * Copyright (c) 2022, Jesse Buhagiar * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include namespace Kernel::USB { USBConfiguration::USBConfiguration(USBConfiguration const& other) : m_device(other.m_device) , m_descriptor(other.m_descriptor) , m_descriptor_index(other.m_descriptor_index) , m_interfaces(other.m_interfaces) { // FIXME: This can definitely OOM for (auto& interface : m_interfaces) interface.set_configuration({}, *this); } USBConfiguration::USBConfiguration(USBConfiguration&& other) : m_device(other.m_device) , m_descriptor(other.m_descriptor) , m_descriptor_index(other.m_descriptor_index) , m_interfaces(move(other.m_interfaces)) { for (auto& interface : m_interfaces) interface.set_configuration({}, *this); } ErrorOr USBConfiguration::enumerate_interfaces() { if (m_descriptor.total_length < sizeof(USBConfigurationDescriptor)) return EINVAL; m_descriptor_hierarchy_buffer = TRY(FixedArray::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, m_descriptor_hierarchy_buffer.data())); FixedMemoryStream stream { m_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()); 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](USBDescriptorCommon const& header) -> ErrorOr { if (header.length < sizeof(T)) return EINVAL; auto descriptor = TRY(stream.read_value()); // 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()); MUST(stream.seek(-sizeof(USBDescriptorCommon), SeekMode::FromCurrentPosition)); switch (descriptor_header.descriptor_type) { case DESCRIPTOR_TYPE_INTERFACE: { auto offset = stream.offset(); auto interface_descriptor = TRY(read_descriptor.operator()(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, offset)); current_interface = &m_interfaces.last(); break; } case DESCRIPTOR_TYPE_ENDPOINT: { auto endpoint_descriptor = TRY(read_descriptor.operator()(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 {}; } }