Newer
Older
minerva / Kernel / Bus / USB / USBConfiguration.h
@minerva minerva on 13 Jul 3 KB Initial commit
/*
 * Copyright (c) 2022, Jesse Buhagiar <jesse.buhagiar@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/FixedArray.h>
#include <AK/MemoryStream.h>
#include <AK/Vector.h>
#include <Kernel/Bus/USB/USBDescriptors.h>
#include <Kernel/Bus/USB/USBDevice.h>
#include <Kernel/Bus/USB/USBInterface.h>

namespace Kernel::USB {

class Device;

class USBConfiguration {
public:
    USBConfiguration() = delete;
    USBConfiguration(Device& device, USBConfigurationDescriptor const descriptor, u8 descriptor_index)
        : m_device(&device)
        , m_descriptor(descriptor)
        , m_descriptor_index(descriptor_index)
    {
        m_interfaces.ensure_capacity(descriptor.number_of_interfaces);
    }

private:
    USBConfiguration(USBConfiguration const&);

public:
    USBConfiguration(USBConfiguration&&);
    USBConfiguration copy() const { return USBConfiguration(*this); }

    Device const& device() const { return *m_device; }
    void set_device(Badge<Device>, Device& device) { m_device = &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

    Vector<USBInterface> const& interfaces() const { return m_interfaces; }

    template<CallableAs<ErrorOr<IterationDecision>, ReadonlyBytes> Callback>
    ErrorOr<void> for_each_descriptor_in_interface(USBInterface const& interface, Callback&& callback) const
    {
        auto stream = FixedMemoryStream(m_descriptor_hierarchy_buffer.span());
        TRY(stream.seek(interface.descriptor_offset({}), SeekMode::SetPosition));

        auto const interface_descriptor = TRY(stream.read_value<USBInterfaceDescriptor>());

        VERIFY(__builtin_memcmp(&interface_descriptor, &interface.descriptor(), sizeof(USBInterfaceDescriptor)) == 0);

        while (!stream.is_eof()) {
            auto const descriptor_header = TRY(stream.read_value<USBDescriptorCommon>());

            if (descriptor_header.descriptor_type == DESCRIPTOR_TYPE_INTERFACE)
                break;

            ReadonlyBytes descriptor_data { m_descriptor_hierarchy_buffer.span().slice(stream.offset() - sizeof(USBDescriptorCommon), descriptor_header.length) };
            if (TRY(callback(descriptor_data)) == IterationDecision::Break)
                return {};

            TRY(stream.seek(descriptor_header.length - sizeof(USBDescriptorCommon), SeekMode::FromCurrentPosition));
        }

        return {};
    }

    ErrorOr<void> enumerate_interfaces();

private:
    Device* m_device;                              // Reference to the device linked to this configuration
    USBConfigurationDescriptor const m_descriptor; // Descriptor that backs this configuration
    u8 m_descriptor_index;                         // Descriptor index for {GET,SET}_DESCRIPTOR
    Vector<USBInterface> m_interfaces;             // Interfaces for this device

    FixedArray<u8> m_descriptor_hierarchy_buffer; // Buffer for us to store the entire hierarchy into
};

}