| // 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 "garnet/lib/machina/virtio_input.h" |
| |
| #include <lib/fxl/logging.h> |
| #include <lib/svc/cpp/services.h> |
| |
| #include "garnet/lib/machina/device/input.h" |
| |
| namespace machina { |
| |
| 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::ui::input::InputListener> |
| input_listener_request, |
| fidl::InterfaceRequest<fuchsia::guest::device::ViewListener> |
| view_listener_request, |
| fuchsia::sys::Launcher* launcher, async_dispatcher_t* dispatcher) { |
| component::Services services; |
| fuchsia::sys::LaunchInfo launch_info{ |
| .url = kVirtioInputUrl, |
| .directory_request = services.NewRequest(), |
| }; |
| launcher->CreateComponent(std::move(launch_info), controller_.NewRequest()); |
| services.ConnectToService(input_.NewRequest()); |
| services.ConnectToService(std::move(input_listener_request)); |
| services.ConnectToService(std::move(view_listener_request)); |
| |
| fuchsia::guest::device::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 = machina::kInputAbsMaxX; |
| config->size = sizeof(config->u.abs); |
| break; |
| case VIRTIO_INPUT_EV_ABS_Y: |
| config->u.abs.min = 0; |
| config->u.abs.max = machina::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: |
| FXL_LOG(ERROR) << "Unsupported select value " << config_.select; |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| } |
| |
| } // namespace machina |