| // Copyright 2016 The Chromium OS 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 "userspace_touchpad/uinputdevice.h" |
| |
| UinputDevice::~UinputDevice() { |
| // Tell the OD to destroy the uinput device as this object is destructed |
| if (uinput_fd_ >= 0) { |
| ioctl(uinput_fd_, UI_DEV_DESTROY); |
| } |
| } |
| |
| bool UinputDevice::CreateUinputFD() { |
| // Open a control file descriptor for creating a new uinput device. |
| // This file discriptor is used with ioctls to configure the device and |
| // receive the outgoing event information. |
| if (uinput_fd_ >= 0) { |
| printf("Control FD already opened! Aborting...\n"); |
| return false; |
| } |
| |
| uinput_fd_ = syscall_handler_->open(UINPUT_CONTROL_FILENAME, |
| O_WRONLY | O_NONBLOCK); |
| if (uinput_fd_ < 0) { |
| printf("Unable to open %s. (%d)\n", UINPUT_CONTROL_FILENAME, uinput_fd_); |
| return false; |
| } |
| return true; |
| } |
| |
| bool UinputDevice::EnableEventType(int ev_type) const { |
| // Tell the kernel that this uinput device will report events of a |
| // certain type (ABS, KEY, etc). Individual event codes must still be |
| // enabled individually, but their overarching types need to be enabled |
| // first, which is done here. |
| int error = syscall_handler_->ioctl(uinput_fd_, UI_SET_EVBIT, ev_type); |
| if (error) { |
| printf("Error: Unable to enable event type 0x%02X. (%d)\n", ev_type, error); |
| return false; |
| } |
| printf("Enabled events of type 0x%02X.\n", ev_type); |
| return true; |
| } |
| |
| bool UinputDevice::EnableKeyEvent(int ev_code) const { |
| // Tell the kernel that this region's uinput device will report a specific |
| // key event. (eg: KEY_BACKSPACE or BTN_TOUCH) |
| int error = syscall_handler_->ioctl(uinput_fd_, UI_SET_KEYBIT, ev_code); |
| if (error) { |
| printf("Error: Unable to enable EV_KEY 0x%02X events. (%d)\n", |
| ev_code, error); |
| return false; |
| } |
| printf("Enabled EV_KEY 0x%02X event.\n", ev_code); |
| return true; |
| } |
| |
| bool UinputDevice::EnableAbsEvent(int ev_code) const { |
| // Tell the kernel that this region's uinput device will report a specific |
| // kind of ABS event. (eg: ABS_MT_POSITION_X or ABS_PRESSURE) |
| int error = syscall_handler_->ioctl(uinput_fd_, UI_SET_ABSBIT, ev_code); |
| if (error) { |
| printf("Error: Unable to enable EV_ABS 0x%02X events. (%d)\n", |
| ev_code, error); |
| return false; |
| } |
| printf("Enabled EV_ABS 0x%02X event.\n", ev_code); |
| return true; |
| } |
| |
| bool UinputDevice::CopyABSOutputEvents(int source_evdev_fd, |
| int width, int height) const { |
| // Configure this region's uinput device to report the correct kinds of |
| // events by copying the events that are reported by the input device |
| // who's file descriptor is passed as a reference. |
| // Instead of copying the range of the absolute axes though, the user |
| // specifies the width and height manually -- essentially creating a |
| // cloned input device with a different size than the source device. |
| int ev_code, error; |
| struct uinput_abs_setup abs_setup; |
| int64_t supported_abs_event_codes[NBITS(KEY_MAX)]; |
| int64_t supported_event_types[EV_MAX]; |
| |
| // Query the source evdev filedescriptor to see which event types it |
| // supports to make sure it supports ABS. |
| memset(supported_event_types, 0, sizeof(supported_event_types)); |
| ioctl(source_evdev_fd, EVIOCGBIT(0, EV_MAX), supported_event_types); |
| if (!test_bit(EV_ABS, supported_event_types)) { |
| printf("ERROR: Touchscreen does not support EV_ABS events.\n"); |
| return false; |
| } |
| |
| // Enable the EV_ABS event type for this device. Fail if it can't |
| if (!EnableEventType(EV_ABS)) { |
| return false; |
| } |
| |
| // Query the device to find which ABS event codes are supported and then |
| // enable them for this uinput device as well. |
| memset(supported_abs_event_codes, 0, sizeof(supported_abs_event_codes)); |
| ioctl(source_evdev_fd, EVIOCGBIT(EV_ABS, KEY_MAX), supported_abs_event_codes); |
| for (ev_code = 0; ev_code < KEY_MAX; ev_code++) { |
| // Skip over any event codes that're unsupported |
| if (!test_bit(ev_code, supported_abs_event_codes)) { |
| continue; |
| } |
| |
| // Enable this event code for the uinput device |
| if (!EnableAbsEvent(ev_code)) { |
| return false; |
| } |
| |
| // Fill in the ranges for each EV_ABS axis, modifying them for X and Y |
| memset(&abs_setup, 0, sizeof(abs_setup)); |
| abs_setup.code = ev_code; |
| ioctl(source_evdev_fd, EVIOCGABS(ev_code), &abs_setup.absinfo); |
| if (ev_code == ABS_MT_POSITION_X || ev_code == ABS_X) { |
| abs_setup.absinfo.minimum = 0; |
| abs_setup.absinfo.maximum = width; |
| } else if (ev_code == ABS_MT_POSITION_Y || ev_code == ABS_Y) { |
| abs_setup.absinfo.minimum = 0; |
| abs_setup.absinfo.maximum = height; |
| } |
| error = ioctl(uinput_fd_, UI_ABS_SETUP, &abs_setup); |
| if (error) { |
| printf("ERROR: Unable to setup axis for event code 0x%02x. (%d)\n", |
| ev_code, error); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool UinputDevice::FinalizeUinputCreation( |
| std::string const &device_name) const { |
| int error; |
| struct uinput_setup device_info; |
| |
| // Build a uinput device struct and write it to the ui_fd to specify the |
| // various identification parameters required such as the device name. |
| memset(&device_info, 0, sizeof(device_info)); |
| snprintf(device_info.name, UINPUT_MAX_NAME_SIZE, "%s", device_name.c_str()); |
| device_info.id.bustype = BUS_USB; |
| device_info.id.vendor = GOOGLE_VENDOR_ID; |
| device_info.id.product = DUMMY_PRODUCT_ID; |
| device_info.id.version = VERSION_NUMBER; |
| error = syscall_handler_->ioctl(uinput_fd_, UI_DEV_SETUP, &device_info); |
| if (error) { |
| printf("Error on uinput device setup ioctl. (%d)\n", error); |
| return false; |
| } |
| |
| // Finally request that a new uinput device is created to those specs. |
| // After this step the device should be fully functional and ready to |
| // send events. |
| error = syscall_handler_->ioctl(uinput_fd_, UI_DEV_CREATE); |
| if (error) { |
| printf("Error on uinput device creation ioctl. (%d)\n", error); |
| return false; |
| } |
| return true; |
| } |
| |
| bool UinputDevice::SendEvent(int ev_type, int ev_code, int value) const { |
| // Send an input event to the kernel through this uinput device. |
| struct input_event ev; |
| ev.type = ev_type; |
| ev.code = ev_code; |
| ev.value = value; |
| |
| int bytes_written = |
| syscall_handler_->write(uinput_fd_, &ev, sizeof(struct input_event)); |
| if (bytes_written != sizeof(struct input_event)) { |
| printf("ERROR: Failed during write() when sending an event. (%d)\n", |
| bytes_written); |
| return false; |
| } |
| return true; |
| } |