blob: 843a1f96b9ad91858156c3bfe0ac2ba3575e84b3 [file] [log] [blame]
// Copyright (c) 2012 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/input.h"
#include <dirent.h>
#include <fcntl.h>
#include <libudev.h>
#include <linux/input.h>
#include "base/file_util.h"
#include "base/file_path.h"
#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
using std::map;
using std::string;
using std::vector;
namespace {
const char kInputUdevSubsystem[] = "input";
const char kSysClassInputPath[] = "/sys/class/input";
const char kDevInputPath[] = "/dev/input";
const char kEventBaseName[] = "event";
const char kInputBaseName[] = "input";
const char kWakeupDisabled[] = "disabled";
const char kWakeupEnabled[] = "enabled";
power_manager::InputType GetInputType(const struct input_event& event) {
if (event.type == EV_KEY) {
// For key events, only handle the keys listed below.
switch (event.code) {
case KEY_POWER: return power_manager::INPUT_POWER_BUTTON;
case KEY_F13: return power_manager::INPUT_LOCK_BUTTON;
case KEY_F4: return power_manager::INPUT_KEY_F4;
case KEY_LEFTCTRL: return power_manager::INPUT_KEY_LEFT_CTRL;
case KEY_RIGHTCTRL: return power_manager::INPUT_KEY_RIGHT_CTRL;
case KEY_LEFTALT: return power_manager::INPUT_KEY_LEFT_ALT;
case KEY_RIGHTALT: return power_manager::INPUT_KEY_RIGHT_ALT;
case KEY_LEFTSHIFT: return power_manager::INPUT_KEY_LEFT_SHIFT;
case KEY_RIGHTSHIFT:return power_manager::INPUT_KEY_RIGHT_SHIFT;
default: return power_manager::INPUT_UNHANDLED;
}
}
// For switch events, only handle events from the lid.
if (event.type == EV_SW && event.code == SW_LID)
return power_manager::INPUT_LID;
return power_manager::INPUT_UNHANDLED;
}
const char* InputTypeToString(power_manager::InputType type) {
switch (type) {
case power_manager::INPUT_LID: return "input(LID)";
case power_manager::INPUT_POWER_BUTTON: return "input(POWER_BUTTON)";
case power_manager::INPUT_LOCK_BUTTON: return "input(LOCK_BUTTON)";
case power_manager::INPUT_KEY_LEFT_CTRL: return "input(KEY_LEFT_CTRL)";
case power_manager::INPUT_KEY_RIGHT_CTRL: return "input(KEY_RIGHT_CTRL)";
case power_manager::INPUT_KEY_LEFT_ALT: return "input(KEY_LEFT_ALT)";
case power_manager::INPUT_KEY_RIGHT_ALT: return "input(KEY_RIGHT_ALT)";
case power_manager::INPUT_KEY_LEFT_SHIFT: return "input(KEY_LEFT_SHIFT)";
case power_manager::INPUT_KEY_RIGHT_SHIFT:return "input(KEY_RIGHT_SHIFT)";
case power_manager::INPUT_KEY_F4: return "input(KEY_F4)";
case power_manager::INPUT_UNHANDLED: return "input(UNHANDLED)";
default: NOTREACHED(); return "";
}
}
bool GetSuffixNumber(const char* name, const char* base_name, int* suffix) {
if (strncmp(base_name, name, strlen(base_name)))
return false;
return base::StringToInt(name + strlen(base_name), suffix);
}
} // namespace
namespace power_manager {
Input::Input()
: handler_(NULL),
handler_data_(NULL),
lid_fd_(-1),
num_power_key_events_(0),
num_lid_events_(0),
wakeups_enabled_(true) {}
Input::~Input() {
for (InputMap::iterator iter = registered_inputs_.begin();
iter != registered_inputs_.end();
++iter) {
CloseIOChannel(&iter->second);
}
if (lid_fd_ >= 0)
close(lid_fd_);
}
bool Input::Init(const vector<string>& wakeup_input_names) {
for (vector<string>::const_iterator names_iter = wakeup_input_names.begin();
names_iter != wakeup_input_names.end(); ++names_iter) {
// Iterate through the vector of input names, and if not the empty string,
// put the input name into the wakeup_inputs_map_, mapping to -1.
// This indicates looking for input devices with this name, but there
// is no input number associated with the device just yet.
if ((*names_iter).length() > 0)
wakeup_inputs_map_[*names_iter] = -1;
}
RegisterUdevEventHandler();
RegisterInputWakeSources();
return RegisterInputDevices();
}
#define BITS_PER_LONG (sizeof(long) * 8)
#define NUM_BITS(x) ((((x) - 1) / BITS_PER_LONG) + 1)
#define OFF(x) ((x) % BITS_PER_LONG)
#define BIT(x) (1UL << OFF(x))
#define LONG(x) ((x) / BITS_PER_LONG)
#define IS_BIT_SET(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
bool Input::QueryLidState(int* lid_state) {
if (0 > lid_fd_) {
LOG(ERROR) << "No lid found on system.";
return false;
}
unsigned long switch_events[NUM_BITS(SW_LID + 1)];
memset(switch_events, 0, sizeof(switch_events));
if (ioctl(lid_fd_, EVIOCGBIT(EV_SW, SW_LID + 1), switch_events) < 0) {
LOG(ERROR) << "Error in GetLidState ioctl";
return false;
}
if (IS_BIT_SET(SW_LID, switch_events)) {
ioctl(lid_fd_, EVIOCGSW(sizeof(switch_events)), switch_events);
*lid_state = IS_BIT_SET(SW_LID, switch_events);
return true;
} else {
return false;
}
}
bool Input::DisableWakeInputs() {
wakeups_enabled_ = false;
return SetInputWakeupStates();
}
bool Input::EnableWakeInputs() {
wakeups_enabled_ = true;
return SetInputWakeupStates();
}
bool Input::RegisterInputDevices() {
FilePath input_path(kDevInputPath);
DIR* dir = opendir(input_path.value().c_str());
int num_registered = 0;
if (dir) {
struct dirent entry;
struct dirent* result;
while (readdir_r(dir, &entry, & result) == 0 && result) {
if (result->d_name[0]) {
if (AddEvent(result->d_name))
num_registered++;
}
}
} else {
LOG(ERROR) << "Cannot open input dir : " << input_path.value().c_str();
return false;
}
LOG(INFO) << "Number of power key events registered : "
<< num_power_key_events_;
// Allow max of one lid.
if (num_lid_events_ > 1) {
LOG(ERROR) << "No lid events registered.";
return false;
}
LOG(INFO) << "Number of lid events registered : " << num_lid_events_;
return true;
}
bool Input::RegisterInputWakeSources() {
DIR* dir = opendir(kSysClassInputPath);
if (dir) {
struct dirent entry;
struct dirent* result;
while (readdir_r(dir, &entry, &result) == 0 && result)
if (result->d_name[0] &&
strncmp(result->d_name, kInputBaseName, strlen(kInputBaseName)) == 0)
AddWakeInput(result->d_name);
}
return true;
}
bool Input::SetInputWakeupStates() {
bool result = true;
for (WakeupMap::iterator iter = wakeup_inputs_map_.begin();
iter != wakeup_inputs_map_.end(); iter++) {
int input_num = (*iter).second;
if (input_num != -1 && !SetWakeupState(input_num, wakeups_enabled_)) {
result = false;
LOG(WARNING) << "Failed to set power/wakeup for input" << input_num;
}
}
return result;
}
bool Input::SetWakeupState(int input_num, bool enabled) {
// Allocate a buffer of size enough to fit the basename "input"
// with an integer up to maxint.
// The number of digits required to hold a number k represented in base b
// is ceil(logb(k)).
// In this case, base b=10 and k = 256^n, where n=number of bytes.
// So number of digits = ceil(log10(256^n)) = ceil (n log10(256))
// = ceil(2.408 n) <= 3 * n.
// + 1 for null terminator.
char name[strlen(kInputBaseName) + sizeof(input_num) * 3 + 1];
snprintf(name, sizeof(name), "%s%d", kInputBaseName, input_num);
FilePath input_path = FilePath(kSysClassInputPath).Append(name);
// wakeup sysfs is at /sys/class/input/inputX/device/power/wakeup
FilePath wakeup_path = input_path.Append("device/power/wakeup");
if (access(wakeup_path.value().c_str(), R_OK)) {
LOG(WARNING) << "Failed to access power/wakeup for : " << name;
return false;
}
const char* wakeup_str = enabled ? kWakeupEnabled : kWakeupDisabled;
if (!file_util::WriteFile(wakeup_path, wakeup_str, strlen(wakeup_str))) {
LOG(ERROR) << "Failed to write to power/wakeup.";
return false;
}
LOG(INFO) << "Set power/wakeup for input" << input_num << " state: "
<< wakeup_str;
return true;
}
void Input::CloseIOChannel(IOChannelWatch* channel) {
// The source id should not be invalid (see AddEvent()), so log a warning and
// continue with the removal instead of failing or skipping. We still want to
// remove the IO channel itself even if the source is invalid.
if (channel->source_id == 0)
LOG(WARNING) << "Attempting to remove invalid glib source.";
else
g_source_remove(channel->source_id);
channel->source_id = 0;
if (g_io_channel_shutdown(channel->channel, true, NULL) != G_IO_STATUS_NORMAL)
LOG(WARNING) << "Error shutting down GIO channel.";
if (close(g_io_channel_unix_get_fd(channel->channel)) < 0)
LOG(ERROR) << "Error closing file handle.";
channel->channel = NULL;
}
bool Input::AddEvent(const char* name) {
int event_num = -1;
if (!GetSuffixNumber(name, kEventBaseName, &event_num)) {
LOG(WARNING) << name << " is not a valid event name, not adding as event.";
return false;
}
InputMap::iterator iter = registered_inputs_.find(event_num);
if (iter != registered_inputs_.end()) {
LOG(WARNING) << "Input event " << event_num << " already registered.";
return false;
}
FilePath event_path = FilePath(kDevInputPath).Append(name);
int event_fd;
if (access(event_path.value().c_str(), R_OK)) {
LOG(WARNING) << "Failed to read from device.";
return false;
}
if ((event_fd = open(event_path.value().c_str(), O_RDONLY)) < 0) {
LOG(ERROR) << "Failed to open - " << event_path.value().c_str();
return false;
}
if (!RegisterInputEvent(event_fd, event_num)) {
if (close(event_fd) < 0) // event not registered, closing.
LOG(ERROR) << "Error closing file handle.";
return false;
}
return true;
}
bool Input::RemoveEvent(const char* name) {
int event_num = -1;
if (!GetSuffixNumber(name, kEventBaseName, &event_num)) {
LOG(WARNING) << name << " is not a valid event name, not removing event.";
return false;
}
InputMap::iterator iter = registered_inputs_.find(event_num);
if (iter == registered_inputs_.end()) {
LOG(WARNING) << "Input event "
<< event_num
<< " not registered. Nothing to remove.";
return false;
}
CloseIOChannel(&iter->second);
registered_inputs_.erase(iter);
return true;
}
bool Input::AddWakeInput(const char* name) {
int input_num = -1;
if (wakeup_inputs_map_.empty() ||
!GetSuffixNumber(name, kInputBaseName, &input_num))
return false;
FilePath device_name_path =
FilePath(kSysClassInputPath).Append(name).Append("name");
if (access(device_name_path.value().c_str(), R_OK)) {
LOG(WARNING) << "Failed to access input name.";
return false;
}
std::string input_name;
if (!file_util::ReadFileToString(device_name_path, &input_name)) {
LOG(WARNING) << "Failed to read input name.";
return false;
}
TrimWhitespaceASCII(input_name, TRIM_TRAILING, &input_name);
WakeupMap::iterator iter = wakeup_inputs_map_.find(input_name);
if (iter == wakeup_inputs_map_.end()) {
// Not on the list of wakeup input devices
return false;
}
if (!SetWakeupState(input_num, wakeups_enabled_)) {
LOG(ERROR) << "Error Adding Wakeup source. Cannot write to power/wakeup.";
return false;
}
wakeup_inputs_map_[input_name] = input_num;
return true;
}
bool Input::RemoveWakeInput(const char* name) {
int input_num = -1;
if (wakeup_inputs_map_.empty() ||
!GetSuffixNumber(name, kInputBaseName, &input_num))
return false;
WakeupMap::iterator iter;
for (iter = wakeup_inputs_map_.begin();
iter != wakeup_inputs_map_.end(); iter++) {
if ((*iter).second == input_num) {
wakeup_inputs_map_[(*iter).first] = -1;
LOG(INFO) << "Remove wakeup source " << (*iter).first << " was:input"
<< input_num;
}
}
return false;
}
bool Input::RegisterInputEvent(int fd, int event_num) {
char name[256] = "Unknown";
if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) {
LOG(ERROR) << "Could not get name of this device.";
return false;
} else {
LOG(INFO) << "Device name : " << name;
}
char phys[256] = "Unknown";
if (ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0) {
LOG(ERROR) << "Could not get topo phys path of this device.";
return false;
} else {
LOG(INFO) << "Device topo phys : " << phys;
}
#ifdef NEW_POWER_BUTTON
// Skip input events from the ACPI power button (identified as LNXPWRBN) if
// a new power button is present. In that case, don't skip the built in
// keyboard, which starts with isa in its topo phys path.
if (0 == strncmp("LNXPWRBN", phys, 8)) {
LOG(INFO) << "Skipping interface : " << phys;
return false;
}
#else
// Skip input events that are on the built in keyboard.
// Many of these devices advertise a power key but do not physically have one.
// Skipping will reduce the wasteful waking of powerm due to keyboard events.
if (0 == strncmp("isa", phys, 3)) {
LOG(INFO) << "Skipping interface : " << phys;
return false;
}
#endif
unsigned long events[NUM_BITS(EV_MAX)];
memset(events, 0, sizeof(events));
if (ioctl(fd, EVIOCGBIT(0, EV_MAX), events) < 0) {
LOG(ERROR) << "Error in powerm ioctl - event list";
return false;
}
GIOChannel* channel = NULL;
guint watch_id = 0;
bool watch_added = false;
if (IS_BIT_SET(EV_KEY, events)) {
unsigned long keys[NUM_BITS(KEY_MAX)];
memset(keys, 0, sizeof(keys));
if (ioctl(fd, EVIOCGBIT(EV_KEY, KEY_MAX), keys) < 0) {
LOG(ERROR) << "Error in powerm ioctl - key";
}
if (IS_BIT_SET(KEY_POWER, keys) || IS_BIT_SET(KEY_F13, keys)) {
LOG(INFO) << "Watching this event for power/lock buttons!";
channel = g_io_channel_unix_new(fd);
watch_id = g_io_add_watch(channel, G_IO_IN, &(Input::EventHandler), this);
num_power_key_events_++;
watch_added = true;
}
}
if (IS_BIT_SET(EV_SW, events)) {
unsigned long switch_events[NUM_BITS(SW_LID + 1)];
memset(switch_events, 0, sizeof(switch_events));
if (ioctl(fd, EVIOCGBIT(EV_SW, SW_LID + 1), switch_events) < 0) {
LOG(ERROR) << "Error in powerm ioctl - switch_events";
}
// An input event may have more than one kind of key or switch.
// For example, if both the power button and the lid switch are handled
// by the gpio_keys driver, both will share a single event in /dev/input.
// In this case, only create one io channel per fd, and only add one
// watch per event file.
if (IS_BIT_SET(SW_LID, switch_events)) {
num_lid_events_++;
if (!watch_added) {
LOG(INFO) << "Watching this event for lid switch!";
channel = g_io_channel_unix_new(fd);
watch_id =
g_io_add_watch(channel, G_IO_IN, &(Input::EventHandler), this);
watch_added = true;
} else {
LOG(INFO) << "Watched event also has a lid!";
}
if (lid_fd_ >= 0)
LOG(WARNING) << "Multiple lid events found on system!";
lid_fd_ = fd;
}
}
if (!watch_added)
return false;
IOChannelWatch desc;
desc.channel = channel;
desc.source_id = watch_id;
// The watch id should be valid if there was a successful event registration.
// Thus, if the id turns out to be invalid, log a warning instead of failing
// or skipping this part.
LOG_IF(WARNING, watch_id == 0) << "Invalid glib source for event " << name;
registered_inputs_[event_num] = desc;
return true;
}
gboolean Input::UdevEventHandler(GIOChannel* /* source */,
GIOCondition /* condition */,
gpointer data) {
Input* input = static_cast<Input*>(data);
struct udev_device* dev = udev_monitor_receive_device(input->udev_monitor_);
if (dev) {
const char* action = udev_device_get_action(dev);
const char* sysname = udev_device_get_sysname(dev);
LOG(INFO) << "Event on ("
<< udev_device_get_subsystem(dev)
<< ") Action "
<< udev_device_get_action(dev)
<< " sys name "
<< udev_device_get_sysname(dev);
if (strncmp(kEventBaseName, sysname, strlen(kEventBaseName)) == 0) {
if (strcmp("add", action) == 0)
input->AddEvent(sysname);
else if (strcmp("remove", action) == 0)
input->RemoveEvent(sysname);
} else if (strncmp(kInputBaseName, sysname, strlen(kInputBaseName)) == 0) {
if (strcmp("add", action) == 0)
input->AddWakeInput(sysname);
else if (strcmp("remove", action) == 0)
input->RemoveWakeInput(sysname);
}
udev_device_unref(dev);
} else {
LOG(ERROR) << "Can't get receive_device()";
return false;
}
return true;
}
void Input::RegisterUdevEventHandler() {
// Create the udev object.
udev_ = udev_new();
if (!udev_)
LOG(ERROR) << "Can't create udev object.";
// Create the udev monitor structure.
udev_monitor_ = udev_monitor_new_from_netlink(udev_, "udev");
if (!udev_monitor_) {
LOG(ERROR) << "Can't create udev monitor.";
udev_unref(udev_);
}
udev_monitor_filter_add_match_subsystem_devtype(udev_monitor_,
kInputUdevSubsystem,
NULL);
udev_monitor_enable_receiving(udev_monitor_);
int fd = udev_monitor_get_fd(udev_monitor_);
GIOChannel* channel = g_io_channel_unix_new(fd);
g_io_add_watch(channel, G_IO_IN, &(Input::UdevEventHandler), this);
LOG(INFO) << "Udev controller waiting for events on subsystem "
<< kInputUdevSubsystem;
}
gboolean Input::EventHandler(GIOChannel* source, GIOCondition condition,
gpointer data) {
Input* input = static_cast<Input*>(data);
if (condition != G_IO_IN)
return false;
struct input_event events[64];
gint fd = g_io_channel_unix_get_fd(source);
ssize_t read_size = read(fd, events, sizeof(events));
if (read_size < 0) {
PLOG(ERROR) << "Read failed in Input::EventHandler";
return true;
}
ssize_t num_events = read_size / sizeof(struct input_event);
if (num_events < 1) {
LOG(ERROR) << "Short read in Input::EventHandler";
return true;
}
if ((num_events * sizeof(struct input_event)) !=
static_cast<size_t>(read_size)) {
LOG(WARNING) << "Read size, not a discrete number of input_events in "
<< "Input::EventHandler";
}
if (!input->handler_)
return true;
for (ssize_t i = 0; i < num_events; i++) {
InputType input_type = GetInputType(events[i]);
if (input_type == INPUT_UNHANDLED)
continue;
LOG(INFO) << "Handling event: " << InputTypeToString(input_type);
(*input->handler_)(input->handler_data_, input_type, events[i].value);
LOG(INFO) << "Input event handled: " << InputTypeToString(input_type);
}
return true;
}
void Input::RegisterHandler(InputHandler handler, void* data) {
handler_ = handler;
handler_data_ = data;
}
} // namespace power_manager