| // 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 <fcntl.h> |
| #include <poll.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <fuchsia/hardware/input/c/fidl.h> |
| #include <hid/hid.h> |
| #include <hid/usages.h> |
| #include <lib/fzl/fdio.h> |
| #include <zircon/syscalls.h> |
| |
| #include <port/port.h> |
| |
| #include <utility> |
| |
| #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<uint8_t>(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); |
| |
| 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 ZX_OK; |
| } |
| |
| 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 { |
| zx_timer_cancel(vi->timer); |
| } |
| 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<vc_input_t*>(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(ZX_TIMER_SLACK_LATE, 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(&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 |