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 <string.h> 6#include <port/port.h> 7#include <fcntl.h> 8#include <lib/fidl/coding.h> 9#include <zircon/assert.h> 10#include <zircon/device/display-controller.h> 11#include <zircon/process.h> 12#include <zircon/processargs.h> 13#include <zircon/status.h> 14#include <zircon/syscalls.h> 15 16#include "fuchsia/display/c/fidl.h" 17#include "vc.h" 18 19static port_handler_t dc_ph; 20static int dc_fd; 21 22typedef struct display_info { 23 uint64_t id; 24 uint32_t width; 25 uint32_t height; 26 uint32_t stride; 27 zx_pixel_format_t format; 28 29 uint64_t image_id; 30 uint64_t layer_id; 31 32 struct list_node node; 33} display_info_t; 34 35static struct list_node display_list = LIST_INITIAL_VALUE(display_list); 36 37static bool displays_bound = false; 38// Owned by vc_gfx, only valid when displays_bound is true 39static zx_handle_t image_vmo = ZX_HANDLE_INVALID; 40static fuchsia_display_ImageConfig image_config; 41 42// remember whether the virtual console controls the display 43bool g_vc_owns_display = false; 44 45static zx_status_t vc_set_mode(uint8_t mode) { 46 fuchsia_display_ControllerSetVirtconModeRequest request; 47 request.hdr.ordinal = fuchsia_display_ControllerSetVirtconModeOrdinal; 48 request.mode = mode; 49 50 return zx_channel_write(dc_ph.handle, 0, &request, sizeof(request), nullptr, 0); 51} 52 53void vc_toggle_framebuffer() { 54 if (list_is_empty(&display_list)) { 55 return; 56 } 57 58 zx_status_t status = vc_set_mode(!g_vc_owns_display ? 59 fuchsia_display_VirtconMode_FORCED : fuchsia_display_VirtconMode_FALLBACK); 60 if (status != ZX_OK) { 61 printf("vc: Failed to toggle ownership %d\n", status); 62 } 63} 64 65static zx_status_t decode_message(void* bytes, uint32_t num_bytes) { 66 fidl_message_header_t* header = (fidl_message_header_t*) bytes; 67 68 if (num_bytes < sizeof(fidl_message_header_t)) { 69 printf("vc: Unexpected short message (size=%d)\n", num_bytes); 70 return ZX_ERR_INTERNAL; 71 } 72 zx_status_t res; 73 74 const fidl_type_t* table = nullptr; 75 if (header->ordinal == fuchsia_display_ControllerDisplaysChangedOrdinal) { 76 table = &fuchsia_display_ControllerDisplaysChangedEventTable; 77 } else if (header->ordinal == fuchsia_display_ControllerClientOwnershipChangeOrdinal) { 78 table = &fuchsia_display_ControllerClientOwnershipChangeEventTable; 79 } 80 if (table != nullptr) { 81 const char* err; 82 if ((res = fidl_decode(table, bytes, num_bytes, nullptr, 0, &err)) != ZX_OK) { 83 printf("vc: Error decoding message %d: %s\n", header->ordinal, err); 84 } 85 } else { 86 printf("vc: Error unknown ordinal %d\n", header->ordinal); 87 res = ZX_ERR_NOT_SUPPORTED; 88 } 89 return res; 90} 91 92static void handle_ownership_change(fuchsia_display_ControllerClientOwnershipChangeEvent* evt) { 93 g_vc_owns_display = evt->has_ownership; 94 95 // If we've gained it, repaint 96 if (g_vc_owns_display && g_active_vc) { 97 vc_full_repaint(g_active_vc); 98 vc_render(g_active_vc); 99 } 100} 101 102static zx_status_t create_layer(uint64_t display_id, uint64_t* layer_id) { 103 fuchsia_display_ControllerCreateLayerRequest create_layer_msg; 104 create_layer_msg.hdr.ordinal = fuchsia_display_ControllerCreateLayerOrdinal; 105 106 fuchsia_display_ControllerCreateLayerResponse create_layer_rsp; 107 zx_channel_call_args_t call_args = {}; 108 call_args.wr_bytes = &create_layer_msg; 109 call_args.rd_bytes = &create_layer_rsp; 110 call_args.wr_num_bytes = sizeof(create_layer_msg); 111 call_args.rd_num_bytes = sizeof(create_layer_rsp); 112 uint32_t actual_bytes, actual_handles; 113 zx_status_t status; 114 if ((status = zx_channel_call(dc_ph.handle, 0, ZX_TIME_INFINITE, &call_args, 115 &actual_bytes, &actual_handles)) != ZX_OK) { 116 printf("vc: Create layer call failed: %d (%s)\n", status, zx_status_get_string(status)); 117 return status; 118 } 119 if (create_layer_rsp.res != ZX_OK) { 120 printf("vc: Failed to create layer %d\n", create_layer_rsp.res); 121 return create_layer_rsp.res; 122 } 123 124 *layer_id = create_layer_rsp.layer_id; 125 return ZX_OK; 126} 127 128 129 130static void destroy_layer(uint64_t layer_id) { 131 fuchsia_display_ControllerDestroyLayerRequest destroy_msg; 132 destroy_msg.hdr.ordinal = fuchsia_display_ControllerDestroyLayerOrdinal; 133 destroy_msg.layer_id = layer_id; 134 135 if (zx_channel_write(dc_ph.handle, 0, &destroy_msg, sizeof(destroy_msg), nullptr, 0) != ZX_OK) { 136 printf("vc: Failed to destroy layer\n"); 137 } 138} 139 140static void release_image(uint64_t image_id) { 141 fuchsia_display_ControllerReleaseImageRequest release_msg; 142 release_msg.hdr.ordinal = fuchsia_display_ControllerReleaseImageOrdinal; 143 release_msg.image_id = image_id; 144 145 if (zx_channel_write(dc_ph.handle, 0, &release_msg, sizeof(release_msg), nullptr, 0)) { 146 printf("vc: Failed to release image\n"); 147 } 148} 149 150static zx_status_t handle_display_added(fuchsia_display_Info* info, fuchsia_display_Mode* mode, 151 int32_t pixel_format) { 152 fuchsia_display_ControllerComputeLinearImageStrideRequest stride_msg; 153 stride_msg.hdr.ordinal = fuchsia_display_ControllerComputeLinearImageStrideOrdinal; 154 stride_msg.width = mode->horizontal_resolution; 155 stride_msg.pixel_format = pixel_format; 156 157 fuchsia_display_ControllerComputeLinearImageStrideResponse stride_rsp; 158 zx_channel_call_args_t stride_call = {}; 159 stride_call.wr_bytes = &stride_msg; 160 stride_call.rd_bytes = &stride_rsp; 161 stride_call.wr_num_bytes = sizeof(stride_msg); 162 stride_call.rd_num_bytes = sizeof(stride_rsp); 163 uint32_t actual_bytes, actual_handles; 164 zx_status_t status; 165 if ((status = zx_channel_call(dc_ph.handle, 0, ZX_TIME_INFINITE, &stride_call, 166 &actual_bytes, &actual_handles)) != ZX_OK) { 167 printf("vc: Failed to compute fb stride: %d (%s)\n", status, 168 zx_status_get_string(status)); 169 return status; 170 } 171 172 if (stride_rsp.stride < mode->horizontal_resolution) { 173 printf("vc: Got bad stride\n"); 174 return ZX_ERR_INVALID_ARGS; 175 } 176 177 display_info_t* display_info = 178 reinterpret_cast<display_info_t*>(malloc(sizeof(display_info_t))); 179 if (!display_info) { 180 printf("vc: failed to alloc display info\n"); 181 return ZX_ERR_NO_MEMORY; 182 } 183 184 if ((status = create_layer(info->id, &display_info->layer_id)) != ZX_OK) { 185 printf("vc: failed to create display layer\n"); 186 free(display_info); 187 return status; 188 } 189 190 display_info->id = info->id; 191 display_info->width = mode->horizontal_resolution; 192 display_info->height = mode->vertical_resolution; 193 display_info->stride = stride_rsp.stride; 194 display_info->format = reinterpret_cast<int32_t*>(info->pixel_format.data)[0]; 195 display_info->image_id = 0; 196 197 list_add_tail(&display_list, &display_info->node); 198 199 return ZX_OK; 200} 201 202static void handle_display_removed(uint64_t id) { 203 if (list_is_empty(&display_list)) { 204 printf("vc: No displays when removing %ld\n", id); 205 return; 206 } 207 208 bool was_primary = list_peek_head_type(&display_list, display_info_t, node)->id == id; 209 display_info_t* info = nullptr; 210 display_info_t* temp = nullptr; 211 list_for_every_entry_safe(&display_list, info, temp, display_info_t, node) { 212 if (info->id == id) { 213 destroy_layer(info->layer_id); 214 release_image(info->image_id); 215 216 list_delete(&info->node); 217 free(info); 218 } else if (was_primary) { 219 release_image(info->image_id); 220 info->image_id = 0; 221 } 222 } 223 224 if (was_primary) { 225 set_log_listener_active(false); 226 vc_free_gfx(); 227 displays_bound = false; 228 } 229} 230 231static zx_status_t allocate_vmo(uint32_t size, zx_handle_t* vmo_out) { 232 fuchsia_display_ControllerAllocateVmoRequest alloc_msg; 233 alloc_msg.hdr.ordinal = fuchsia_display_ControllerAllocateVmoOrdinal; 234 alloc_msg.size = size; 235 236 fuchsia_display_ControllerAllocateVmoResponse alloc_rsp; 237 zx_channel_call_args_t call_args = {}; 238 call_args.wr_bytes = &alloc_msg; 239 call_args.rd_bytes = &alloc_rsp; 240 call_args.rd_handles = vmo_out; 241 call_args.wr_num_bytes = sizeof(alloc_msg); 242 call_args.rd_num_bytes = sizeof(alloc_rsp); 243 call_args.rd_num_handles = 1; 244 uint32_t actual_bytes, actual_handles; 245 zx_status_t status; 246 if ((status = zx_channel_call(dc_ph.handle, 0, ZX_TIME_INFINITE, &call_args, 247 &actual_bytes, &actual_handles)) != ZX_OK) { 248 printf("vc: Failed to alloc vmo: %d (%s)\n", status, zx_status_get_string(status)); 249 return status; 250 } 251 if (alloc_rsp.res != ZX_OK) { 252 printf("vc: Failed to alloc vmo %d\n", alloc_rsp.res); 253 return alloc_rsp.res; 254 } 255 return actual_handles == 1 ? ZX_OK : ZX_ERR_INTERNAL; 256} 257 258static zx_status_t import_vmo(zx_handle_t vmo, fuchsia_display_ImageConfig* config, uint64_t* id) { 259 zx_handle_t vmo_dup; 260 zx_status_t status; 261 if ((status = zx_handle_duplicate(vmo, ZX_RIGHT_SAME_RIGHTS, &vmo_dup)) != ZX_OK) { 262 printf("vc: Failed to dup fb handle %d\n", status); 263 return status; 264 } 265 266 fuchsia_display_ControllerImportVmoImageRequest import_msg = {}; 267 import_msg.hdr.ordinal = fuchsia_display_ControllerImportVmoImageOrdinal; 268 import_msg.image_config = *config; 269 import_msg.vmo = FIDL_HANDLE_PRESENT; 270 import_msg.offset = 0; 271 272 fuchsia_display_ControllerImportVmoImageResponse import_rsp; 273 zx_channel_call_args_t call_args = {}; 274 call_args.wr_bytes = &import_msg; 275 call_args.wr_handles = &vmo_dup; 276 call_args.rd_bytes = &import_rsp; 277 call_args.wr_num_bytes = sizeof(import_msg); 278 call_args.wr_num_handles = 1; 279 call_args.rd_num_bytes = sizeof(import_rsp); 280 uint32_t actual_bytes, actual_handles; 281 if ((status = zx_channel_call(dc_ph.handle, 0, ZX_TIME_INFINITE, &call_args, 282 &actual_bytes, &actual_handles)) != ZX_OK) { 283 printf("vc: Failed to import vmo call %d (%s)\n", status, zx_status_get_string(status)); 284 return status; 285 } 286 287 if (import_rsp.res != ZX_OK) { 288 printf("vc: Failed to import vmo %d\n", import_rsp.res); 289 return import_rsp.res; 290 } 291 292 *id = import_rsp.image_id; 293 return ZX_OK; 294} 295 296static zx_status_t set_display_layer(uint64_t display_id, uint64_t layer_id) { 297 zx_status_t status; 298 // Put the layer on the display 299 uint8_t fidl_bytes[sizeof(fuchsia_display_ControllerSetDisplayLayersRequest) 300 + FIDL_ALIGN(sizeof(uint64_t))]; 301 auto set_display_layer_request = 302 reinterpret_cast<fuchsia_display_ControllerSetDisplayLayersRequest*>(fidl_bytes); 303 304 set_display_layer_request->hdr.ordinal = fuchsia_display_ControllerSetDisplayLayersOrdinal; 305 set_display_layer_request->display_id = display_id; 306 set_display_layer_request->layer_ids.data = reinterpret_cast<void*>(FIDL_ALLOC_PRESENT); 307 308 uint32_t size; 309 if (layer_id) { 310 set_display_layer_request->layer_ids.count = 1; 311 *reinterpret_cast<uint64_t*>(set_display_layer_request + 1) = layer_id; 312 size = sizeof(fidl_bytes); 313 } else { 314 set_display_layer_request->layer_ids.count = 0; 315 size = sizeof(fuchsia_display_ControllerSetDisplayLayersRequest); 316 } 317 if ((status = zx_channel_write(dc_ph.handle, 0, 318 fidl_bytes, size, nullptr, 0)) != ZX_OK) { 319 printf("vc: Failed to set display layers %d\n", status); 320 return status; 321 } 322 323 return ZX_OK; 324} 325 326static zx_status_t configure_layer(display_info_t* display, uint64_t layer_id, 327 uint64_t image_id, fuchsia_display_ImageConfig* config) { 328 zx_status_t status; 329 fuchsia_display_ControllerSetLayerPrimaryConfigRequest layer_cfg_msg; 330 layer_cfg_msg.hdr.ordinal = fuchsia_display_ControllerSetLayerPrimaryConfigOrdinal; 331 layer_cfg_msg.layer_id = layer_id; 332 layer_cfg_msg.image_config = *config; 333 if ((status = zx_channel_write(dc_ph.handle, 0, &layer_cfg_msg, 334 sizeof(layer_cfg_msg), nullptr, 0)) != ZX_OK) { 335 printf("vc: Failed to set layer config %d\n", status); 336 return status; 337 } 338 339 fuchsia_display_ControllerSetLayerPrimaryPositionRequest layer_pos_msg = {}; 340 layer_pos_msg.hdr.ordinal = fuchsia_display_ControllerSetLayerPrimaryPositionOrdinal; 341 layer_pos_msg.layer_id = layer_id; 342 layer_pos_msg.transform = fuchsia_display_Transform_IDENTITY; 343 layer_pos_msg.src_frame.width = config->width; 344 layer_pos_msg.src_frame.height = config->height; 345 layer_pos_msg.dest_frame.width = display->width; 346 layer_pos_msg.dest_frame.height = display->height; 347 if ((status = zx_channel_write(dc_ph.handle, 0, &layer_pos_msg, 348 sizeof(layer_pos_msg), nullptr, 0)) != ZX_OK) { 349 printf("vc: Failed to set layer position %d\n", status); 350 return status; 351 } 352 353 fuchsia_display_ControllerSetLayerImageRequest set_msg; 354 set_msg.hdr.ordinal = fuchsia_display_ControllerSetLayerImageOrdinal; 355 set_msg.layer_id = layer_id; 356 set_msg.image_id = image_id; 357 if ((status = zx_channel_write(dc_ph.handle, 0, 358 &set_msg, sizeof(set_msg), nullptr, 0)) != ZX_OK) { 359 printf("vc: Failed to set image %d\n", status); 360 return status; 361 } 362 return ZX_OK; 363} 364 365static zx_status_t apply_configuration() { 366 // Validate and then apply the new configuration 367 zx_status_t status; 368 fuchsia_display_ControllerCheckConfigRequest check_msg; 369 uint8_t check_rsp_bytes[ZX_CHANNEL_MAX_MSG_BYTES]; 370 auto check_rsp = 371 reinterpret_cast<fuchsia_display_ControllerCheckConfigResponse*>(check_rsp_bytes); 372 check_msg.discard = false; 373 check_msg.hdr.ordinal = fuchsia_display_ControllerCheckConfigOrdinal; 374 zx_channel_call_args_t call_args = {}; 375 call_args.wr_bytes = &check_msg; 376 call_args.rd_bytes = check_rsp; 377 call_args.wr_num_bytes = sizeof(check_msg); 378 call_args.rd_num_bytes = sizeof(check_rsp_bytes); 379 uint32_t actual_bytes, actual_handles; 380 if ((status = zx_channel_call(dc_ph.handle, 0, ZX_TIME_INFINITE, &call_args, 381 &actual_bytes, &actual_handles)) != ZX_OK) { 382 printf("vc: Failed to validate display config: %d (%s)\n", status, 383 zx_status_get_string(status)); 384 return status; 385 } 386 387 if (check_rsp->res != fuchsia_display_ConfigResult_OK) { 388 printf("vc: Config not valid %d\n", check_rsp->res); 389 return ZX_ERR_INTERNAL; 390 } 391 392 fuchsia_display_ControllerApplyConfigRequest apply_msg; 393 apply_msg.hdr.ordinal = fuchsia_display_ControllerApplyConfigOrdinal; 394 if ((status = zx_channel_write(dc_ph.handle, 0, 395 &apply_msg, sizeof(apply_msg), nullptr, 0)) != ZX_OK) { 396 printf("vc: Applying config failed %d\n", status); 397 return status; 398 } 399 400 return ZX_OK; 401} 402 403static zx_status_t rebind_display(bool use_all) { 404 // Arbitrarily pick the oldest display as the primary dispay 405 display_info* primary = list_peek_head_type(&display_list, display_info, node); 406 if (primary == nullptr) { 407 printf("vc: No display to bind to\n"); 408 return ZX_ERR_NO_RESOURCES; 409 } 410 411 zx_status_t status; 412 if (!displays_bound) { 413 uint32_t size = primary->stride * primary->height * ZX_PIXEL_FORMAT_BYTES(primary->format); 414 if ((status = allocate_vmo(size, &image_vmo)) != ZX_OK) { 415 return ZX_ERR_NO_MEMORY; 416 } 417 image_config.height = primary->height; 418 image_config.width = primary->width; 419 image_config.pixel_format = primary->format; 420 image_config.type = IMAGE_TYPE_SIMPLE; 421 422 if ((status = vc_init_gfx(image_vmo, primary->width, primary->height, 423 primary->format, primary->stride)) != ZX_OK) { 424 printf("vc: failed to initialize graphics for new display %d\n", status); 425 zx_handle_close(image_vmo); 426 return status; 427 } 428 } 429 430 display_info_t* info = nullptr; 431 list_for_every_entry(&display_list, info, display_info_t, node) { 432 if (!use_all && info != primary) { 433 // If we're not showing anything on this display, remove its layer 434 if ((status = set_display_layer(info->id, 0)) != ZX_OK) { 435 break; 436 } 437 } else if (info->image_id == 0) { 438 // If we want to display something but aren't, configure the display 439 if ((status = import_vmo(image_vmo, &image_config, &info->image_id)) != ZX_OK) { 440 break; 441 } 442 443 if ((status = set_display_layer(info->id, info->layer_id)) != ZX_OK) { 444 break; 445 } 446 447 if ((status = configure_layer(info, info->layer_id, 448 info->image_id, &image_config) != ZX_OK)) { 449 break; 450 } 451 } 452 } 453 454 if (status == ZX_OK && apply_configuration() == ZX_OK) { 455 // Only listen for logs when we have somewhere to print them. Also, 456 // use a repeating wait so that we don't add/remove observers for each 457 // log message (which is helpful when tracing the addition/removal of 458 // observers). 459 set_log_listener_active(true); 460 vc_show_active(); 461 462 printf("vc: Successfully attached to display %ld\n", primary->id); 463 displays_bound = true; 464 return ZX_OK; 465 } else { 466 display_info_t* info = nullptr; 467 list_for_every_entry(&display_list, info, display_info_t, node) { 468 if (info->image_id) { 469 release_image(info->image_id); 470 info->image_id = 0; 471 } 472 } 473 474 vc_free_gfx(); 475 476 if (use_all) { 477 return rebind_display(false); 478 } else { 479 printf("vc: Failed to bind to displays\n"); 480 return ZX_ERR_INTERNAL; 481 } 482 } 483} 484 485static zx_status_t handle_display_changed(fuchsia_display_ControllerDisplaysChangedEvent* evt) { 486 for (unsigned i = 0; i < evt->added.count; i++) { 487 fuchsia_display_Info* info = reinterpret_cast<fuchsia_display_Info*>(evt->added.data) + i; 488 fuchsia_display_Mode* mode = reinterpret_cast<fuchsia_display_Mode*>(info->modes.data); 489 int32_t pixel_format = reinterpret_cast<int32_t*>(info->pixel_format.data)[0]; 490 zx_status_t status = handle_display_added(info, mode, pixel_format); 491 if (status != ZX_OK) { 492 return status; 493 } 494 } 495 496 for (unsigned i = 0; i < evt->removed.count; i++) { 497 handle_display_removed(reinterpret_cast<int32_t*>(evt->removed.data)[i]); 498 } 499 500 return rebind_display(true); 501} 502 503static zx_status_t dc_callback_handler(port_handler_t* ph, zx_signals_t signals, uint32_t evt) { 504 if (signals & ZX_CHANNEL_PEER_CLOSED) { 505 printf("vc: Displays lost\n"); 506 while (!list_is_empty(&display_list)) { 507 handle_display_removed(list_peek_head_type(&display_list, display_info_t, node)->id); 508 } 509 510 close(dc_fd); 511 zx_handle_close(dc_ph.handle); 512 return ZX_ERR_STOP; 513 } 514 ZX_DEBUG_ASSERT(signals & ZX_CHANNEL_READABLE); 515 516 zx_status_t status; 517 uint32_t actual_bytes, actual_handles; 518 uint8_t fidl_buffer[ZX_CHANNEL_MAX_MSG_BYTES]; 519 if ((status = zx_channel_read(dc_ph.handle, 0, 520 fidl_buffer, nullptr, ZX_CHANNEL_MAX_MSG_BYTES, 0, 521 &actual_bytes, &actual_handles)) != ZX_OK) { 522 printf("vc: Error reading display message %d\n", status); 523 return ZX_OK; 524 } 525 526 if (decode_message(fidl_buffer, actual_bytes) != ZX_OK) { 527 return ZX_OK; 528 } 529 530 fidl_message_header_t* header = (fidl_message_header_t*) fidl_buffer; 531 switch (header->ordinal) { 532 case fuchsia_display_ControllerDisplaysChangedOrdinal: { 533 handle_display_changed( 534 reinterpret_cast<fuchsia_display_ControllerDisplaysChangedEvent*>(fidl_buffer)); 535 break; 536 } 537 case fuchsia_display_ControllerClientOwnershipChangeOrdinal: { 538 auto evt = reinterpret_cast<fuchsia_display_ControllerClientOwnershipChangeEvent*>( 539 fidl_buffer); 540 handle_ownership_change(evt); 541 break; 542 } 543 default: 544 printf("vc: Unknown display callback message %d\n", header->ordinal); 545 break; 546 } 547 548 return ZX_OK; 549} 550 551bool vc_display_init() { 552 for (;;) { 553 if ((dc_fd= open("/dev/class/display-controller/000/virtcon", O_RDWR)) >= 0) { 554 break; 555 } 556 usleep(100000); 557 } 558 559 if (ioctl_display_controller_get_handle(dc_fd, &dc_ph.handle) != sizeof(zx_handle_t)) { 560 printf("vc: failed to get display controller handle\n"); 561 return false; 562 } 563 564 zx_status_t status = vc_set_mode(getenv("virtcon.hide-on-boot") == nullptr ? 565 fuchsia_display_VirtconMode_FALLBACK : fuchsia_display_VirtconMode_INACTIVE); 566 if (status != ZX_OK) { 567 printf("vc: Failed to set initial ownership %d\n", status); 568 return false; 569 } 570 571 dc_ph.waitfor = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED; 572 dc_ph.func = dc_callback_handler; 573 if ((status = port_wait(&port, &dc_ph)) != ZX_OK) { 574 printf("vc: Failed to set port waiter %d\n", status); 575 return false; 576 } 577 578 return true; 579} 580