// Copyright 2017 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 #include #include #include #include #include #include #include #include #include #include #include #include "keyboard.h" #define LOW_REPEAT_KEY_FREQ 250000000 #define HIGH_REPEAT_KEY_FREQ 50000000 static int modifiers_from_keycode(uint8_t keycode) { switch (keycode) { case HID_USAGE_KEY_LEFT_SHIFT: return MOD_LSHIFT; case HID_USAGE_KEY_RIGHT_SHIFT: return MOD_RSHIFT; case HID_USAGE_KEY_LEFT_ALT: return MOD_LALT; case HID_USAGE_KEY_RIGHT_ALT: return MOD_RALT; case HID_USAGE_KEY_LEFT_CTRL: return MOD_LCTRL; case HID_USAGE_KEY_RIGHT_CTRL: return MOD_RCTRL; } return 0; } static void set_caps_lock_led(int keyboard_fd, bool caps_lock) { // The following bit to set is specified in "Device Class Definition // for Human Interface Devices (HID)", Version 1.11, // http://www.usb.org/developers/hidpage/HID1_11.pdf. Zircon leaves // USB keyboards in boot mode, so the relevant section is Appendix B, // "Boot Interface Descriptors", "B.1 Protocol 1 (Keyboard)". const uint8_t kUsbCapsLockBit = 1 << 1; const uint8_t report_body[1] = { static_cast(caps_lock ? kUsbCapsLockBit : 0) }; // Temporarily wrap keyboard_fd, we will release it after the call so we don't close it. fzl::FdioCaller caller{fbl::unique_fd(keyboard_fd)}; zx_status_t call_status; zx_status_t status = fuchsia_hardware_input_DeviceSetReport( caller.borrow_channel(), fuchsia_hardware_input_ReportType_OUTPUT, 0, report_body, sizeof(report_body), &call_status); caller.release().release(); if (status != ZX_OK || call_status != ZX_OK) { #if !BUILD_FOR_TEST printf("fuchsia.hardware.input.Device.SetReport() failed (returned %d, %d)\n", status, call_status); #endif } } struct vc_input { port_fd_handler_t fh; port_handler_t th; zx_handle_t timer; keypress_handler_t handler; int fd; uint8_t previous_report_buf[8]; uint8_t report_buf[8]; hid_keys_t state[2]; int cur_idx; int prev_idx; int modifiers; uint64_t repeat_interval; bool repeat_enabled; }; // returns true if key was pressed and none were released bool vc_input_process(vc_input_t* vi, uint8_t report[8]) { bool do_repeat = false; // process the key uint8_t keycode; hid_keys_t keys; hid_kbd_parse_report(report, &vi->state[vi->cur_idx]); hid_kbd_pressed_keys(&vi->state[vi->prev_idx], &vi->state[vi->cur_idx], &keys); hid_for_every_key(&keys, keycode) { vi->modifiers |= modifiers_from_keycode(keycode); if (keycode == HID_USAGE_KEY_CAPSLOCK) { vi->modifiers ^= MOD_CAPSLOCK; set_caps_lock_led(vi->fd, vi->modifiers & MOD_CAPSLOCK); } vi->handler(keycode, vi->modifiers); do_repeat = true; } hid_kbd_released_keys(&vi->state[vi->prev_idx], &vi->state[vi->cur_idx], &keys); hid_for_every_key(&keys, keycode) { vi->modifiers &= ~modifiers_from_keycode(keycode); do_repeat = false; } // swap key states vi->cur_idx = 1 - vi->cur_idx; vi->prev_idx = 1 - vi->prev_idx; return do_repeat; } #if !BUILD_FOR_TEST static void vc_input_destroy(vc_input_t* vi) { port_cancel(&port, &vi->th); if (vi->fd >= 0) { port_fd_handler_done(&vi->fh); close(vi->fd); } zx_handle_close(vi->timer); free(vi); } static zx_status_t vc_timer_cb(port_handler_t* ph, zx_signals_t signals, uint32_t evt) { vc_input_t* vi = containerof(ph, vc_input_t, th); // if interval is infinite, repeat was canceled if (vi->repeat_interval != ZX_TIME_INFINITE) { vc_input_process(vi, vi->previous_report_buf); vc_input_process(vi, vi->report_buf); // increase repeat rate if we're not yet at the fastest rate if ((vi->repeat_interval = vi->repeat_interval * 3 / 4) < HIGH_REPEAT_KEY_FREQ) { vi->repeat_interval = HIGH_REPEAT_KEY_FREQ; } zx_timer_set(vi->timer, zx_deadline_after(vi->repeat_interval), 0); } // return non-OK to avoid needlessly re-arming the repeating wait return ZX_ERR_NEXT; } static zx_status_t vc_input_cb(port_fd_handler_t* fh, unsigned pollevt, uint32_t evt) { vc_input_t* vi = containerof(fh, vc_input_t, fh); ssize_t r; if (!(pollevt & POLLIN)) { r = ZX_ERR_PEER_CLOSED; } else { memcpy(vi->previous_report_buf, vi->report_buf, sizeof(vi->report_buf)); r = read(vi->fd, vi->report_buf, sizeof(vi->report_buf)); } if (r <= 0) { vc_input_destroy(vi); return ZX_ERR_STOP; } if ((size_t)(r) != sizeof(vi->report_buf)) { vi->repeat_interval = ZX_TIME_INFINITE; return ZX_OK; } if (vc_input_process(vi, vi->report_buf) && vi->repeat_enabled) { vi->repeat_interval = LOW_REPEAT_KEY_FREQ; zx_timer_set(vi->timer, zx_deadline_after(vi->repeat_interval), 0); } else { vi->repeat_interval = ZX_TIME_INFINITE; } return ZX_OK; } #endif zx_status_t vc_input_create(vc_input_t** out, keypress_handler_t handler, int fd) { vc_input_t* vi = reinterpret_cast(calloc(1, sizeof(vc_input_t))); if (vi == NULL) { return ZX_ERR_NO_MEMORY; } vi->fd = fd; vi->handler = handler; vi->cur_idx = 0; vi->prev_idx = 1; vi->modifiers = 0; vi->repeat_interval = ZX_TIME_INFINITE; vi->repeat_enabled = true; char* flag = getenv("virtcon.keyrepeat"); if (flag && (!strcmp(flag, "0") || !strcmp(flag, "false"))) { printf("vc: Key repeat disabled\n"); vi->repeat_enabled = false; } #if !BUILD_FOR_TEST zx_status_t r; if ((r = zx_timer_create(0, ZX_CLOCK_MONOTONIC, &vi->timer)) < 0) { free(vi); return r; } vi->fh.func = vc_input_cb; if ((r = port_fd_handler_init(&vi->fh, fd, POLLIN | POLLHUP | POLLRDHUP)) < 0) { zx_handle_close(vi->timer); free(vi); return r; } if ((r = port_wait(&port, &vi->fh.ph)) < 0) { port_fd_handler_done(&vi->fh); zx_handle_close(vi->timer); free(vi); return r; } vi->th.handle = vi->timer; vi->th.waitfor = ZX_TIMER_SIGNALED; vi->th.func = vc_timer_cb; port_wait_repeating(&port, &vi->th); #endif *out = vi; return ZX_OK; } #if !BUILD_FOR_TEST zx_status_t new_input_device(int fd, keypress_handler_t handler) { // test to see if this is a device we can read uint32_t proto = fuchsia_hardware_input_BootProtocol_NONE; // Temporarily wrap fd, we will release it after the call so we don't close it. fzl::FdioCaller caller{fbl::unique_fd(fd)}; zx_status_t status = fuchsia_hardware_input_DeviceGetBootProtocol(caller.borrow_channel(), &proto); caller.release().release(); if ((status != ZX_OK) || (proto != fuchsia_hardware_input_BootProtocol_KBD)) { // skip devices that aren't keyboards close(fd); return ZX_ERR_NOT_SUPPORTED; } vc_input_t* vi; if ((status = vc_input_create(&vi, handler, fd)) < 0) { close(fd); } return status; } #endif