blob: 09c8eae1d481d5d10baafdc6f8d35b3ffd0bac44 [file] [log] [blame]
// Copyright 2019 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/lib/hid-input-report/keyboard.h"
#include <stdint.h>
#include <fbl/span.h>
#include <hid-parser/parser.h>
#include <hid-parser/report.h>
#include <hid-parser/units.h>
#include <hid-parser/usages.h>
#include <hid/usages.h>
#include "src/ui/input/lib/hid-input-report/device.h"
#include "src/ui/lib/key_util/key_util.h"
namespace hid_input_report {
namespace {
void InsertFuchsiaKey(uint32_t hid_usage, uint32_t hid_key,
std::set<::llcpp::fuchsia::ui::input2::Key>* key_values) {
std::optional<fuchsia::ui::input2::Key> fuchsia_key =
key_util::hid_key_to_fuchsia_key(hid::USAGE(hid_usage, hid_key));
if (fuchsia_key) {
// Cast the key enum from HLCPP to LLCPP. We are guaranteed that this will be
// equivalent.
key_values->insert(static_cast<llcpp::fuchsia::ui::input2::Key>(*fuchsia_key));
}
}
void InsertFuchsiaKey3(uint32_t hid_usage, uint32_t hid_key,
std::set<::llcpp::fuchsia::input::Key>* key_values) {
std::optional<fuchsia::input::Key> fuchsia_key3 =
key_util::hid_key_to_fuchsia_key3(hid::USAGE(hid_usage, hid_key));
if (fuchsia_key3) {
// Cast the key enum from HLCPP to LLCPP. We are guaranteed that this will be
// equivalent.
key_values->insert(static_cast<llcpp::fuchsia::input::Key>(*fuchsia_key3));
}
}
} // namespace
ParseResult Keyboard::ParseInputReportDescriptor(
const hid::ReportDescriptor& hid_report_descriptor) {
// Use a set to make it easy to create a list of sorted and unique keys.
std::set<::llcpp::fuchsia::ui::input2::Key> key_values;
std::set<::llcpp::fuchsia::input::Key> key_3_values;
std::array<hid::ReportField, fuchsia_input_report::KEYBOARD_MAX_NUM_KEYS> key_fields;
size_t num_key_fields = 0;
for (size_t i = 0; i < hid_report_descriptor.input_count; i++) {
const hid::ReportField& field = hid_report_descriptor.input_fields[i];
if (field.attr.usage.page == hid::usage::Page::kKeyboardKeypad) {
if (field.flags & hid::FieldTypeFlags::kArray) {
for (uint8_t key = static_cast<uint8_t>(field.attr.logc_mm.min);
key < static_cast<uint8_t>(field.attr.logc_mm.max); key++) {
InsertFuchsiaKey(field.attr.usage.page, key, &key_values);
InsertFuchsiaKey3(field.attr.usage.page, key, &key_3_values);
}
} else {
InsertFuchsiaKey(field.attr.usage.page, field.attr.usage.usage, &key_values);
InsertFuchsiaKey3(field.attr.usage.page, field.attr.usage.usage, &key_3_values);
}
key_fields[num_key_fields++] = field;
if (num_key_fields == fuchsia_input_report::KEYBOARD_MAX_NUM_KEYS) {
return ParseResult::kTooManyItems;
}
}
}
if (key_values.size() >= fuchsia_input_report::KEYBOARD_MAX_NUM_KEYS) {
return ParseResult::kTooManyItems;
}
// No error, write to class members.
key_values_ = std::move(key_values);
key_3_values_ = std::move(key_3_values);
num_key_fields_ = num_key_fields;
key_fields_ = key_fields;
input_report_size_ = hid_report_descriptor.input_byte_sz;
input_report_id_ = hid_report_descriptor.report_id;
return ParseResult::kOk;
}
ParseResult Keyboard::ParseOutputReportDescriptor(
const hid::ReportDescriptor& hid_report_descriptor) {
std::array<hid::ReportField, ::llcpp::fuchsia::input::report::KEYBOARD_MAX_NUM_LEDS> led_fields;
size_t num_leds = 0;
for (size_t i = 0; i < hid_report_descriptor.output_count; i++) {
const hid::ReportField& field = hid_report_descriptor.output_fields[i];
if (field.attr.usage.page == hid::usage::Page::kLEDs) {
if (num_leds == ::llcpp::fuchsia::input::report::KEYBOARD_MAX_NUM_LEDS) {
return ParseResult::kTooManyItems;
}
led_fields[num_leds++] = field;
}
}
if (num_leds == 0) {
return ParseResult::kOk;
}
// No errors, write to class memebers.
num_leds_ = num_leds;
led_fields_ = led_fields;
output_report_id_ = hid_report_descriptor.report_id;
output_report_size_ = hid_report_descriptor.output_byte_sz;
return ParseResult::kOk;
}
ParseResult Keyboard::ParseReportDescriptor(const hid::ReportDescriptor& hid_report_descriptor) {
ParseResult res = ParseInputReportDescriptor(hid_report_descriptor);
if (res != ParseResult::kOk) {
return res;
}
return ParseOutputReportDescriptor(hid_report_descriptor);
};
ParseResult Keyboard::CreateDescriptor(
fidl::Allocator* allocator, fuchsia_input_report::DeviceDescriptor::Builder* descriptor) {
auto keyboard = fuchsia_input_report::KeyboardDescriptor::Builder(
allocator->make<fuchsia_input_report::KeyboardDescriptor::Frame>());
// Input Descriptor parsing.
if (input_report_size_ > 0) {
auto keyboard_input = fuchsia_input_report::KeyboardInputDescriptor::Builder(
allocator->make<fuchsia_input_report::KeyboardInputDescriptor::Frame>());
size_t keys_index = 0;
auto keys = allocator->make<::llcpp::fuchsia::ui::input2::Key[]>(key_values_.size());
for (auto& key : key_values_) {
keys[keys_index++] = key;
}
size_t keys_3_index = 0;
auto keys_3 = allocator->make<::llcpp::fuchsia::input::Key[]>(key_3_values_.size());
for (auto& key : key_3_values_) {
keys_3[keys_3_index++] = key;
}
keyboard_input.set_keys(allocator->make<fidl::VectorView<::llcpp::fuchsia::ui::input2::Key>>(
std::move(keys), key_values_.size()));
keyboard_input.set_keys3(allocator->make<fidl::VectorView<::llcpp::fuchsia::input::Key>>(
std::move(keys_3), key_3_values_.size()));
keyboard.set_input(
allocator->make<fuchsia_input_report::KeyboardInputDescriptor>(keyboard_input.build()));
}
// Output Descriptor parsing.
if (output_report_size_ > 0) {
auto keyboard_output = fuchsia_input_report::KeyboardOutputDescriptor::Builder(
allocator->make<fuchsia_input_report::KeyboardOutputDescriptor::Frame>());
size_t leds_index = 0;
auto leds = allocator->make<fuchsia_input_report::LedType[]>(num_leds_);
for (hid::ReportField& field : fbl::Span(led_fields_.data(), num_leds_)) {
zx_status_t status = HidLedUsageToLlcppLedType(
static_cast<hid::usage::LEDs>(field.attr.usage.usage), &leds[leds_index++]);
if (status != ZX_OK) {
return ParseResult::kBadReport;
}
}
auto leds_view = allocator->make<fidl::VectorView<fuchsia_input_report::LedType>>(
std::move(leds), num_leds_);
keyboard_output.set_leds(std::move(leds_view));
keyboard.set_output(
allocator->make<fuchsia_input_report::KeyboardOutputDescriptor>(keyboard_output.build()));
}
descriptor->set_keyboard(
allocator->make<fuchsia_input_report::KeyboardDescriptor>(keyboard.build()));
return ParseResult::kOk;
}
ParseResult Keyboard::ParseInputReport(const uint8_t* data, size_t len, fidl::Allocator* allocator,
fuchsia_input_report::InputReport::Builder* report) {
if (len != input_report_size_) {
return ParseResult::kReportSizeMismatch;
}
auto keyboard_report = fuchsia_input_report::KeyboardInputReport::Builder(
allocator->make<fuchsia_input_report::KeyboardInputReport::Frame>());
size_t num_pressed_keys = 0;
size_t num_pressed_keys_3 = 0;
std::array<::llcpp::fuchsia::ui::input2::Key, fuchsia_input_report::KEYBOARD_MAX_NUM_KEYS>
pressed_keys;
std::array<::llcpp::fuchsia::input::Key, fuchsia_input_report::KEYBOARD_MAX_NUM_KEYS>
pressed_keys_3;
for (hid::ReportField& field : fbl::Span(key_fields_.data(), num_key_fields_)) {
double val_out_double;
if (!ExtractAsUnitType(data, len, field.attr, &val_out_double)) {
continue;
}
uint32_t val_out = static_cast<uint32_t>(val_out_double);
if (val_out == 0) {
continue;
}
// Get the HID key.
uint32_t hid_key;
if (field.flags & hid::FieldTypeFlags::kArray) {
if (val_out == HID_USAGE_KEY_ERROR_ROLLOVER) {
return ParseResult::kBadReport;
}
hid_key = val_out;
} else {
hid_key = field.attr.usage.usage;
}
// Convert to fuchsia key.
auto fuchsia_key =
key_util::hid_key_to_fuchsia_key(hid::USAGE(hid::usage::Page::kKeyboardKeypad, hid_key));
if (fuchsia_key) {
// Cast the key enum from HLCPP to LLCPP. We are guaranteed that this will be equivalent.
pressed_keys[num_pressed_keys++] = static_cast<llcpp::fuchsia::ui::input2::Key>(*fuchsia_key);
}
auto fuchsia_key_3 =
key_util::hid_key_to_fuchsia_key3(hid::USAGE(hid::usage::Page::kKeyboardKeypad, hid_key));
if (fuchsia_key_3) {
pressed_keys_3[num_pressed_keys_3++] =
static_cast<llcpp::fuchsia::input::Key>(*fuchsia_key_3);
}
}
auto fidl_pressed_keys = allocator->make<::llcpp::fuchsia::ui::input2::Key[]>(num_pressed_keys);
for (size_t i = 0; i < num_pressed_keys; i++) {
fidl_pressed_keys[i] = pressed_keys[i];
}
auto fidl_pressed_keys_3 = allocator->make<::llcpp::fuchsia::input::Key[]>(num_pressed_keys_3);
for (size_t i = 0; i < num_pressed_keys_3; i++) {
fidl_pressed_keys_3[i] = pressed_keys_3[i];
}
keyboard_report.set_pressed_keys(
allocator->make<fidl::VectorView<::llcpp::fuchsia::ui::input2::Key>>(
std::move(fidl_pressed_keys), num_pressed_keys));
keyboard_report.set_pressed_keys3(allocator->make<fidl::VectorView<::llcpp::fuchsia::input::Key>>(
std::move(fidl_pressed_keys_3), num_pressed_keys_3));
report->set_keyboard(
allocator->make<fuchsia_input_report::KeyboardInputReport>(keyboard_report.build()));
return ParseResult::kOk;
}
ParseResult Keyboard::SetOutputReport(const fuchsia_input_report::OutputReport* report,
uint8_t* data, size_t data_size, size_t* data_out_size) {
if (!report->has_keyboard()) {
return ParseResult::kNotImplemented;
}
if (!report->keyboard().has_enabled_leds()) {
return ParseResult::kNotImplemented;
}
if (data_size < output_report_size_) {
return ParseResult::kNoMemory;
}
for (size_t i = 0; i < data_size; i++) {
data[i] = 0;
}
// Go through each enabled LED and set its report field to enabled.
for (fuchsia_input_report::LedType led : report->keyboard().enabled_leds()) {
bool found = false;
for (size_t i = 0; i < num_leds_; i++) {
hid::ReportField& hid_led = led_fields_[i];
// Convert the usage to LedType.
fuchsia_input_report::LedType hid_led_type;
zx_status_t status = HidLedUsageToLlcppLedType(
static_cast<hid::usage::LEDs>(hid_led.attr.usage.usage), &hid_led_type);
if (status != ZX_OK) {
return ParseResult::kBadReport;
}
if (hid_led_type == led) {
found = true;
if (!InsertAsUnitType(data, data_size, hid_led.attr, 1)) {
return ParseResult::kBadReport;
}
break;
}
}
if (!found) {
return ParseResult::kItemNotFound;
}
}
*data_out_size = output_report_size_;
return ParseResult::kOk;
}
} // namespace hid_input_report