| // Copyright 2014 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 "power_manager/powerd/system/event_device.h" |
| |
| #include <fcntl.h> |
| #include <linux/input.h> |
| |
| #include <base/check.h> |
| #include <base/posix/eintr_wrapper.h> |
| |
| #include "power_manager/common/power_constants.h" |
| |
| // Helper macros for accessing the bitfields returned by the kernel interface, |
| // compare with include/linux/bitops.h. |
| #define BITS_PER_LONG (sizeof(long) * 8) // NOLINT(runtime/int) |
| #define BITS_TO_LONGS(bits) (((bits)-1) / BITS_PER_LONG + 1) |
| #define BITMASK_GET_BIT(bitmask, bit) \ |
| ((bitmask[bit / BITS_PER_LONG] >> (bit % BITS_PER_LONG)) & 1) |
| |
| #define MAX(a, b) ((a) > (b) ? (a) : (b)) |
| |
| namespace power_manager { |
| namespace system { |
| |
| namespace { |
| // C++14's <algorithm> could do std::max(EV_MAX, KEY_MAX, SW_MAX); |
| static constexpr int kMaxBit = MAX(MAX(EV_MAX, KEY_MAX), SW_MAX); |
| } // namespace |
| |
| EventDevice::EventDevice(int fd, const base::FilePath& path) |
| : fd_(fd), path_(path) {} |
| |
| EventDevice::~EventDevice() { |
| fd_watcher_.reset(); |
| // ENODEV is expected if the device was just unplugged. |
| if (close(fd_) != 0 && errno != ENODEV) |
| PLOG(ERROR) << "Unable to close FD " << fd_; |
| } |
| |
| std::string EventDevice::GetDebugName() { |
| return path_.value(); |
| } |
| |
| std::string EventDevice::GetPhysPath() { |
| char phys[256] = ""; |
| |
| if (TEMP_FAILURE_RETRY(ioctl(fd_, EVIOCGPHYS(sizeof(phys)), phys)) < 0 && |
| errno != ENOENT) |
| PLOG(ERROR) << "Could not get topo phys path of " << path_.value(); |
| |
| return phys; |
| } |
| |
| std::string EventDevice::GetName() { |
| char name[256] = {0}; |
| |
| if (TEMP_FAILURE_RETRY(ioctl(fd_, EVIOCGNAME(sizeof(name) - 1), name)) < 0) |
| PLOG(ERROR) << "Could not get name of " << path_.value(); |
| |
| return name; |
| } |
| |
| bool EventDevice::IsCrosFp() { |
| return GetName() == kCrosFpInputDevName; |
| } |
| |
| bool EventDevice::IsLidSwitch() { |
| return HasEventBit(0, EV_SW) && HasEventBit(EV_SW, SW_LID); |
| } |
| |
| bool EventDevice::IsTabletModeSwitch() { |
| return HasEventBit(0, EV_SW) && HasEventBit(EV_SW, SW_TABLET_MODE); |
| } |
| |
| bool EventDevice::IsPowerButton() { |
| return HasEventBit(0, EV_KEY) && HasEventBit(EV_KEY, KEY_POWER); |
| } |
| |
| bool EventDevice::HoverSupported() { |
| // Multitouch hover uses just the ABS_MT_DISTANCE event in addition to |
| // the normal multi-touch events. |
| if (HasEventBit(0, EV_ABS) && HasEventBit(EV_ABS, ABS_MT_DISTANCE)) |
| return true; |
| |
| // Simple single-touch hover presence-only detection uses 3 events: |
| // ABS_DISTANCE, BTN_TOUCH, and BTN_TOOL_FINGER. |
| if (HasEventBit(0, EV_ABS) && HasEventBit(EV_ABS, ABS_DISTANCE) && |
| HasEventBit(0, EV_KEY) && HasEventBit(EV_KEY, BTN_TOUCH) && |
| HasEventBit(EV_KEY, BTN_TOOL_FINGER)) |
| return true; |
| |
| return false; |
| } |
| |
| bool EventDevice::HasLeftButton() { |
| return HasEventBit(0, EV_KEY) && HasEventBit(EV_KEY, BTN_LEFT); |
| } |
| |
| LidState EventDevice::GetInitialLidState() { |
| CHECK(!fd_watcher_) << "GetInitialLidState called after WatchForEvents"; |
| return GetSwitchBit(SW_LID) ? LidState::CLOSED : LidState::OPEN; |
| } |
| |
| TabletMode EventDevice::GetInitialTabletMode() { |
| CHECK(!fd_watcher_) << "GetInitialTabletMode called after WatchForEvents"; |
| return GetSwitchBit(SW_TABLET_MODE) ? TabletMode::ON : TabletMode::OFF; |
| } |
| |
| bool EventDevice::HasEventBit(int event_type, int bit) { |
| DCHECK(bit <= kMaxBit); |
| |
| // bitmask needs to hold kMaxBit+1 bits |
| unsigned long bitmask[BITS_TO_LONGS(kMaxBit + 1)]; // NOLINT(runtime/int) |
| memset(bitmask, 0, sizeof(bitmask)); |
| if (TEMP_FAILURE_RETRY( |
| ioctl(fd_, EVIOCGBIT(event_type, sizeof(bitmask)), bitmask)) < 0) { |
| PLOG(ERROR) << "EVIOCGBIT failed for " << path_.value(); |
| return false; |
| } |
| return BITMASK_GET_BIT(bitmask, bit); |
| } |
| |
| bool EventDevice::GetSwitchBit(int bit) { |
| DCHECK(bit <= kMaxBit); |
| |
| // bitmask needs to hold SW_MAX+1 bits |
| unsigned long bitmask[BITS_TO_LONGS(SW_MAX + 1)]; // NOLINT(runtime/int) |
| memset(bitmask, 0, sizeof(bitmask)); |
| if (TEMP_FAILURE_RETRY(ioctl(fd_, EVIOCGSW(sizeof(bitmask)), bitmask)) < 0) { |
| PLOG(ERROR) << "EVIOCGBIT failed for " << path_.value(); |
| return false; |
| } |
| return BITMASK_GET_BIT(bitmask, bit); |
| } |
| |
| bool EventDevice::ReadEvents(std::vector<input_event>* events_out) { |
| DCHECK(events_out); |
| events_out->clear(); |
| |
| struct input_event events[64]; |
| ssize_t read_size = HANDLE_EINTR(read(fd_, events, sizeof(events))); |
| if (read_size < 0) { |
| // ENODEV is expected if the device was just unplugged. |
| if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODEV) |
| PLOG(ERROR) << "Reading events from " << path_.value() << " failed"; |
| return false; |
| } else if (read_size == 0) { |
| LOG(ERROR) << "Read returned 0 when reading events from " << path_.value(); |
| return false; |
| } |
| |
| const size_t num_events = read_size / sizeof(struct input_event); |
| if (read_size % sizeof(struct input_event)) { |
| LOG(ERROR) << "Read " << read_size << " byte(s) while expecting " |
| << sizeof(struct input_event) << "-byte events"; |
| return false; |
| } |
| |
| events_out->reserve(num_events); |
| for (size_t i = 0; i < num_events; ++i) |
| events_out->push_back(events[i]); |
| return true; |
| } |
| |
| void EventDevice::WatchForEvents(base::Closure new_events_cb) { |
| fd_watcher_ = base::FileDescriptorWatcher::WatchReadable(fd_, new_events_cb); |
| } |
| |
| EventDeviceFactory::EventDeviceFactory() {} |
| |
| EventDeviceFactory::~EventDeviceFactory() {} |
| |
| std::shared_ptr<EventDeviceInterface> EventDeviceFactory::Open( |
| const base::FilePath& path) { |
| int fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY | O_NONBLOCK)); |
| if (fd < 0) { |
| PLOG(ERROR) << "open() failed for " << path.value(); |
| return std::shared_ptr<EventDeviceInterface>(); |
| } |
| return std::shared_ptr<EventDeviceInterface>(new EventDevice(fd, path)); |
| } |
| |
| } // namespace system |
| } // namespace power_manager |