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