blob: 3f4361eb5d0b2f006a015d805d01ec4248234c92 [file] [log] [blame]
// 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 "src/virtualization/bin/vmm/controller/virtio_input.h"
#include <lib/sys/cpp/service_directory.h>
#include <lib/syslog/cpp/macros.h>
#include "src/virtualization/bin/vmm/device/input.h"
static constexpr char kVirtioInputUrl[] =
"fuchsia-pkg://fuchsia.com/virtio_input#meta/virtio_input.cmx";
static constexpr char kDeviceName[] = "machina-input";
static_assert(sizeof(kDeviceName) - 1 < sizeof(virtio_input_config_t::u),
"Device name is too long");
static constexpr char kDeviceSerial[] = "serial-number";
static_assert(sizeof(kDeviceSerial) - 1 < sizeof(virtio_input_config_t::u),
"Device serial is too long");
// Make sure to report only these event codes from keyboard.
// Reporting other keycodes may cause guest OS to recognize keyboard as
// touchpad, stylus or joystick.
static constexpr uint32_t kATKeyboardFirstCode = 0;
static constexpr uint32_t kATKeyboardLastCode = 255;
static constexpr uint32_t kMediaKeyboardFirstCode = 0x160;
static constexpr uint32_t kMediaKeyboardLastCode = 0x2bf;
static_assert(kATKeyboardFirstCode % 8 == 0, "First scan code must be byte aligned.");
static_assert((kATKeyboardLastCode + 1 - kATKeyboardFirstCode) % 8 == 0,
"Scan code range must be byte aligned.");
static_assert(kMediaKeyboardFirstCode % 8 == 0, "First scan code must be byte aligned.");
static_assert((kMediaKeyboardLastCode + 1 - kMediaKeyboardFirstCode) % 8 == 0,
"Scan code range must be byte aligned.");
static_assert((kATKeyboardLastCode + 7) / 8 < sizeof(virtio_input_config_t().u.bitmap),
"Last scan code cannot exceed allowed range.");
static_assert((kMediaKeyboardLastCode + 7) / 8 < sizeof(virtio_input_config_t().u.bitmap),
"Last scan code cannot exceed allowed range.");
static void set_config_bit(uint8_t* bitmap, uint32_t event_code) {
bitmap[event_code / 8] |= 1u << (event_code % 8);
}
uint8_t VirtioInput::Keyboard(uint8_t subsel, uint8_t* bitmap) {
if (subsel != VIRTIO_INPUT_EV_KEY) {
return 0;
}
memset(&bitmap[kATKeyboardFirstCode / 8], 0xff,
(kATKeyboardLastCode + 1 - kATKeyboardFirstCode) / 8);
memset(&bitmap[kMediaKeyboardFirstCode / 8], 0xff,
(kMediaKeyboardLastCode + 1 - kMediaKeyboardFirstCode) / 8);
set_config_bit(bitmap, kButtonTouchCode);
return sizeof(virtio_input_config::u);
}
uint8_t VirtioInput::Pointer(uint8_t subsel, uint8_t* bitmap) {
if (subsel != VIRTIO_INPUT_EV_ABS) {
return 0;
}
set_config_bit(bitmap, VIRTIO_INPUT_EV_ABS_X);
set_config_bit(bitmap, VIRTIO_INPUT_EV_ABS_Y);
return 1;
}
VirtioInput::VirtioInput(const PhysMem& phys_mem, VirtioInputType type)
: VirtioComponentDevice(phys_mem, 0 /* device_features */,
fit::bind_member(this, &VirtioInput::ConfigureQueue),
fit::bind_member(this, &VirtioInput::ConfigureDevice),
fit::bind_member(this, &VirtioInput::Ready)),
type_(type) {}
zx_status_t VirtioInput::Start(const zx::guest& guest, fuchsia::sys::Launcher* launcher,
async_dispatcher_t* dispatcher) {
fuchsia::sys::LaunchInfo launch_info;
launch_info.url = kVirtioInputUrl;
services_ = sys::ServiceDirectory::CreateWithRequest(&launch_info.directory_request);
launcher->CreateComponent(std::move(launch_info), controller_.NewRequest());
services_->Connect(input_.NewRequest());
fuchsia::virtualization::hardware::StartInfo start_info;
zx_status_t status = PrepStart(guest, dispatcher, &start_info);
if (status != ZX_OK) {
return status;
}
return input_->Start(std::move(start_info));
}
zx_status_t VirtioInput::ConfigureQueue(uint16_t queue, uint16_t size, zx_gpaddr_t desc,
zx_gpaddr_t avail, zx_gpaddr_t used) {
return input_->ConfigureQueue(queue, size, desc, avail, used);
}
zx_status_t VirtioInput::Ready(uint32_t negotiated_features) {
return input_->Ready(negotiated_features);
}
static void configure_abs_info(virtio_input_config_t* config) {
memset(&config->u, 0, sizeof(config->u));
switch (config->subsel) {
case VIRTIO_INPUT_EV_ABS_X:
config->u.abs.min = 0;
config->u.abs.max = kInputAbsMaxX;
config->size = sizeof(config->u.abs);
break;
case VIRTIO_INPUT_EV_ABS_Y:
config->u.abs.min = 0;
config->u.abs.max = kInputAbsMaxY;
config->size = sizeof(config->u.abs);
break;
default:
config->size = 0;
break;
}
}
zx_status_t VirtioInput::ConfigureDevice(uint64_t addr, const IoValue& value) {
if (addr >= 2) {
return ZX_OK;
}
// A write to select or subselect modifies the contents of the config.u field.
std::lock_guard<std::mutex> lock(device_config_.mutex);
switch (config_.select) {
case VIRTIO_INPUT_CFG_EV_BITS:
// VIRTIO_INPUT_CFG_EV_BITS: subsel specifies the event type (EV_*).
// If size is non-zero the event type is supported and a bitmap the of
// supported event codes is returned in u.bitmap.
memset(&config_.u, 0, sizeof(config_.u));
config_.size = type_(config_.subsel, config_.u.bitmap);
return ZX_OK;
case VIRTIO_INPUT_CFG_ABS_INFO:
configure_abs_info(&config_);
return ZX_OK;
case VIRTIO_INPUT_CFG_ID_NAME:
// From virtio-input spec, Section 5.7.4, Device configuration layout:
// Strings do not include a terminating NUL byte.
config_.size = sizeof(kDeviceName) - 1;
memcpy(config_.u.string, kDeviceName, config_.size);
return ZX_OK;
case VIRTIO_INPUT_CFG_ID_SERIAL:
config_.size = sizeof(kDeviceSerial) - 1;
memcpy(config_.u.string, kDeviceSerial, config_.size);
return ZX_OK;
case VIRTIO_INPUT_CFG_UNSET:
case VIRTIO_INPUT_CFG_ID_DEVIDS:
case VIRTIO_INPUT_CFG_PROP_BITS:
memset(&config_.u, 0, sizeof(config_.u));
config_.size = 0;
return ZX_OK;
default:
FX_LOGS(ERROR) << "Unsupported select value " << config_.select;
return ZX_ERR_NOT_SUPPORTED;
}
}