1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <lib/fzl/vmo-pool.h>
6#include <lib/zx/vmar.h>
7#include <string.h>
8
9namespace fzl {
10
11VmoPool::~VmoPool() {
12    // Clear out the free_buffers_, since the intrusive container
13    // will throw an assert if it contains unmanaged pointers on
14    // destruction.
15    free_buffers_.clear_unsafe();
16}
17
18zx_status_t VmoPool::Init(const fbl::Vector<zx::vmo>& vmos) {
19    return Init(vmos.begin(), vmos.size());
20}
21
22zx_status_t VmoPool::Init(const zx::vmo* vmos, size_t num_vmos) {
23    fbl::AllocChecker ac;
24    fbl::Array<ListableBuffer> buffers(new (&ac) ListableBuffer[num_vmos], num_vmos);
25    if (!ac.check()) {
26        return ZX_ERR_NO_MEMORY;
27    }
28    buffers_ = fbl::move(buffers);
29    free_buffers_.clear_unsafe();
30
31    zx_status_t status;
32    for (size_t i = 0; i < num_vmos; ++i) {
33        free_buffers_.push_front(&buffers_[i]);
34        status = buffers_[i].buffer.Map(vmos[i], 0, 0, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE);
35        if (status != ZX_OK) {
36            free_buffers_.clear_unsafe();
37            buffers_.reset();
38            return status;
39        }
40    }
41    current_buffer_ = kInvalidCurBuffer;
42    return ZX_OK;
43}
44
45void VmoPool::Reset() {
46    current_buffer_ = kInvalidCurBuffer;
47    for (size_t i = 0; i < buffers_.size(); ++i) {
48        if (!buffers_[i].InContainer()) {
49            free_buffers_.push_front(&buffers_[i]);
50        }
51    }
52}
53
54zx_status_t VmoPool::GetNewBuffer(uint32_t* buffer_index) {
55    if (HasBufferInProgress()) {
56        return ZX_ERR_BAD_STATE;
57    }
58    if (free_buffers_.is_empty()) { // No available buffers!
59        return ZX_ERR_NOT_FOUND;
60    }
61    ListableBuffer* buf = free_buffers_.pop_front();
62    ZX_DEBUG_ASSERT(buf >= &buffers_[0]);
63    uint32_t buffer_offset = static_cast<uint32_t>(buf - &buffers_[0]);
64    ZX_DEBUG_ASSERT(buffer_offset < buffers_.size());
65    current_buffer_ = buffer_offset;
66    if (buffer_index != nullptr) {
67        *buffer_index = current_buffer_;
68    }
69    return ZX_OK;
70}
71
72zx_status_t VmoPool::BufferCompleted(uint32_t* buffer_index) {
73    if (!HasBufferInProgress()) {
74        return ZX_ERR_BAD_STATE;
75    }
76    if (buffer_index != nullptr) {
77        *buffer_index = current_buffer_;
78    }
79    current_buffer_ = kInvalidCurBuffer;
80    return ZX_OK;
81}
82
83zx_status_t VmoPool::BufferRelease(uint32_t buffer_index) {
84    if (buffer_index >= buffers_.size()) {
85        return ZX_ERR_INVALID_ARGS;
86    }
87    if (buffers_[buffer_index].InContainer()) {
88        return ZX_ERR_NOT_FOUND;
89    }
90    // If we are cancelling the in-progress buffer:
91    if (current_buffer_ == buffer_index) {
92        current_buffer_ = kInvalidCurBuffer;
93    }
94
95    free_buffers_.push_front(&buffers_[buffer_index]);
96    return ZX_OK;
97}
98
99uint64_t VmoPool::CurrentBufferSize() const {
100    if (HasBufferInProgress()) {
101        return buffers_[current_buffer_].buffer.size();
102    }
103    return 0;
104}
105void* VmoPool::CurrentBufferAddress() const {
106    if (HasBufferInProgress()) {
107        return buffers_[current_buffer_].buffer.start();
108    }
109    return nullptr;
110}
111} // namespace fzl
112