summaryrefslogtreecommitdiff
path: root/Kernel/Bus/USB/UHCI/UHCIDescriptorPool.h
blob: d58b3984d82345e3f8451849d9c5a54535b398f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/*
 * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/NonnullOwnPtr.h>
#include <AK/OwnPtr.h>
#include <AK/Stack.h>
#include <Kernel/Memory/MemoryManager.h>
#include <Kernel/Memory/Region.h>
#include <Kernel/StdLib.h>

namespace Kernel::USB {

// This pool is bound by PAGE_SIZE / sizeof(T). The underlying allocation for the pointers
// is AK::Stack. As such, we never dynamically allocate any memory past the amount
// that can fit in a single page.
template<typename T>
class UHCIDescriptorPool {
    AK_MAKE_NONCOPYABLE(UHCIDescriptorPool);
    AK_MAKE_NONMOVABLE(UHCIDescriptorPool);

    // Ensure that we can't get into a situation where we'll write past the page
    // and blow up
    static_assert(sizeof(T) <= PAGE_SIZE);

public:
    static KResultOr<NonnullOwnPtr<UHCIDescriptorPool<T>>> try_create(StringView name)
    {
        auto pool_memory_block = MM.allocate_kernel_region(PAGE_SIZE, "UHCI Descriptor Pool", Memory::Region::Access::ReadWrite);
        if (!pool_memory_block)
            return ENOMEM;

        return adopt_nonnull_own_or_enomem(new (nothrow) UHCIDescriptorPool(pool_memory_block.release_nonnull(), name));
    }

    ~UHCIDescriptorPool() = default;

    [[nodiscard]] T* try_take_free_descriptor()
    {
        // We're out of descriptors!
        if (m_free_descriptor_stack.is_empty())
            return nullptr;

        dbgln_if(UHCI_VERBOSE_DEBUG, "Got a free UHCI Descriptor @ {} from pool {}", m_free_descriptor_stack.top(), m_pool_name);
        T* descriptor = m_free_descriptor_stack.top();
        m_free_descriptor_stack.pop();

        return descriptor;
    }

    void release_to_pool(T* ptr)
    {
        dbgln_if(UHCI_VERBOSE_DEBUG, "Returning descriptor @ {} to pool {}", ptr, m_pool_name);
        if (!m_free_descriptor_stack.push(ptr))
            dbgln("Failed to return descriptor to pool {}. Stack overflow!", m_pool_name);
    }

    void print_pool_information() const
    {
        dbgln("Pool {} allocated @ {}", m_pool_name, m_pool_region->physical_page(0)->paddr());
    }

private:
    UHCIDescriptorPool(NonnullOwnPtr<Memory::Region> pool_memory_block, StringView name)
        : m_pool_name(name)
        , m_pool_region(move(pool_memory_block))
    {
        // Go through the number of descriptors to create in the pool, and create a virtual/physical address mapping
        for (size_t i = 0; i < PAGE_SIZE / sizeof(T); i++) {
            auto placement_address = reinterpret_cast<void*>(m_pool_region->vaddr().get() + (i * sizeof(T)));
            auto physical_address = static_cast<u32>(m_pool_region->physical_page(0)->paddr().get() + (i * sizeof(T)));
            auto* object = new (placement_address) T(physical_address);
            m_free_descriptor_stack.push(object); // Push the descriptor's pointer onto the free list
        }
    }

    StringView m_pool_name;                                   // Name of this pool
    NonnullOwnPtr<Memory::Region> m_pool_region;              // Memory region where descriptors actually reside
    Stack<T*, PAGE_SIZE / sizeof(T)> m_free_descriptor_stack; // Stack of currently free descriptor pointers
};
}