blob: b8cb9fc2b4a19df2e0b6d1ee255dbb2047c3682d [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 "src/ui/input/drivers/virtio/input_kbd.h"
#include <lib/ddk/debug.h>
#include <zircon/status.h>
#include <fbl/algorithm.h>
#include "src/devices/bus/lib/virtio/trace.h"
#define LOCAL_TRACE 0
namespace virtio {
namespace {
// These are Linux input event codes:
// https://github.com/torvalds/linux/blob/HEAD/include/uapi/linux/input-event-codes.h
constexpr std::optional<fuchsia_input::wire::Key> kEventCodeMap[] = {
/* 0x00 */ std::nullopt,
/* 0x01 */ fuchsia_input::wire::Key::kEscape,
/* 0x02 */ fuchsia_input::wire::Key::kKey1,
/* 0x03 */ fuchsia_input::wire::Key::kKey2,
/* 0x04 */ fuchsia_input::wire::Key::kKey3,
/* 0x05 */ fuchsia_input::wire::Key::kKey4,
/* 0x06 */ fuchsia_input::wire::Key::kKey5,
/* 0x07 */ fuchsia_input::wire::Key::kKey6,
/* 0x08 */ fuchsia_input::wire::Key::kKey7,
/* 0x09 */ fuchsia_input::wire::Key::kKey8,
/* 0x0a */ fuchsia_input::wire::Key::kKey9,
/* 0x0b */ fuchsia_input::wire::Key::kKey0,
/* 0x0c */ fuchsia_input::wire::Key::kMinus,
/* 0x0d */ fuchsia_input::wire::Key::kEquals,
/* 0x0e */ fuchsia_input::wire::Key::kBackspace,
/* 0x0f */ fuchsia_input::wire::Key::kTab,
/* 0x10 */ fuchsia_input::wire::Key::kQ,
/* 0x11 */ fuchsia_input::wire::Key::kW,
/* 0x12 */ fuchsia_input::wire::Key::kE,
/* 0x13 */ fuchsia_input::wire::Key::kR,
/* 0x14 */ fuchsia_input::wire::Key::kT,
/* 0x15 */ fuchsia_input::wire::Key::kY,
/* 0x16 */ fuchsia_input::wire::Key::kU,
/* 0x17 */ fuchsia_input::wire::Key::kI,
/* 0x18 */ fuchsia_input::wire::Key::kO,
/* 0x19 */ fuchsia_input::wire::Key::kP,
/* 0x1a */ fuchsia_input::wire::Key::kLeftBrace,
/* 0x1b */ fuchsia_input::wire::Key::kRightBrace,
/* 0x1c */ fuchsia_input::wire::Key::kEnter,
/* 0x1d */ fuchsia_input::wire::Key::kLeftCtrl,
/* 0x1e */ fuchsia_input::wire::Key::kA,
/* 0x1f */ fuchsia_input::wire::Key::kS,
/* 0x20 */ fuchsia_input::wire::Key::kD,
/* 0x21 */ fuchsia_input::wire::Key::kF,
/* 0x22 */ fuchsia_input::wire::Key::kG,
/* 0x23 */ fuchsia_input::wire::Key::kH,
/* 0x24 */ fuchsia_input::wire::Key::kJ,
/* 0x25 */ fuchsia_input::wire::Key::kK,
/* 0x26 */ fuchsia_input::wire::Key::kL,
/* 0x27 */ fuchsia_input::wire::Key::kSemicolon,
/* 0x28 */ fuchsia_input::wire::Key::kApostrophe,
/* 0x29 */ fuchsia_input::wire::Key::kGraveAccent,
/* 0x2a */ fuchsia_input::wire::Key::kLeftShift,
/* 0x2b */ fuchsia_input::wire::Key::kBackslash,
/* 0x2c */ fuchsia_input::wire::Key::kZ,
/* 0x2d */ fuchsia_input::wire::Key::kX,
/* 0x2e */ fuchsia_input::wire::Key::kC,
/* 0x2f */ fuchsia_input::wire::Key::kV,
/* 0x30 */ fuchsia_input::wire::Key::kB,
/* 0x31 */ fuchsia_input::wire::Key::kN,
/* 0x32 */ fuchsia_input::wire::Key::kM,
/* 0x33 */ fuchsia_input::wire::Key::kComma,
/* 0x34 */ fuchsia_input::wire::Key::kDot,
/* 0x35 */ fuchsia_input::wire::Key::kSlash,
/* 0x36 */ fuchsia_input::wire::Key::kRightShift,
/* 0x37 */ fuchsia_input::wire::Key::kKeypadAsterisk,
/* 0x38 */ fuchsia_input::wire::Key::kLeftAlt,
/* 0x39 */ fuchsia_input::wire::Key::kSpace,
/* 0x3a */ fuchsia_input::wire::Key::kCapsLock,
/* 0x3b */ fuchsia_input::wire::Key::kF1,
/* 0x3c */ fuchsia_input::wire::Key::kF2,
/* 0x3d */ fuchsia_input::wire::Key::kF3,
/* 0x3e */ fuchsia_input::wire::Key::kF4,
/* 0x3f */ fuchsia_input::wire::Key::kF5,
/* 0x40 */ fuchsia_input::wire::Key::kF6,
/* 0x41 */ fuchsia_input::wire::Key::kF7,
/* 0x42 */ fuchsia_input::wire::Key::kF8,
/* 0x43 */ fuchsia_input::wire::Key::kF9,
/* 0x44 */ fuchsia_input::wire::Key::kF10,
/* 0x45 */ fuchsia_input::wire::Key::kNumLock,
/* 0x46 */ fuchsia_input::wire::Key::kScrollLock,
/* 0x47 */ fuchsia_input::wire::Key::kKeypad7,
/* 0x48 */ fuchsia_input::wire::Key::kKeypad8,
/* 0x49 */ fuchsia_input::wire::Key::kKeypad9,
/* 0x4a */ fuchsia_input::wire::Key::kKeypadMinus,
/* 0x4b */ fuchsia_input::wire::Key::kKeypad4,
/* 0x4c */ fuchsia_input::wire::Key::kKeypad5,
/* 0x4d */ fuchsia_input::wire::Key::kKeypad6,
/* 0x4e */ fuchsia_input::wire::Key::kKeypadPlus,
/* 0x4f */ fuchsia_input::wire::Key::kKeypad1,
/* 0x50 */ fuchsia_input::wire::Key::kKeypad2,
/* 0x51 */ fuchsia_input::wire::Key::kKeypad3,
/* 0x52 */ fuchsia_input::wire::Key::kKeypad0,
/* 0x53 */ fuchsia_input::wire::Key::kKeypadDot,
/* 0x54 */ std::nullopt,
/* 0x55 */ std::nullopt,
/* 0x56 */ std::nullopt,
/* 0x57 */ std::nullopt,
/* 0x58 */ std::nullopt,
/* 0x59 */ std::nullopt,
/* 0x5a */ std::nullopt,
/* 0x5b */ std::nullopt,
/* 0x5c */ std::nullopt,
/* 0x5d */ std::nullopt,
/* 0x5e */ std::nullopt,
/* 0x5f */ std::nullopt,
/* 0x60 */ std::nullopt,
/* 0x61 */ fuchsia_input::wire::Key::kRightCtrl,
/* 0x62 */ std::nullopt,
/* 0x63 */ std::nullopt,
/* 0x64 */ fuchsia_input::wire::Key::kRightAlt,
/* 0x65 */ std::nullopt,
/* 0x66 */ std::nullopt,
/* 0x67 */ fuchsia_input::wire::Key::kUp,
/* 0x68 */ std::nullopt,
/* 0x69 */ fuchsia_input::wire::Key::kLeft,
/* 0x6a */ fuchsia_input::wire::Key::kRight,
/* 0x6b */ std::nullopt,
/* 0x6c */ fuchsia_input::wire::Key::kDown,
/* 0x6d */ fuchsia_input::wire::Key::kPageDown,
/* 0x6e */ fuchsia_input::wire::Key::kInsert,
/* 0x6f */ fuchsia_input::wire::Key::kDelete,
/* 0x70 */ std::nullopt,
/* 0x71 */ std::nullopt,
/* 0x72 */ std::nullopt,
/* 0x73 */ std::nullopt,
/* 0x74 */ std::nullopt,
/* 0x75 */ std::nullopt,
/* 0x76 */ std::nullopt,
/* 0x77 */ fuchsia_input::wire::Key::kPause,
/* 0x78 */ std::nullopt,
/* 0x79 */ std::nullopt,
/* 0x7a */ std::nullopt,
/* 0x7b */ std::nullopt,
/* 0x7c */ std::nullopt,
/* 0x7d */ fuchsia_input::wire::Key::kLeftMeta,
/* 0x7e */ fuchsia_input::wire::Key::kRightMeta,
};
constexpr size_t kKeyCount = []() {
size_t count = 0;
for (const auto& k : kEventCodeMap) {
if (k.has_value()) {
count++;
}
}
return count;
}();
constexpr std::array<fuchsia_input::wire::Key, kKeyCount> kKeys = []() {
std::array<fuchsia_input::wire::Key, kKeyCount> keys;
size_t i = 0;
for (const auto& k : kEventCodeMap) {
if (k.has_value()) {
keys[i++] = k.value();
}
}
return keys;
}();
} // namespace
void KeyboardReport::ToFidlInputReport(
fidl::WireTableBuilder<::fuchsia_input_report::wire::InputReport>& input_report,
fidl::AnyArena& allocator) const {
fidl::VectorView<fuchsia_input::wire::Key> keys3(allocator, kMaxKeys);
size_t idx = 0;
for (const auto& key : usage) {
if (!key.has_value()) {
break;
}
keys3[idx++] = *key;
}
keys3.set_size(idx);
auto keyboard_report =
fuchsia_input_report::wire::KeyboardInputReport::Builder(allocator).pressed_keys3(keys3);
input_report.event_time(event_time.get()).keyboard(keyboard_report.Build());
}
fuchsia_input_report::wire::DeviceDescriptor HidKeyboard::GetDescriptor(fidl::AnyArena& allocator) {
auto device_info = fuchsia_input_report::wire::DeviceInformation::Builder(allocator);
device_info.vendor_id(static_cast<uint32_t>(fuchsia_input_report::wire::VendorId::kGoogle));
device_info.product_id(
static_cast<uint32_t>(fuchsia_input_report::wire::VendorGoogleProductId::kVirtioKeyboard));
const auto input =
fuchsia_input_report::wire::KeyboardInputDescriptor::Builder(allocator).keys3(kKeys).Build();
const auto output =
fuchsia_input_report::wire::KeyboardOutputDescriptor::Builder(allocator)
.leds({allocator,
{fuchsia_input_report::LedType::kNumLock, fuchsia_input_report::LedType::kCapsLock,
fuchsia_input_report::LedType::kScrollLock,
fuchsia_input_report::LedType::kCompose, fuchsia_input_report::LedType::kKana}})
.Build();
const auto keyboard = fuchsia_input_report::wire::KeyboardDescriptor::Builder(allocator)
.input(input)
.output(output)
.Build();
return fuchsia_input_report::wire::DeviceDescriptor::Builder(allocator)
.device_information(device_info.Build())
.keyboard(keyboard)
.Build();
}
void HidKeyboard::AddKeypressToReport(uint16_t event_code) {
auto hid_code = kEventCodeMap[event_code];
if (!hid_code.has_value()) {
return;
}
for (auto& usage : report_.usage) {
if (!usage.has_value()) {
usage = hid_code;
return;
}
if (*usage == *hid_code) {
// The key already exists in the report so we ignore it.
return;
}
}
// There's no free slot in the report.
// TODO: Record a rollover status.
}
void HidKeyboard::RemoveKeypressFromReport(uint16_t event_code) {
auto hid_code = kEventCodeMap[event_code];
if (!hid_code.has_value()) {
return;
}
int id = -1;
for (int i = 0; i != kMaxKeys; ++i) {
if (report_.usage[i].has_value() && *report_.usage[i] == *hid_code) {
id = i;
break;
}
}
if (id == -1) {
// They key is not in the report so we ignore it.
return;
}
for (size_t i = id; i != kMaxKeys - 1; ++i) {
report_.usage[i] = report_.usage[i + 1];
}
report_.usage[kMaxKeys - 1] = std::nullopt;
}
void HidKeyboard::ReceiveEvent(virtio_input_event_t* event) {
if (event->type != VIRTIO_INPUT_EV_KEY) {
zxlogf(TRACE, "Unsupported event type %d\n", event->type);
return;
}
if (event->code == 0 || event->code >= std::size(kEventCodeMap)) {
zxlogf(TRACE, "Unknown key %d\n", event->code);
return;
}
if (event->value == VIRTIO_INPUT_EV_KEY_PRESSED) {
AddKeypressToReport(event->code);
} else if (event->value == VIRTIO_INPUT_EV_KEY_RELEASED) {
RemoveKeypressFromReport(event->code);
}
}
} // namespace virtio