blob: eaabaefc0eaf7283455bf243f1e1813321b04226 [file] [log] [blame]
// Copyright 2018 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 "vc-display.h"
#include <fcntl.h>
#include <fuchsia/hardware/display/controller/c/banjo.h>
#include <fuchsia/hardware/display/llcpp/fidl.h>
#include <fuchsia/io/llcpp/fidl.h>
#include <fuchsia/sysmem/llcpp/fidl.h>
#include <lib/async/cpp/wait.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/io.h>
#include <lib/fidl/coding.h>
#include <lib/fidl/llcpp/connect_service.h>
#include <lib/image-format-llcpp/image-format-llcpp.h>
#include <lib/service/llcpp/service.h>
#include <lib/statusor/endpoint-or-error.h>
#include <lib/statusor/status-macros.h>
#include <lib/zx/channel.h>
#include <lib/zx/vmo.h>
#include <string.h>
#include <zircon/assert.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <fbl/unique_fd.h>
#include <src/lib/fsl/handles/object_info.h>
#include "vc.h"
namespace fhd = fuchsia_hardware_display;
namespace sysmem = fuchsia_sysmem;
namespace fio = fuchsia_io;
using fhd::wire::VirtconMode;
static async_dispatcher_t* dc_dispatcher = nullptr;
// At any point, |dc_wait| will either be waiting on the display controller device directory
// for a display controller instance or it will be waiting on a display controller interface
// for messages.
static async::Wait dc_wait;
static std::unique_ptr<fidl::WireSyncClient<fhd::Controller>> dc_client;
static std::unique_ptr<fidl::WireSyncClient<sysmem::Allocator>> sysmem_allocator;
static uint64_t next_buffer_collection_id = 1;
static struct list_node display_list = LIST_INITIAL_VALUE(display_list);
static bool primary_bound = false;
static bool g_hide_on_boot = false;
// remember whether the virtual console controls the display
bool g_vc_owns_display = false;
static void vc_find_display_controller();
bool is_primary_bound() { return primary_bound; }
#if BUILD_FOR_DISPLAY_TEST
struct list_node* get_display_list() {
return &display_list;
}
fidl::WireSyncClient<sysmem::Allocator>* get_sysmem_allocator() { return sysmem_allocator.get(); }
#endif // BUILD_FOR_DISPLAY_TEST
static constexpr const char* kDisplayControllerDir = "/dev/class/display-controller";
static int dc_dir_fd;
static zx_handle_t dc_device;
static zx_status_t vc_set_mode(VirtconMode mode) {
return dc_client->SetVirtconMode(static_cast<uint8_t>(mode)).status();
}
void vc_attach_to_main_display(vc_t* vc) {
if (list_is_empty(&display_list)) {
return;
}
display_info_t* primary = list_peek_head_type(&display_list, display_info_t, node);
vc->graphics = primary->graphics;
vc_attach_gfx(vc);
}
void vc_toggle_framebuffer() {
if (list_is_empty(&display_list)) {
return;
}
zx_status_t status =
vc_set_mode(!g_vc_owns_display ? VirtconMode::kForced : VirtconMode::kFallback);
if (status != ZX_OK) {
printf("vc: Failed to toggle ownership %d\n", status);
}
}
static void handle_ownership_change(bool has_ownership) {
g_vc_owns_display = has_ownership;
#if !BUILD_FOR_DISPLAY_TEST
// If we've gained it, repaint
if (g_vc_owns_display && g_active_vc) {
vc_full_repaint(g_active_vc);
vc_render(g_active_vc);
}
#endif // !BUILD_FOR_DISPLAY_TEST
}
zx_status_t create_layer(uint64_t display_id, uint64_t* layer_id) {
auto rsp = dc_client->CreateLayer();
RETURN_IF_ERROR(rsp, "vc: Create layer call failed");
if (rsp->res != ZX_OK) {
printf("vc: Failed to create layer %d\n", rsp->res);
return rsp->res;
}
*layer_id = rsp->layer_id;
return ZX_OK;
}
void destroy_layer(uint64_t layer_id) {
if (!dc_client->DestroyLayer(layer_id).ok()) {
printf("vc: Failed to destroy layer\n");
}
}
void release_image(uint64_t image_id) {
if (!dc_client->ReleaseImage(image_id).ok()) {
printf("vc: Failed to release image\n");
}
}
static zx_status_t handle_display_added(fhd::wire::Info* info) {
display_info_t* display_info =
reinterpret_cast<display_info_t*>(calloc(1, sizeof(display_info_t)));
if (!display_info) {
printf("vc: failed to alloc display info\n");
return ZX_ERR_NO_MEMORY;
}
zx_status_t status;
if ((status = create_layer(info->id, &display_info->layer_id)) != ZX_OK) {
printf("vc: failed to create display layer\n");
free(display_info);
return status;
}
display_info->id = info->id;
display_info->width = info->modes[0].horizontal_resolution;
display_info->height = info->modes[0].vertical_resolution;
display_info->format = static_cast<int32_t>(info->pixel_format[0]);
display_info->image_id = 0;
display_info->image_vmo = ZX_HANDLE_INVALID;
display_info->buffer_collection_id = 0;
display_info->bound = false;
display_info->log_vc = nullptr;
display_info->graphics = nullptr;
list_add_tail(&display_list, &display_info->node);
return ZX_OK;
}
void handle_display_removed(uint64_t id) {
if (list_is_empty(&display_list)) {
printf("vc: No displays when removing %ld\n", id);
return;
}
bool was_primary = list_peek_head_type(&display_list, display_info_t, node)->id == id;
display_info_t* info = nullptr;
display_info_t* temp = nullptr;
list_for_every_entry_safe (&display_list, info, temp, display_info_t, node) {
if (info->id == id) {
destroy_layer(info->layer_id);
release_image(info->image_id);
list_delete(&info->node);
zx_handle_close(info->image_vmo);
if (info->buffer_collection_id) {
dc_client->ReleaseBufferCollection(info->buffer_collection_id);
}
if (info->graphics) {
free(info->graphics);
}
if (info->log_vc) {
log_delete_vc(info->log_vc);
}
free(info);
}
}
if (was_primary) {
set_log_listener_active(false);
primary_bound = false;
}
}
static zx_status_t get_single_framebuffer(zx_handle_t* vmo_out, uint32_t* stride_out) {
auto rsp = dc_client->GetSingleBufferFramebuffer();
RETURN_IF_ERROR(rsp, "vc: Failed to get single framebuffer");
if (rsp->res != ZX_OK) {
// Don't print an error since this can happen on non-single-framebuffer
// systems.
return rsp->res;
}
if (!rsp->vmo) {
return ZX_ERR_INTERNAL;
}
*vmo_out = rsp->vmo.release();
*stride_out = rsp->stride;
return ZX_OK;
}
zx_status_t import_buffer_collection(uint64_t collection_id, fhd::wire::ImageConfig* config,
uint64_t* id) {
constexpr uint32_t kImageIndex = 0;
auto import_rsp = dc_client->ImportImage(*config, collection_id, kImageIndex);
RETURN_IF_ERROR(import_rsp, "vc: Failed to import image call");
if (import_rsp->res != ZX_OK) {
printf("vc: Failed to import vmo collection %d\n", import_rsp->res);
return import_rsp->res;
}
*id = import_rsp->image_id;
return ZX_OK;
}
zx_status_t import_vmo(zx_handle_t vmo, fhd::wire::ImageConfig* config, uint64_t* id) {
zx_handle_t vmo_dup;
zx_status_t status;
if ((status = zx_handle_duplicate(vmo, ZX_RIGHT_SAME_RIGHTS, &vmo_dup)) != ZX_OK) {
printf("vc: Failed to dup fb handle %d\n", status);
return status;
}
auto import_rsp = dc_client->ImportVmoImage(*config, zx::vmo(vmo_dup), 0);
RETURN_IF_ERROR(import_rsp, "vc: Failed to import vmo call");
if (import_rsp->res != ZX_OK) {
printf("vc: Failed to import vmo %d\n", import_rsp->res);
return import_rsp->res;
}
*id = import_rsp->image_id;
return ZX_OK;
}
zx_status_t set_display_layer(uint64_t display_id, uint64_t layer_id) {
RETURN_IF_ERROR(dc_client->SetDisplayLayers(display_id, fidl::VectorView<uint64_t>::FromExternal(
&layer_id, layer_id ? 1 : 0)),
"vc: Failed to set display layers");
return ZX_OK;
}
zx_status_t configure_layer(display_info_t* display, uint64_t layer_id, uint64_t image_id,
fhd::wire::ImageConfig* config) {
RETURN_IF_ERROR(dc_client->SetLayerPrimaryConfig(layer_id, *config),
"vc: Failed to set layer config");
RETURN_IF_ERROR(dc_client->SetLayerPrimaryPosition(
layer_id, fhd::wire::Transform::kIdentity,
fhd::wire::Frame{.width = config->width, .height = config->height},
fhd::wire::Frame{.width = display->width, .height = display->height}),
"vc: Failed to set layer position");
RETURN_IF_ERROR(dc_client->SetLayerImage(layer_id, image_id, 0, 0), "vc: Failed to set image");
return ZX_OK;
}
zx_status_t apply_configuration() {
// Validate and then apply the new configuration
auto check_rsp = dc_client->CheckConfig(false);
RETURN_IF_ERROR(check_rsp, "vc: Failed to validate display config");
if (check_rsp->res != fhd::wire::ConfigResult::kOk) {
printf("vc: Config not valid %d\n", static_cast<int>(check_rsp->res));
return ZX_ERR_INTERNAL;
}
RETURN_IF_ERROR(dc_client->ApplyConfig(), "Applying config failed");
return ZX_OK;
}
static zx_status_t create_buffer_collection(
display_info_t* display, uint64_t id,
std::unique_ptr<fidl::WireSyncClient<sysmem::BufferCollection>>* collection_client) {
fidl::ClientEnd<sysmem::BufferCollectionToken> token;
{
zx::status token_server = fidl::CreateEndpoints(&token);
if (token_server.is_error()) {
printf("vc: Failed to create collection channel: %d\n", token_server.status_value());
return token_server.status_value();
}
zx_status_t status =
sysmem_allocator->AllocateSharedCollection(std::move(*token_server)).status();
if (status != ZX_OK) {
printf("vc: Failed to allocate shared collection: %d\n", status);
return status;
}
}
constexpr uint32_t kVcNamePriority = 1000000;
const char kVcCollectionName[] = "vc-framebuffer";
zx_status_t status =
fidl::WireCall(token).SetName(kVcNamePriority, fidl::StringView(kVcCollectionName)).status();
if (status != ZX_OK) {
printf("vc: Failed to set debug info: %d\n", status);
return status;
}
fidl::ClientEnd<sysmem::BufferCollectionToken> display_token;
{
zx::status display_token_server = fidl::CreateEndpoints(&display_token);
if (display_token_server.is_error()) {
printf("vc: Failed to allocate display token: %d\n", display_token_server.status_value());
return display_token_server.status_value();
}
status = fidl::WireCall(token)
.Duplicate(ZX_RIGHT_SAME_RIGHTS, *std::move(display_token_server))
.status();
if (status != ZX_OK) {
printf("vc: Failed to duplicate token: %d\n", status);
return status;
}
}
fidl::WireSyncClient<sysmem::BufferCollection> collection;
{
auto endpoints = fidl::CreateEndpoints<sysmem::BufferCollection>();
if (endpoints.is_error()) {
printf("vc: Failed to create collection channel: %d\n", endpoints.status_value());
return endpoints.status_value();
}
status = sysmem_allocator->BindSharedCollection(std::move(token), std::move(endpoints->server))
.status();
if (status != ZX_OK) {
printf("vc: Failed to bind collection: %d\n", status);
return status;
}
collection = fidl::BindSyncClient(std::move(endpoints->client));
}
status = collection.Sync().status();
if (status != ZX_OK) {
printf("vc: Failed to sync collection: %d\n", status);
return status;
}
auto import_rsp = dc_client->ImportBufferCollection(id, std::move(display_token));
RETURN_IF_ERROR(import_rsp, "vc: Failed to import buffer collection");
if (import_rsp->res != ZX_OK) {
printf("vc: Import buffer collection error\n");
return import_rsp->res;
}
auto set_display_constraints =
dc_client->SetBufferCollectionConstraints(id, display->image_config);
RETURN_IF_ERROR(set_display_constraints, "vc: Failed to set display constraints");
if (set_display_constraints->res != ZX_OK) {
printf("vc: Display constraints error\n");
return set_display_constraints->res;
}
sysmem::wire::BufferCollectionConstraints constraints;
constraints.usage.cpu = sysmem::wire::kCpuUsageWriteOften | sysmem::wire::kCpuUsageRead;
constraints.min_buffer_count = 1;
constraints.has_buffer_memory_constraints = true;
constraints.buffer_memory_constraints = image_format::GetDefaultBufferMemoryConstraints();
constraints.buffer_memory_constraints.ram_domain_supported = true;
constraints.image_format_constraints_count = 1;
auto& image_constraints = constraints.image_format_constraints[0];
image_constraints = image_format::GetDefaultImageFormatConstraints();
fuchsia_sysmem_PixelFormat pixel_format;
if (!ImageFormatConvertZxToSysmem(display->format, &pixel_format)) {
printf("vc: Unsupported pixel format\n");
return ZX_ERR_INVALID_ARGS;
}
image_constraints.pixel_format = image_format::GetCppPixelFormat(pixel_format);
image_constraints.color_spaces_count = 1;
image_constraints.color_space[0].type = sysmem::wire::ColorSpaceType::kSrgb;
image_constraints.min_coded_width = display->width;
image_constraints.min_coded_height = display->height;
image_constraints.max_coded_width = 0xffffffff;
image_constraints.max_coded_height = 0xffffffff;
image_constraints.min_bytes_per_row = 0;
image_constraints.max_bytes_per_row = 0xffffffff;
RETURN_IF_ERROR(collection.SetConstraints(true, constraints), "vc: Failed to set constraints");
*collection_client =
std::make_unique<fidl::WireSyncClient<sysmem::BufferCollection>>(std::move(collection));
return ZX_OK;
}
zx_status_t alloc_display_info_vmo(display_info_t* display) {
display->buffer_collection_id = 0;
display->image_config.height = display->height;
display->image_config.width = display->width;
display->image_config.pixel_format = display->format;
display->image_config.type = IMAGE_TYPE_SIMPLE;
if (get_single_framebuffer(&display->image_vmo, &display->stride) != ZX_OK) {
uint64_t buffer_collection_id = next_buffer_collection_id++;
std::unique_ptr<fidl::WireSyncClient<sysmem::BufferCollection>> collection_client;
zx_status_t status =
create_buffer_collection(display, buffer_collection_id, &collection_client);
if (status != ZX_OK) {
return status;
}
display->buffer_collection_id = buffer_collection_id;
auto info_result = collection_client->WaitForBuffersAllocated();
RETURN_IF_ERROR(info_result, "vc: Couldn't wait for buffers allocated");
if (info_result->status != ZX_OK) {
printf("vc: Couldn't wait for buffers allocated\n");
return info_result->status;
}
uint32_t bytes_per_row;
bool got_stride = image_format::GetMinimumRowBytes(
info_result->buffer_collection_info.settings.image_format_constraints, display->width,
&bytes_per_row);
if (!got_stride) {
return ZX_ERR_INVALID_ARGS;
}
display->stride = bytes_per_row / ZX_PIXEL_FORMAT_BYTES(display->format);
display->image_vmo = info_result->buffer_collection_info.buffers[0].vmo.release();
collection_client->Close();
}
return ZX_OK;
}
zx_status_t rebind_display(bool use_all) {
// Arbitrarily pick the oldest display as the primary dispay
display_info* primary = list_peek_head_type(&display_list, display_info, node);
if (primary == nullptr) {
printf("vc: No display to bind to\n");
return ZX_ERR_NO_RESOURCES;
}
zx_status_t status;
// This happens when the last primary disconnected and a new, already
// bound display becomes primary. We must un-bind the display and
// rebind.
if (!primary_bound && primary->bound) {
// Remove the primary display's log console.
if (primary->log_vc) {
log_delete_vc(primary->log_vc);
primary->log_vc = nullptr;
}
// Switch all of the current vcs to using this display.
vc_change_graphics(primary->graphics);
}
display_info_t* info = nullptr;
list_for_every_entry (&display_list, info, display_info_t, node) {
if (!use_all && info != primary) {
// If we're not showing anything on this display, remove its layer.
if ((status = set_display_layer(info->id, 0)) != ZX_OK) {
break;
}
} else if (info->image_id == 0) {
// If we want to display something but aren't, configure the display.
if ((status = alloc_display_info_vmo(info)) != ZX_OK) {
printf("vc: failed to allocate vmo for new display %d\n", status);
break;
}
info->graphics = reinterpret_cast<vc_gfx_t*>(calloc(1, sizeof(vc_gfx_t)));
if ((status = vc_init_gfx(info->graphics, info->image_vmo, info->width, info->height,
info->format, info->stride)) != ZX_OK) {
printf("vc: failed to initialize graphics for new display %d\n", status);
break;
}
// If this is not the primary display then create the log console
// for the display.
if (info != primary) {
if ((status = log_create_vc(info->graphics, &info->log_vc)) != ZX_OK) {
break;
}
}
info->bound = true;
fhd::wire::ImageConfig config = info->image_config;
if (info->buffer_collection_id) {
if ((status = import_buffer_collection(info->buffer_collection_id, &config,
&info->image_id)) != ZX_OK) {
break;
}
} else {
if ((status = import_vmo(info->image_vmo, &config, &info->image_id)) != ZX_OK) {
break;
}
}
if ((status = set_display_layer(info->id, info->layer_id)) != ZX_OK) {
break;
}
if ((status = configure_layer(info, info->layer_id, info->image_id, &config) != ZX_OK)) {
break;
}
}
}
if (status == ZX_OK && apply_configuration() == ZX_OK) {
// Only listen for logs when we have somewhere to print them. Also,
// use a repeating wait so that we don't add/remove observers for each
// log message (which is helpful when tracing the addition/removal of
// observers).
set_log_listener_active(true);
vc_change_graphics(primary->graphics);
printf("vc: Successfully attached to display %ld\n", primary->id);
primary_bound = true;
return ZX_OK;
} else {
display_info_t* info = nullptr;
list_for_every_entry (&display_list, info, display_info_t, node) {
if (info->image_id) {
release_image(info->image_id);
info->image_id = 0;
}
if (info->image_vmo) {
zx_handle_close(info->image_vmo);
info->image_vmo = 0;
}
if (info->graphics) {
free(info->graphics);
info->graphics = nullptr;
}
}
if (use_all) {
return rebind_display(false);
} else {
printf("vc: Failed to bind to displays\n");
return ZX_ERR_INTERNAL;
}
}
}
static zx_status_t handle_displays_changed(fidl::VectorView<fhd::wire::Info>& added,
fidl::VectorView<uint64_t>& removed) {
for (auto& display : added) {
zx_status_t status = handle_display_added(&display);
if (status != ZX_OK) {
return status;
}
}
for (auto& display_id : removed) {
handle_display_removed(display_id);
}
return rebind_display(true);
}
zx_status_t dc_callback_handler(zx_signals_t signals) {
if (signals & ZX_CHANNEL_PEER_CLOSED) {
printf("vc: Displays lost\n");
while (!list_is_empty(&display_list)) {
handle_display_removed(list_peek_head_type(&display_list, display_info_t, node)->id);
}
vc_change_graphics(nullptr);
zx_handle_close(dc_device);
dc_client.reset();
vc_find_display_controller();
return ZX_ERR_STOP;
}
ZX_DEBUG_ASSERT(signals & ZX_CHANNEL_READABLE);
class EventHandler : public fidl::WireSyncEventHandler<fhd::Controller> {
public:
EventHandler() = default;
void OnDisplaysChanged(fidl::WireResponse<fhd::Controller::OnDisplaysChanged>* event) override {
handle_displays_changed(event->added, event->removed);
}
void OnVsync(fidl::WireResponse<fhd::Controller::OnVsync>* event) override {}
void OnClientOwnershipChange(
fidl::WireResponse<fhd::Controller::OnClientOwnershipChange>* event) override {
handle_ownership_change(event->has_ownership);
}
zx_status_t Unknown() override {
printf("vc: Unknown display callback message\n");
return ZX_OK;
}
};
EventHandler event_handler;
return dc_client->HandleOneEvent(event_handler).status();
}
#if BUILD_FOR_DISPLAY_TEST
void initialize_display_channel(fidl::ClientEnd<fhd::Controller> channel) {
dc_client = std::make_unique<fidl::WireSyncClient<fhd::Controller>>(
fidl::BindSyncClient(std::move(channel)));
dc_wait.set_object(dc_client->channel().get());
}
#else
static zx_status_t vc_dc_event(uint32_t evt, const char* name) {
if ((evt != fio::wire::kWatchEventExisting) && (evt != fio::wire::kWatchEventAdded)) {
return ZX_OK;
}
printf("vc: new display device %s/%s\n", kDisplayControllerDir, name);
char buf[64];
snprintf(buf, 64, "%s/%s", kDisplayControllerDir, name);
fbl::unique_fd fd(open(buf, O_RDWR));
if (!fd) {
printf("vc: failed to open display controller device\n");
return ZX_OK;
}
zx::channel device_server, device_client;
zx_status_t status = zx::channel::create(0, &device_server, &device_client);
if (status != ZX_OK) {
return status;
}
auto dc_endpoints = fidl::CreateEndpoints<fhd::Controller>();
if (dc_endpoints.is_error()) {
return dc_endpoints.status_value();
}
fdio_cpp::FdioCaller caller(std::move(fd));
auto open_rsp =
fidl::WireCall(caller.borrow_as<fhd::Provider>())
.OpenVirtconController(std::move(device_server), std::move(dc_endpoints->server));
if (!open_rsp.ok()) {
return open_rsp.status();
}
if (open_rsp->s != ZX_OK) {
return open_rsp->s;
}
dc_device = device_client.release();
dc_client =
std::make_unique<fidl::WireSyncClient<fhd::Controller>>(std::move(dc_endpoints->client));
zx_handle_close(dc_wait.object());
status = vc_set_mode(g_hide_on_boot ? fhd::wire::VirtconMode::kInactive
: fhd::wire::VirtconMode::kFallback);
if (status != ZX_OK) {
printf("vc: Failed to set initial ownership %d\n", status);
vc_find_display_controller();
return ZX_ERR_STOP;
}
ZX_DEBUG_ASSERT(!dc_wait.is_pending());
dc_wait.set_object(dc_client->channel().get());
dc_wait.set_trigger(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED);
dc_wait.set_handler([](async_dispatcher_t* dispatcher, async::Wait* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
if (status != ZX_OK) {
return;
}
status = dc_callback_handler(signal->observed);
if (status != ZX_OK) {
return;
}
wait->Begin(dispatcher);
});
status = dc_wait.Begin(dc_dispatcher);
if (status != ZX_OK) {
printf("vc: Failed to set port waiter %d\n", status);
vc_find_display_controller();
}
return ZX_ERR_STOP;
}
#endif // BUILD_FOR_DISPLAY_TEST
zx_status_t handle_device_dir_event(zx_handle_t handle, zx_signals_t signals,
zx_status_t (*event_handler)(unsigned event, const char* msg)) {
if (!(signals & ZX_CHANNEL_READABLE)) {
printf("vc: device directory died\n");
return ZX_ERR_STOP;
}
// Buffer contains events { Opcode, Len, Name[Len] }
// See zircon/device/vfs.h for more detail
// extra byte is for temporary NUL
uint8_t buf[fio::wire::kMaxBuf + 1];
uint32_t len;
if (zx_channel_read(handle, 0, buf, nullptr, sizeof(buf) - 1, 0, &len, nullptr) < 0) {
printf("vc: failed to read from device directory\n");
return ZX_ERR_STOP;
}
uint8_t* msg = buf;
while (len >= 2) {
uint8_t event = *msg++;
uint8_t namelen = *msg++;
if (len < (namelen + 2u)) {
printf("vc: malformed device directory message\n");
return ZX_ERR_STOP;
}
// add temporary nul
uint8_t tmp = msg[namelen];
msg[namelen] = 0;
zx_status_t status = event_handler(event, reinterpret_cast<char*>(msg));
if (status != ZX_OK) {
return status;
}
msg[namelen] = tmp;
msg += namelen;
len -= (namelen + 2u);
}
return ZX_OK;
}
static zx_status_t vc_dc_dir_event_cb(async_dispatcher_t* dispatcher, async::Wait* wait,
zx_status_t status, const zx_packet_signal_t* signal) {
#if BUILD_FOR_DISPLAY_TEST
return ZX_ERR_NOT_SUPPORTED;
#else
if (status != ZX_OK) {
return status;
}
status = handle_device_dir_event(wait->object(), signal->observed, vc_dc_event);
if (status != ZX_OK) {
return status;
}
wait->Begin(dispatcher);
return ZX_OK;
#endif
}
static void vc_find_display_controller() {
auto endpoints = fidl::CreateEndpoints<fio::DirectoryWatcher>();
if (endpoints.is_error()) {
printf("vc: Failed to create dc watcher channel\n");
return;
}
auto [client, server] = *std::move(endpoints);
fdio_cpp::UnownedFdioCaller dir_caller(dc_dir_fd);
auto result = fidl::WireCall(dir_caller.directory())
.Watch(fio::wire::kWatchMaskAll, 0, server.TakeChannel());
if (result.status() != ZX_OK) {
printf("vc: Failed to watch dc directory\n");
return;
}
ZX_DEBUG_ASSERT(!dc_wait.is_pending());
dc_wait.set_object(client.TakeChannel().release());
dc_wait.set_trigger(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED);
dc_wait.set_handler([](async_dispatcher_t* dispatcher, async::Wait* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
vc_dc_dir_event_cb(dispatcher, wait, status, signal);
});
zx_status_t status = dc_wait.Begin(dc_dispatcher);
if (status != ZX_OK) {
printf("vc: Failed to wait on dc directory\n");
}
}
bool vc_display_init(async_dispatcher_t* dispatcher, bool hide_on_boot) {
g_hide_on_boot = hide_on_boot;
fbl::unique_fd fd(open(kDisplayControllerDir, O_DIRECTORY | O_RDONLY));
if (!fd) {
return false;
}
dc_dir_fd = fd.release();
ZX_DEBUG_ASSERT(dc_dispatcher == nullptr);
dc_dispatcher = dispatcher;
vc_find_display_controller();
return true;
}
bool vc_sysmem_connect() {
auto sysmem_client = service::Connect<fuchsia_sysmem::Allocator>();
if (sysmem_client.is_error()) {
return false;
}
sysmem_allocator =
std::make_unique<fidl::WireSyncClient<sysmem::Allocator>>(*std::move(sysmem_client));
sysmem_allocator->SetDebugClientInfo(fidl::StringView::FromExternal(fsl::GetCurrentProcessName()),
fsl::GetCurrentProcessKoid());
return true;
}