| |
| /* 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, ¤t_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, ¤t_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); |
| } |