blob: d7240f88a5370010f3c3e487f1b21554a81ec92d [file] [log] [blame]
/* Copyright (c) 2011 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 <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <unistd.h>
/* TODO(thutt): Try to exclude this module if input events are not used. */
#include "verbose.h"
#include "sys_input.h"
#include "thread_management.h"
#include "utils.h"
#include "workfifo.h"
#include "gpio_switch_monitor.h"
typedef struct switch_state_t {
const char *thread_name;
const char *device_name;
const char *insert_command;
const char *remove_command;
unsigned state; /* 0 -> remove, 1 -> insert */
} switch_state_t;
static const char *gpio_switch_decode_state(unsigned state)
{
switch (state) {
case 0: return "off";
case 1: return "on";
default: return "(invalid)";
}
}
FIFO_ENTRY("GPIO Switch Notify State", workfifo, gpio_switch_state,
{
switch_state_t *ss = (switch_state_t *)data;
VERBOSE_FUNCTION_ENTER("%s: %s", ss->thread_name,
gpio_switch_decode_state(ss->state));
if (ss->insert_command != NULL && ss->remove_command != NULL) {
const char *cmd = ss->state ? ss->insert_command
: ss->remove_command;
threads_lock_hardware();
utils_execute_command(cmd);
threads_unlock_hardware();
free(ss);
} else {
/* If there is no command for insertion, or there is no
* command for removal, then both commands must not exist. In
* other words, both must be present, or both must not be
* present.
*
* This invariant is done so that board-based CPP identifiers
* can be used to define the insert & remove commands.
*/
assert(ss->insert_command == NULL && ss->remove_command == NULL);
}
VERBOSE_FUNCTION_EXIT("%s: %s", ss->thread_name,
gpio_switch_decode_state(ss->state));
});
static void gpio_switch_monitor_work(const char *thread_name,
unsigned switch_event,
const char *insert_command,
const char *remove_command,
int fd,
unsigned current_state)
{
unsigned last_state = !current_state;
assert(current_state == 0 || current_state == 1);
while (!thread_management.tm_exit) {
struct timeval timeout;
verbose_log(9, LOG_INFO,
"%s: %s: last: '%s' current: '%s'",
__FUNCTION__, thread_name,
gpio_switch_decode_state(last_state),
gpio_switch_decode_state(current_state));
if (current_state != last_state) {
switch_state_t *ss = calloc((size_t)1,
sizeof(switch_state_t));
/* Only create workfifo entry if associated data can be
* allocated.
*
* If 'ss' cannot be allocated, or if the work fifo item
* cannot be added, don't update the state of the switch
* either; try again during the next time quantum.
*/
if (ss != NULL) {
ss->thread_name = thread_name;
ss->insert_command = insert_command;
ss->remove_command = remove_command;
ss->state = current_state;
if (FIFO_ADD_ITEM(workfifo, gpio_switch_state, ss)) {
last_state = current_state;
} else {
free(ss);
}
}
}
timeout.tv_sec = 0;
timeout.tv_usec = 500000; /* 1/2 second. */
select(fd + 1, NULL, NULL, NULL, &timeout);
sys_input_get_switch_state(fd, switch_event, &current_state);
}
}
void gpio_switch_monitor(const char *thread_name,
const char *device_name,
unsigned switch_event,
const char *insert_command,
const char *remove_command)
{
char *device;
unsigned current_state;
int fd;
VERBOSE_FUNCTION_ENTER("%s, %s, %u, %s, %s",
thread_name, device_name, switch_event,
insert_command, remove_command);
device = sys_input_find_device_by_name(device_name);
if (device != NULL) {
fd = open(device, O_RDONLY);
if (fd != -1 &&
sys_input_get_switch_state(fd, switch_event, &current_state)) {
gpio_switch_monitor_work(thread_name,
switch_event,
insert_command,
remove_command,
fd,
current_state);
close(fd);
} else {
verbose_log(0, LOG_ERR, "%s: unable to find device for '%s'",
__FUNCTION__, device_name);
}
free(device);
} else {
verbose_log(0, LOG_ERR, "%s: unable to find device for '%s'",
__FUNCTION__, device_name);
}
VERBOSE_FUNCTION_EXIT("%s, %s, %u, %s, %s",
thread_name, device_name, switch_event,
insert_command, remove_command);
}