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 <ddk/debug.h> 6#include <string.h> 7#include <lib/zx/vmar.h> 8 9#include "video-buffer.h" 10 11namespace video { 12namespace usb { 13 14VideoBuffer::~VideoBuffer() { 15 if (virt_ != nullptr) { 16 zx::vmar::root_self().unmap(reinterpret_cast<uintptr_t>(virt_), size_); 17 } 18} 19 20zx_status_t VideoBuffer::Create(zx::vmo&& vmo, 21 fbl::unique_ptr<VideoBuffer>* out, 22 uint32_t max_frame_size) { 23 if (!vmo.is_valid()) { 24 zxlogf(ERROR, "invalid buffer handle\n"); 25 return ZX_ERR_BAD_HANDLE; 26 } 27 28 uint64_t size; 29 zx_status_t status = vmo.get_size(&size); 30 if (status != ZX_OK) { 31 zxlogf(ERROR, "could not get vmo size, err: %d\n", status); 32 return status; 33 } 34 35 void* virt; 36 uint32_t flags = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE; 37 status = zx::vmar::root_self().map(0u, vmo, 38 0u, size, 39 flags, reinterpret_cast<uintptr_t*>(&virt)); 40 41 if (status != ZX_OK) { 42 zxlogf(ERROR, "failed to map VMO, got error: %d\n", status); 43 return status; 44 } 45 46 fbl::AllocChecker ac; 47 fbl::unique_ptr<VideoBuffer> res( 48 new (&ac) VideoBuffer(fbl::move(vmo), size, virt)); 49 if (!ac.check()) { 50 return ZX_ERR_NO_MEMORY; 51 } 52 53 status = res->Alloc(max_frame_size); 54 if (status != ZX_OK) { 55 zxlogf(ERROR, "failed to init video buffer, err: %d\n", status); 56 return status; 57 } 58 59 res->Init(); 60 61 *out = fbl::move(res); 62 return ZX_OK; 63} 64 65zx_status_t VideoBuffer::Alloc(uint32_t max_frame_size) { 66 if (max_frame_size == 0) { 67 return ZX_ERR_INVALID_ARGS; 68 } 69 uint64_t num_frames = size() / max_frame_size; 70 zxlogf(TRACE, "buffer size: %lu, num_frames: %lu\n", size(), num_frames); 71 72 fbl::AllocChecker ac; 73 free_frames_.reserve(num_frames, &ac); 74 if (!ac.check()) { 75 return ZX_ERR_NO_MEMORY; 76 } 77 locked_frames_.reserve(num_frames, &ac); 78 if (!ac.check()) { 79 return ZX_ERR_NO_MEMORY; 80 } 81 82 for (uint64_t i = 0; i < num_frames; ++i) { 83 free_frames_.push_back(i * max_frame_size); 84 } 85 return ZX_OK; 86} 87 88void VideoBuffer::Init() { 89 if (has_in_progress_frame_) { 90 free_frames_.push_back(in_progress_frame_); 91 has_in_progress_frame_ = false; 92 } 93 for (size_t n = locked_frames_.size(); n > 0; --n) { 94 free_frames_.push_back(locked_frames_.erase(n - 1)); 95 } 96 // Zero out the buffer. 97 memset(virt_, 0, size_); 98} 99 100zx_status_t VideoBuffer::GetNewFrame(FrameOffset* out_offset) { 101 if (out_offset == nullptr) { 102 return ZX_ERR_INVALID_ARGS; 103 } 104 if (has_in_progress_frame_) { 105 zxlogf(ERROR, "GetNewFrame failed, already writing to frame at offset: %lu\n", 106 in_progress_frame_); 107 return ZX_ERR_BAD_STATE; 108 } 109 if (free_frames_.is_empty()) { 110 return ZX_ERR_NOT_FOUND; 111 } 112 size_t last = free_frames_.size() - 1; 113 in_progress_frame_ = free_frames_.erase(last); 114 has_in_progress_frame_ = true; 115 *out_offset = in_progress_frame_; 116 return ZX_OK; 117} 118 119zx_status_t VideoBuffer::FrameCompleted() { 120 if (!has_in_progress_frame_) { 121 zxlogf(ERROR, "FrameCompleted failed, no frame is currently in progress\n"); 122 return ZX_ERR_BAD_STATE; 123 } 124 locked_frames_.push_back(in_progress_frame_); 125 has_in_progress_frame_ = false; 126 return ZX_OK; 127} 128 129zx_status_t VideoBuffer::FrameRelease(FrameOffset req_frame_offset) { 130 size_t i = 0; 131 for (auto& locked_offset : locked_frames_) { 132 if (req_frame_offset == locked_offset) { 133 free_frames_.push_back(locked_frames_.erase(i)); 134 return ZX_OK; 135 } 136 i++; 137 } 138 zxlogf(ERROR, "frame with offset %ld not found in free frames list\n", 139 req_frame_offset); 140 return ZX_ERR_NOT_FOUND; 141} 142 143} // namespace usb 144} // namespace video 145