| // 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."); | |
| VirtioInput::VirtioInput(const PhysMem& phys_mem) | |
| : VirtioComponentDevice(phys_mem, 0 /* device_features */, | |
| fit::bind_member(this, &VirtioInput::ConfigureQueue), | |
| fit::bind_member(this, &VirtioInput::ConfigureDevice), | |
| fit::bind_member(this, &VirtioInput::Ready)) {} | |
| zx_status_t VirtioInput::Start( | |
| const zx::guest& guest, | |
| fidl::InterfaceRequest<fuchsia::virtualization::hardware::ViewListener> view_listener_request, | |
| fuchsia::sys::Launcher* launcher, async_dispatcher_t* dispatcher) { | |
| fuchsia::sys::LaunchInfo launch_info; | |
| launch_info.url = kVirtioInputUrl; | |
| auto services = sys::ServiceDirectory::CreateWithRequest(&launch_info.directory_request); | |
| launcher->CreateComponent(std::move(launch_info), controller_.NewRequest()); | |
| services->Connect(input_.NewRequest()); | |
| services->Connect(std::move(view_listener_request)); | |
| 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 set_config_bit(virtio_input_config_t* config, uint32_t event_code) { | |
| config->u.bitmap[event_code / 8] |= 1u << (event_code % 8); | |
| } | |
| static void configure_ev_bits(virtio_input_config_t* config) { | |
| // 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)); | |
| switch (config->subsel) { | |
| case VIRTIO_INPUT_EV_KEY: | |
| memset(&config->u.bitmap[kATKeyboardFirstCode / 8], 0xff, | |
| (kATKeyboardLastCode + 1 - kATKeyboardFirstCode) / 8); | |
| memset(&config->u.bitmap[kMediaKeyboardFirstCode / 8], 0xff, | |
| (kMediaKeyboardLastCode + 1 - kMediaKeyboardFirstCode) / 8); | |
| set_config_bit(config, kButtonTouchCode); | |
| config->size = sizeof(config->u); | |
| break; | |
| case VIRTIO_INPUT_EV_ABS: | |
| set_config_bit(config, VIRTIO_INPUT_EV_ABS_X); | |
| set_config_bit(config, VIRTIO_INPUT_EV_ABS_Y); | |
| config->size = 1; | |
| break; | |
| default: | |
| config->size = 0; | |
| break; | |
| } | |
| } | |
| 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: | |
| configure_ev_bits(&config_); | |
| 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; | |
| } | |
| } |