blob: e19770677b0c8a9a449e21ed218884e93952182d [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/graphics/drivers/misc/goldfish_control/control_device.h"
#include <fuchsia/hardware/goldfish/llcpp/fidl.h>
#include <zircon/syscalls.h>
#include <memory>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/trace/event.h>
#include <ddktl/fidl.h>
#include <fbl/auto_call.h>
#include <fbl/auto_lock.h>
#include "src/graphics/drivers/misc/goldfish_control/heap.h"
#ifdef GOLDFISH_CONTROL_USE_COMPOSITE_DEVICE
#include <ddk/platform-defs.h> // nogncheck
#include <ddk/protocol/composite.h> // nogncheck
#endif
namespace goldfish {
namespace {
#ifdef GOLDFISH_CONTROL_USE_COMPOSITE_DEVICE
enum { FRAGMENT_GOLDFISH_PIPE, FRAGMENT_GOLDFISH_ADDRESS_SPACE, FRAGMENT_COUNT };
#endif
const char* kTag = "goldfish-control";
const char* kPipeName = "pipe:opengles";
constexpr uint32_t kClientFlags = 0;
constexpr uint32_t VULKAN_ONLY = 1;
constexpr uint32_t kInvalidColorBuffer = 0U;
struct CreateColorBufferCmd {
uint32_t op;
uint32_t size;
uint32_t width;
uint32_t height;
uint32_t internalformat;
};
constexpr uint32_t kOP_rcCreateColorBuffer = 10012;
constexpr uint32_t kSize_rcCreateColorBuffer = 20;
struct CloseColorBufferCmd {
uint32_t op;
uint32_t size;
uint32_t id;
};
constexpr uint32_t kOP_rcCloseColorBuffer = 10014;
constexpr uint32_t kSize_rcCloseColorBuffer = 12;
struct CreateBufferCmd {
uint32_t op;
uint32_t size;
uint32_t buffer_size;
};
constexpr uint32_t kOP_rcCreateBuffer = 10049;
constexpr uint32_t kSize_rcCreateBuffer = 12;
struct CloseBufferCmd {
uint32_t op;
uint32_t size;
uint32_t id;
};
constexpr uint32_t kOP_rcCloseBuffer = 10050;
constexpr uint32_t kSize_rcCloseBuffer = 12;
struct SetColorBufferVulkanMode2Cmd {
uint32_t op;
uint32_t size;
uint32_t id;
uint32_t mode;
uint32_t memory_property;
};
constexpr uint32_t kOP_rcSetColorBufferVulkanMode2 = 10051;
constexpr uint32_t kSize_rcSetColorBufferVulkanMode2 = 20;
struct __attribute__((__packed__)) MapGpaToBufferHandleCmd {
uint32_t op;
uint32_t size;
uint32_t id;
uint64_t gpa;
};
constexpr uint32_t kOP_rcMapGpaToBufferHandle = 10052;
constexpr uint32_t kSize_rcMapGpaToBufferHandle = 20;
zx_koid_t GetKoidForVmo(const zx::vmo& vmo) {
zx_info_handle_basic_t info;
zx_status_t status =
zx_object_get_info(vmo.get(), ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: zx_object_get_info() failed - status: %d", kTag, status);
return ZX_KOID_INVALID;
}
return info.koid;
}
} // namespace
// static
zx_status_t Control::Create(void* ctx, zx_device_t* device) {
auto control = std::make_unique<Control>(device);
zx_status_t status = control->Bind();
if (status == ZX_OK) {
// devmgr now owns device.
__UNUSED auto* dev = control.release();
}
return status;
}
Control::Control(zx_device_t* parent) : ControlType(parent) {
// Initialize parent protocols.
Init();
goldfish_control_protocol_t self{&goldfish_control_protocol_ops_, this};
control_ = ddk::GoldfishControlProtocolClient(&self);
}
Control::~Control() {
if (id_) {
fbl::AutoLock lock(&lock_);
if (cmd_buffer_.is_valid()) {
for (auto& buffer : buffer_handles_) {
CloseBufferOrColorBufferLocked(buffer.second);
}
auto buffer = static_cast<pipe_cmd_buffer_t*>(cmd_buffer_.virt());
buffer->id = id_;
buffer->cmd = PIPE_CMD_CODE_CLOSE;
buffer->status = PIPE_ERROR_INVAL;
pipe_.Exec(id_);
ZX_DEBUG_ASSERT(!buffer->status);
}
pipe_.Destroy(id_);
}
}
zx_status_t Control::Init() {
#ifdef GOLDFISH_CONTROL_USE_COMPOSITE_DEVICE
composite_protocol_t composite;
auto status = device_get_protocol(parent(), ZX_PROTOCOL_COMPOSITE, &composite);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: could not get composite protocol: %d", kTag, status);
return status;
}
zx_device_t* fragments[FRAGMENT_COUNT];
size_t actual;
composite_get_fragments(&composite, fragments, countof(fragments), &actual);
if (actual != countof(fragments)) {
zxlogf(ERROR, "%s: could not get fragments", kTag);
return ZX_ERR_NOT_SUPPORTED;
}
pipe_ = fragments[FRAGMENT_GOLDFISH_PIPE];
if (!pipe_.is_valid()) {
zxlogf(ERROR, "%s: goldfish pipe fragment is invalid", kTag);
return ZX_ERR_NO_RESOURCES;
}
address_space_ = fragments[FRAGMENT_GOLDFISH_ADDRESS_SPACE];
if (!address_space_.is_valid()) {
zxlogf(ERROR, "%s: goldfish address space fragment is invalid", kTag);
return ZX_ERR_NO_RESOURCES;
}
return ZX_OK;
#else
pipe_ = ddk::GoldfishPipeProtocolClient(parent());
if (!pipe_.is_valid()) {
zxlogf(ERROR, "%s: goldfish pipe protocol is invalid", kTag);
return ZX_ERR_NO_RESOURCES;
}
return ZX_OK;
#endif
}
zx_status_t Control::InitPipeDeviceLocked() {
if (!pipe_.is_valid()) {
zxlogf(ERROR, "%s: no pipe protocol", kTag);
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t status = pipe_.GetBti(&bti_);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: GetBti failed: %d", kTag, status);
return status;
}
status = io_buffer_.Init(bti_.get(), PAGE_SIZE, IO_BUFFER_RW | IO_BUFFER_CONTIG);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: io_buffer_init failed: %d", kTag, status);
return status;
}
ZX_DEBUG_ASSERT(!pipe_event_.is_valid());
status = zx::event::create(0u, &pipe_event_);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: zx_event_create failed: %d", kTag, status);
return status;
}
zx::event pipe_event_dup;
status = pipe_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &pipe_event_dup);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: zx_handle_duplicate failed: %d", kTag, status);
return status;
}
zx::vmo vmo;
status = pipe_.Create(&id_, &vmo);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: pipe Create failed: %d", kTag, status);
return status;
}
status = pipe_.SetEvent(id_, std::move(pipe_event_dup));
if (status != ZX_OK) {
zxlogf(ERROR, "%s: pipe SetEvent failed: %d", kTag, status);
return status;
}
status = cmd_buffer_.InitVmo(bti_.get(), vmo.get(), 0, IO_BUFFER_RW);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: io_buffer_init_vmo failed: %d", kTag, status);
return status;
}
auto release_buffer =
fbl::MakeAutoCall([this]() TA_NO_THREAD_SAFETY_ANALYSIS { cmd_buffer_.release(); });
auto buffer = static_cast<pipe_cmd_buffer_t*>(cmd_buffer_.virt());
buffer->id = id_;
buffer->cmd = PIPE_CMD_CODE_OPEN;
buffer->status = PIPE_ERROR_INVAL;
pipe_.Open(id_);
if (buffer->status) {
zxlogf(ERROR, "%s: Open failed: %d", kTag, buffer->status);
return ZX_ERR_INTERNAL;
}
// Keep buffer after successful execution of OPEN command. This way
// we'll send CLOSE later.
release_buffer.cancel();
size_t length = strlen(kPipeName) + 1;
memcpy(io_buffer_.virt(), kPipeName, length);
int32_t consumed_size = 0;
int32_t result = WriteLocked(static_cast<uint32_t>(length), &consumed_size);
if (result < 0) {
zxlogf(ERROR, "%s: failed connecting to '%s' pipe: %d", kTag, kPipeName, result);
return ZX_ERR_INTERNAL;
}
ZX_DEBUG_ASSERT(consumed_size == static_cast<int32_t>(length));
memcpy(io_buffer_.virt(), &kClientFlags, sizeof(kClientFlags));
WriteLocked(sizeof(kClientFlags));
return ZX_OK;
}
#ifdef GOLDFISH_CONTROL_USE_COMPOSITE_DEVICE
zx_status_t Control::InitAddressSpaceDeviceLocked() {
if (!address_space_.is_valid()) {
zxlogf(ERROR, "%s: no address space protocol", kTag);
return ZX_ERR_NOT_SUPPORTED;
}
// Initialize address space device.
zx::channel address_space_child_client, address_space_child_req;
zx_status_t status =
zx::channel::create(0u, &address_space_child_client, &address_space_child_req);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: zx_channel_create failed: %d", kTag, status);
return status;
}
status = address_space_.OpenChildDriver(ADDRESS_SPACE_CHILD_DRIVER_TYPE_DEFAULT,
std::move(address_space_child_req));
if (status != ZX_OK) {
zxlogf(ERROR, "%s: AddressSpaceDevice::OpenChildDriver failed: %d", kTag, status);
return status;
}
address_space_child_ =
std::make_unique<llcpp::fuchsia::hardware::goldfish::AddressSpaceChildDriver::SyncClient>(
std::move(address_space_child_client));
return ZX_OK;
}
#endif
zx_status_t Control::Bind() {
fbl::AutoLock lock(&lock_);
zx_status_t status = InitPipeDeviceLocked();
if (status != ZX_OK) {
zxlogf(ERROR, "%s: InitPipeDeviceLocked() failed: %d", kTag, status);
return status;
}
#ifdef GOLDFISH_CONTROL_USE_COMPOSITE_DEVICE
status = InitAddressSpaceDeviceLocked();
if (status != ZX_OK) {
zxlogf(ERROR, "%s: InitAddressSpaceDeviceLocked() failed: %d", kTag, status);
return status;
}
#endif
// We are now ready to serve goldfish heap allocations. Create a channel
// and register client-end with sysmem.
zx::channel heap_request, heap_connection;
status = zx::channel::create(0, &heap_request, &heap_connection);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: zx::channel:create() failed: %d", kTag, status);
return status;
}
status = pipe_.RegisterSysmemHeap(
static_cast<uint64_t>(llcpp::fuchsia::sysmem2::HeapType::GOLDFISH_DEVICE_LOCAL),
std::move(heap_connection));
if (status != ZX_OK) {
zxlogf(ERROR, "%s: failed to register heap: %d", kTag, status);
return status;
}
std::unique_ptr<Heap> heap = Heap::Create(this);
Heap* heap_ptr = heap.get();
heaps_.push_back(std::move(heap));
heap_ptr->Bind(std::move(heap_request));
return DdkAdd(ddk::DeviceAddArgs("goldfish-control").set_proto_id(ZX_PROTOCOL_GOLDFISH_CONTROL));
}
uint64_t Control::RegisterBufferHandle(const zx::vmo& vmo) {
zx_koid_t koid = GetKoidForVmo(vmo);
if (koid == ZX_KOID_INVALID) {
return static_cast<uint64_t>(ZX_KOID_INVALID);
}
fbl::AutoLock lock(&lock_);
buffer_handles_[koid] = kInvalidColorBuffer;
return static_cast<uint64_t>(koid);
}
void Control::FreeBufferHandle(uint64_t id) {
fbl::AutoLock lock(&lock_);
auto it = buffer_handles_.find(static_cast<zx_koid_t>(id));
if (it == buffer_handles_.end()) {
zxlogf(ERROR, "%s: invalid key", kTag);
return;
}
if (it->second) {
CloseBufferOrColorBufferLocked(it->second);
}
buffer_handle_types_.erase(it->second);
buffer_handles_.erase(it);
}
void Control::CreateColorBuffer2(
zx::vmo vmo, llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params create_params,
CreateColorBuffer2Completer::Sync completer) {
// Check argument validity.
if (!create_params.has_width() || !create_params.has_height() || !create_params.has_format() ||
!create_params.has_memory_property()) {
zxlogf(ERROR, "%s: invalid arguments: width? %d height? %d format? %d memory property? %d\n",
kTag, create_params.has_width(), create_params.has_height(), create_params.has_format(),
create_params.has_memory_property());
completer.Reply(ZX_ERR_INVALID_ARGS, -1);
return;
}
if ((create_params.memory_property() &
llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_HOST_VISIBLE) &&
!create_params.has_physical_address()) {
zxlogf(ERROR, "%s: invalid arguments: memory_property %d, no physical address\n", kTag,
create_params.memory_property());
completer.Reply(ZX_ERR_INVALID_ARGS, -1);
return;
}
TRACE_DURATION("gfx", "Control::CreateColorBuffer2", "width", create_params.width(), "height",
create_params.height(), "format", static_cast<uint32_t>(create_params.format()),
"memory_property", create_params.memory_property());
zx_koid_t koid = GetKoidForVmo(vmo);
if (koid == ZX_KOID_INVALID) {
completer.Close(ZX_ERR_INVALID_ARGS);
return;
}
fbl::AutoLock lock(&lock_);
auto it = buffer_handles_.find(koid);
if (it == buffer_handles_.end()) {
completer.Reply(ZX_ERR_INVALID_ARGS, -1);
return;
}
if (it->second != kInvalidColorBuffer) {
completer.Reply(ZX_ERR_ALREADY_EXISTS, -1);
return;
}
uint32_t id;
zx_status_t status = CreateColorBufferLocked(create_params.width(), create_params.height(),
static_cast<uint32_t>(create_params.format()), &id);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: failed to create color buffer: %d", kTag, status);
completer.Close(status);
return;
}
auto close_color_buffer =
fbl::MakeAutoCall([this, id]() TA_NO_THREAD_SAFETY_ANALYSIS { CloseColorBufferLocked(id); });
uint32_t result = 0;
status =
SetColorBufferVulkanMode2Locked(id, VULKAN_ONLY, create_params.memory_property(), &result);
if (status != ZX_OK || result) {
zxlogf(ERROR, "%s: failed to set vulkan mode: %d %d", kTag, status, result);
completer.Close(status);
return;
}
int32_t hw_address_page_offset = -1;
if (create_params.memory_property() &
llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_HOST_VISIBLE) {
uint32_t map_result = 0;
status = MapGpaToBufferHandleLocked(id, create_params.physical_address(), &map_result);
if (status != ZX_OK || map_result < 0) {
zxlogf(ERROR, "%s: failed to map gpa to color buffer: %d %d", kTag, status, map_result);
completer.Close(status);
return;
}
hw_address_page_offset = map_result;
}
close_color_buffer.cancel();
it->second = id;
buffer_handle_types_[id] = llcpp::fuchsia::hardware::goldfish::BufferHandleType::COLOR_BUFFER;
completer.Reply(ZX_OK, hw_address_page_offset);
}
void Control::CreateBuffer(zx::vmo vmo, uint32_t size, CreateBufferCompleter::Sync completer) {
TRACE_DURATION("gfx", "Control::FidlCreateBuffer", "size", size);
zx_koid_t koid = GetKoidForVmo(vmo);
if (koid == ZX_KOID_INVALID) {
completer.Close(ZX_ERR_INVALID_ARGS);
return;
}
fbl::AutoLock lock(&lock_);
auto it = buffer_handles_.find(koid);
if (it == buffer_handles_.end()) {
completer.Reply(ZX_ERR_INVALID_ARGS);
return;
}
if (it->second != kInvalidColorBuffer) {
completer.Reply(ZX_ERR_ALREADY_EXISTS);
return;
}
uint32_t id;
zx_status_t status = CreateBufferLocked(size, &id);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: failed to create buffer: %d", kTag, status);
completer.Close(status);
return;
}
it->second = id;
buffer_handle_types_[id] = llcpp::fuchsia::hardware::goldfish::BufferHandleType::BUFFER;
completer.Reply(ZX_OK);
}
void Control::GetBufferHandle(zx::vmo vmo, GetBufferHandleCompleter::Sync completer) {
TRACE_DURATION("gfx", "Control::FidlGetBufferHandle");
zx_koid_t koid = GetKoidForVmo(vmo);
if (koid == ZX_KOID_INVALID) {
completer.Close(ZX_ERR_INVALID_ARGS);
return;
}
uint32_t handle = kInvalidColorBuffer;
auto handle_type = llcpp::fuchsia::hardware::goldfish::BufferHandleType::INVALID;
fbl::AutoLock lock(&lock_);
auto it = buffer_handles_.find(koid);
if (it == buffer_handles_.end()) {
completer.Reply(ZX_ERR_INVALID_ARGS, handle, handle_type);
return;
}
handle = it->second;
if (handle == kInvalidColorBuffer) {
// Color buffer not created yet.
completer.Reply(ZX_ERR_NOT_FOUND, handle, handle_type);
return;
}
auto it_types = buffer_handle_types_.find(handle);
if (it_types == buffer_handle_types_.end()) {
// Color buffer type not registered yet.
completer.Reply(ZX_ERR_NOT_FOUND, handle, handle_type);
return;
}
handle_type = it_types->second;
completer.Reply(ZX_OK, handle, handle_type);
}
void Control::DdkUnbindNew(ddk::UnbindTxn txn) { txn.Reply(); }
void Control::DdkRelease() { delete this; }
zx_status_t Control::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
DdkTransaction transaction(txn);
llcpp::fuchsia::hardware::goldfish::ControlDevice::Dispatch(this, msg, &transaction);
return transaction.Status();
}
zx_status_t Control::DdkGetProtocol(uint32_t proto_id, void* out_protocol) {
fbl::AutoLock lock(&lock_);
switch (proto_id) {
case ZX_PROTOCOL_GOLDFISH_PIPE: {
pipe_.GetProto(static_cast<goldfish_pipe_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_GOLDFISH_CONTROL: {
control_.GetProto(static_cast<goldfish_control_protocol_t*>(out_protocol));
return ZX_OK;
}
default:
return ZX_ERR_NOT_SUPPORTED;
}
}
zx_status_t Control::GoldfishControlGetColorBuffer(zx::vmo vmo, uint32_t* out_id) {
zx_koid_t koid = GetKoidForVmo(vmo);
if (koid == ZX_KOID_INVALID) {
return ZX_ERR_INVALID_ARGS;
}
fbl::AutoLock lock(&lock_);
auto it = buffer_handles_.find(koid);
if (it == buffer_handles_.end()) {
return ZX_ERR_INVALID_ARGS;
}
*out_id = it->second;
return ZX_OK;
}
int32_t Control::WriteLocked(uint32_t cmd_size, int32_t* consumed_size) {
TRACE_DURATION("gfx", "Control::Write", "cmd_size", cmd_size);
auto buffer = static_cast<pipe_cmd_buffer_t*>(cmd_buffer_.virt());
buffer->id = id_;
buffer->cmd = PIPE_CMD_CODE_WRITE;
buffer->status = PIPE_ERROR_INVAL;
buffer->rw_params.ptrs[0] = io_buffer_.phys();
buffer->rw_params.sizes[0] = cmd_size;
buffer->rw_params.buffers_count = 1;
buffer->rw_params.consumed_size = 0;
pipe_.Exec(id_);
*consumed_size = buffer->rw_params.consumed_size;
return buffer->status;
}
void Control::WriteLocked(uint32_t cmd_size) {
int32_t consumed_size;
int32_t result = WriteLocked(cmd_size, &consumed_size);
ZX_DEBUG_ASSERT(result >= 0);
ZX_DEBUG_ASSERT(consumed_size == static_cast<int32_t>(cmd_size));
}
zx_status_t Control::ReadResultLocked(uint32_t* result) {
TRACE_DURATION("gfx", "Control::ReadResult");
while (true) {
auto buffer = static_cast<pipe_cmd_buffer_t*>(cmd_buffer_.virt());
buffer->id = id_;
buffer->cmd = PIPE_CMD_CODE_READ;
buffer->status = PIPE_ERROR_INVAL;
buffer->rw_params.ptrs[0] = io_buffer_.phys();
buffer->rw_params.sizes[0] = sizeof(*result);
buffer->rw_params.buffers_count = 1;
buffer->rw_params.consumed_size = 0;
pipe_.Exec(id_);
// Positive consumed size always indicate a successful transfer.
if (buffer->rw_params.consumed_size) {
ZX_DEBUG_ASSERT(buffer->rw_params.consumed_size == sizeof(*result));
*result = *static_cast<uint32_t*>(io_buffer_.virt());
return ZX_OK;
}
// Early out if error is not because of back-pressure.
if (buffer->status != PIPE_ERROR_AGAIN) {
zxlogf(ERROR, "%s: reading result failed: %d", kTag, buffer->status);
return ZX_ERR_INTERNAL;
}
buffer->id = id_;
buffer->cmd = PIPE_CMD_CODE_WAKE_ON_READ;
buffer->status = PIPE_ERROR_INVAL;
pipe_.Exec(id_);
ZX_DEBUG_ASSERT(!buffer->status);
// Wait for pipe to become readable.
zx_status_t status =
pipe_event_.wait_one(llcpp::fuchsia::hardware::goldfish::SIGNAL_HANGUP |
llcpp::fuchsia::hardware::goldfish::SIGNAL_READABLE,
zx::time::infinite(), nullptr);
if (status != ZX_OK) {
if (status != ZX_ERR_CANCELED) {
zxlogf(ERROR, "%s: zx_object_wait_one failed: %d", kTag, status);
}
return status;
}
}
}
zx_status_t Control::ExecuteCommandLocked(uint32_t cmd_size, uint32_t* result) {
TRACE_DURATION("gfx", "Control::ExecuteCommand", "cnd_size", cmd_size);
WriteLocked(cmd_size);
return ReadResultLocked(result);
}
zx_status_t Control::CreateBufferLocked(uint32_t size, uint32_t* id) {
TRACE_DURATION("gfx", "Control::CreateBuffer", "size", size);
auto cmd = static_cast<CreateBufferCmd*>(io_buffer_.virt());
cmd->op = kOP_rcCreateBuffer;
cmd->size = kSize_rcCreateBuffer;
cmd->buffer_size = size;
return ExecuteCommandLocked(kSize_rcCreateBuffer, id);
}
zx_status_t Control::CreateColorBufferLocked(uint32_t width, uint32_t height, uint32_t format,
uint32_t* id) {
TRACE_DURATION("gfx", "Control::CreateColorBuffer", "width", width, "height", height);
auto cmd = static_cast<CreateColorBufferCmd*>(io_buffer_.virt());
cmd->op = kOP_rcCreateColorBuffer;
cmd->size = kSize_rcCreateColorBuffer;
cmd->width = width;
cmd->height = height;
cmd->internalformat = format;
return ExecuteCommandLocked(kSize_rcCreateColorBuffer, id);
}
void Control::CloseBufferOrColorBufferLocked(uint32_t id) {
ZX_DEBUG_ASSERT(buffer_handle_types_.find(id) != buffer_handle_types_.end());
auto buffer_type = buffer_handle_types_.at(id);
switch (buffer_type) {
case llcpp::fuchsia::hardware::goldfish::BufferHandleType::BUFFER:
CloseBufferLocked(id);
break;
case llcpp::fuchsia::hardware::goldfish::BufferHandleType::COLOR_BUFFER:
CloseColorBufferLocked(id);
break;
default:
// Otherwise buffer/colorBuffer was not created. We don't need to do
// anything.
break;
}
}
void Control::CloseColorBufferLocked(uint32_t id) {
TRACE_DURATION("gfx", "Control::CloseColorBuffer", "id", id);
auto cmd = static_cast<CloseColorBufferCmd*>(io_buffer_.virt());
cmd->op = kOP_rcCloseColorBuffer;
cmd->size = kSize_rcCloseColorBuffer;
cmd->id = id;
WriteLocked(kSize_rcCloseColorBuffer);
}
void Control::CloseBufferLocked(uint32_t id) {
TRACE_DURATION("gfx", "Control::CloseBuffer", "id", id);
auto cmd = static_cast<CloseBufferCmd*>(io_buffer_.virt());
cmd->op = kOP_rcCloseBuffer;
cmd->size = kSize_rcCloseBuffer;
cmd->id = id;
WriteLocked(kSize_rcCloseBuffer);
}
zx_status_t Control::SetColorBufferVulkanMode2Locked(uint32_t id, uint32_t mode,
uint32_t memory_property, uint32_t* result) {
TRACE_DURATION("gfx", "Control::SetColorBufferVulkanMode2Locked", "id", id, "mode", mode,
"memory_property", memory_property);
auto cmd = static_cast<SetColorBufferVulkanMode2Cmd*>(io_buffer_.virt());
cmd->op = kOP_rcSetColorBufferVulkanMode2;
cmd->size = kSize_rcSetColorBufferVulkanMode2;
cmd->id = id;
cmd->mode = mode;
cmd->memory_property = memory_property;
return ExecuteCommandLocked(kSize_rcSetColorBufferVulkanMode2, result);
}
zx_status_t Control::MapGpaToBufferHandleLocked(uint32_t id, uint64_t gpa, uint32_t* result) {
TRACE_DURATION("gfx", "Control::MapGpaToBufferHandleLocked", "id", id, "gpa", gpa);
auto cmd = static_cast<MapGpaToBufferHandleCmd*>(io_buffer_.virt());
cmd->op = kOP_rcMapGpaToBufferHandle;
cmd->size = kSize_rcMapGpaToBufferHandle;
cmd->id = id;
cmd->gpa = gpa;
return ExecuteCommandLocked(kSize_rcMapGpaToBufferHandle, result);
}
void Control::RemoveHeap(Heap* heap) {
fbl::AutoLock lock(&lock_);
heaps_.erase(*heap);
}
} // namespace goldfish
static constexpr zx_driver_ops_t goldfish_control_driver_ops = []() -> zx_driver_ops_t {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = goldfish::Control::Create;
return ops;
}();
// clang-format off
#ifdef GOLDFISH_CONTROL_USE_COMPOSITE_DEVICE
ZIRCON_DRIVER_BEGIN(goldfish_control_composite, goldfish_control_driver_ops, "zircon", "0.1", 4)
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_COMPOSITE),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GOOGLE),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_GOLDFISH),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_GOLDFISH_CONTROL),
ZIRCON_DRIVER_END(goldfish_control_composite)
#else
ZIRCON_DRIVER_BEGIN(goldfish_control, goldfish_control_driver_ops, "zircon", "0.1", 1)
BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_GOLDFISH_PIPE),
ZIRCON_DRIVER_END(goldfish_control)
#endif
// clang-format on