commit 4c3f0eca5797909e8087c8d1179c03873e2dbed7 Author: tdVincentB Date: Tue Mar 19 15:41:17 2019 +0100 First commit of project diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6936121 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +TOOLS_CFLAGS := -Wstrict-prototypes -Wshadow -Wpointer-arith -Wcast-qual \ + -Wcast-align -Wwrite-strings -Wnested-externs -Winline \ + -W -Wundef -Wmissing-prototypes +# +# Programs +# +all: funkey_gpio_management + +funkey_gpio_management: funkey_gpio_management.o gpio-utils.o uinput.o gpio_mapping.o read_conf_file.o keydefs.o driver_pcal6416a.o + $(CC) $(LDFLAGS) -o $@ $^ + + +# +# Objects +# + +%.o: %.c + $(CC) $(CFLAGS) $(TOOLS_CFLAGS) -c $< -o $@ + +clean: + rm *.o funkey_gpio_management diff --git a/driver_pcal6416a.c b/driver_pcal6416a.c new file mode 100644 index 0000000..76dea8f --- /dev/null +++ b/driver_pcal6416a.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "read_conf_file.h" +#include +#include +#include "driver_pcal6416a.h" + +/**************************************************************** + * Defines + ****************************************************************/ +#define DEBUG_PCAL6416A_PRINTF (0) + +#if (DEBUG_PCAL6416A_PRINTF) + #define DEBUG_PRINTF(...) printf(__VA_ARGS__); +#else + #define DEBUG_PRINTF(...) +#endif + +/**************************************************************** + * Static variables + ****************************************************************/ +int fd_i2c_expander; +char i2c0_sysfs_filename[] = "/dev/i2c-0"; + +/**************************************************************** + * Static functions + ****************************************************************/ + +/**************************************************************** + * Public functions + ****************************************************************/ +bool pcal6416a_init(void) { + if ((fd_i2c_expander = open(i2c0_sysfs_filename,O_RDWR)) < 0) { + printf("Failed to open the I2C bus %s", i2c0_sysfs_filename); + // ERROR HANDLING; you can check errno to see what went wrong + return false; + } + + if (ioctl(fd_i2c_expander,I2C_SLAVE,PCAL6416A_I2C_ADDR) < 0) { + printf("Failed to acquire bus access and/or talk to slave.\n"); + // ERROR HANDLING; you can check errno to see what went wrong + return false; + } + + uint16_t val_enable_pullups = 0xffff; + i2c_smbus_write_word_data ( fd_i2c_expander , PCAL6416A_EN_PULLUPDOWN , val_enable_pullups ); + + uint16_t val_enable_interrupts = 0x0000; + i2c_smbus_write_word_data ( fd_i2c_expander , PCAL6416A_INT_MASK , val_enable_interrupts ); + + return true; +} + +bool pcal6416a_deinit(void) { + // Close I2C open interface + close(fd_i2c_expander); + return true; +} + +uint16_t pcal6416a_read_mask_interrupts(void){ + uint16_t val = i2c_smbus_read_word_data ( fd_i2c_expander , PCAL6416A_INT_STATUS ); + DEBUG_PRINTF("READ PCAL6416A_INT_STATUS : 0x%04X\n",val); + return val; +} + +uint16_t pcal6416a_read_mask_active_GPIOs(void){ + uint16_t val = i2c_smbus_read_word_data ( fd_i2c_expander , PCAL6416A_INPUT ); + val = 0xFFFF-val; + DEBUG_PRINTF("READ PCAL6416A_INPUT (active GPIOs) : 0x%04X\n",val); + return val; +} \ No newline at end of file diff --git a/driver_pcal6416a.h b/driver_pcal6416a.h new file mode 100644 index 0000000..872efde --- /dev/null +++ b/driver_pcal6416a.h @@ -0,0 +1,39 @@ +#ifndef _DRIVER_PCAL6416A_H_ +#define _DRIVER_PCAL6416A_H_ + + + /**************************************************************** + * Includes + ****************************************************************/ +#include + + /**************************************************************** + * Defines + ****************************************************************/ +// Chip physical address +#define PCAL6416A_I2C_ADDR 0x20 + +// Chip registers adresses +#define PCAL6416A_INPUT 0x00 /* Input port [RO] */ +#define PCAL6416A_DAT_OUT 0x02 /* GPIO DATA OUT [R/W] */ +#define PCAL6416A_POLARITY 0x04 /* Polarity Inversion port [R/W] */ +#define PCAL6416A_CONFIG 0x06 /* Configuration port [R/W] */ +#define PCAL6416A_DRIVE0 0x40 /* Output drive strength Port0 [R/W] */ +#define PCAL6416A_DRIVE1 0x42 /* Output drive strength Port1 [R/W] */ +#define PCAL6416A_INPUT_LATCH 0x44 /* Port0 Input latch [R/W] */ +#define PCAL6416A_EN_PULLUPDOWN 0x46 /* Port0 Pull-up/Pull-down enbl [R/W] */ +#define PCAL6416A_SEL_PULLUPDOWN 0x48 /* Port0 Pull-up/Pull-down slct [R/W] */ +#define PCAL6416A_INT_MASK 0x4A /* Interrupt mask [R/W] */ +#define PCAL6416A_INT_STATUS 0x4C /* Interrupt status [RO] */ +#define PCAL6416A_OUTPUT_CONFIG 0x4F /* Output port config [R/W] */ + +/**************************************************************** + * Public functions + ****************************************************************/ +bool pcal6416a_init(void); +bool pcal6416a_deinit(void); +uint16_t pcal6416a_read_mask_interrupts(void); +uint16_t pcal6416a_read_mask_active_GPIOs(void); + + +#endif //_DRIVER_PCAL6416A_H_ diff --git a/funkey_gpio_management.c b/funkey_gpio_management.c new file mode 100644 index 0000000..8764f65 --- /dev/null +++ b/funkey_gpio_management.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include // Defines signal-handling functions (i.e. trap Ctrl-C) +#include +#include "gpio-utils.h" +#include +#include "uinput.h" +#include "gpio_mapping.h" +#include "read_conf_file.h" + + /**************************************************************** + * Defines + ****************************************************************/ + +/**************************************************************** + * Global variables + ****************************************************************/ +int keepgoing = 1; // Set to 0 when ctrl-c is pressed + +/**************************************************************** + * signal_handler + ****************************************************************/ +void signal_handler(int sig); +// Callback called when SIGINT is sent to the process (Ctrl-C) +void signal_handler(int sig) +{ + printf( "Ctrl-C pressed, cleaning up and exiting..\n" ); + keepgoing = 0; +} + +/**************************************************************** + * Local functions + ****************************************************************/ + + +/**************************************************************** + * Main + ****************************************************************/ +int main(int argc, char **argv, char **envp) +{ + // Variables + STRUCT_MAPPED_GPIO * chained_list_mapping = NULL; + int nb_valid_gpios = 0; + int * gpio_pins = NULL; + + // Set the signal callback for Ctrl-C + signal(SIGINT, signal_handler); + + // Init uinput device + init_uinput(); + + // Read Conf File: Get GPIO pins to declare and all valid pin mappings + get_mapping_from_conf_file(&chained_list_mapping, &nb_valid_gpios, &gpio_pins); + + // Init GPIOs + init_mapping_gpios(gpio_pins, nb_valid_gpios, chained_list_mapping); + + // Main Loop + while (keepgoing) { + listen_gpios_interrupts(); + } + + // De-Init GPIOs + deinit_mapping_gpios(); + + /* + * Give userspace some time to read the events before we destroy the + * device with UI_DEV_DESTOY. + */ + close_uinput(); + + return 0; +} + diff --git a/funkey_gpio_mapping.conf b/funkey_gpio_mapping.conf new file mode 100644 index 0000000..4478bda --- /dev/null +++ b/funkey_gpio_mapping.conf @@ -0,0 +1,55 @@ +################################## +# Funkey GPIO keymap config file # +################################## +# Format: +# +# - First all GPIO Pin numbers must be declared (integers separated by commas) +# Example: 0,1,2,3,4,6,7,11,12,13,14,15 +# +# - Then the mapping can be done as follows (one line per mapping): +# Pin_number[+Pin_number...], type_mapping, value, str_help_name_pin, str_help_fct_pin +# +# args: * Pin_number is the pin number concerned by the maping, +# add +Pin_number for multiple touch mapping (not limited in nb of pins) +# * type_mapping can be KEYBOARD or SHELL_COMMAND +# * value is : +# if type_mapping==KEYBOARD: the keycode from /usr/include/linux/input.h] +# if type_mapping==SHELL_COMMAND: the shell command to exec +# * str_help_name_pin is a sweet name for the pin (ex: PB5) +# * str_help_fct_pin is an help str to indicate the function of this mapping + + + +################################### +# Pins declaration: +0,1,2,3,4,6,7,11,12,13,14,15 + + +################################### +# Mapping: +6, KEYBOARD, KEY_S, KEY_S, Start +7, KEYBOARD, KEY_F, KEY_F, Fn +3, KEYBOARD, KEY_U, KEY_U, Up +4, KEYBOARD, KEY_L, KEY_L, Left +1, KEYBOARD, KEY_D, KEY_D, Down +0, KEYBOARD, KEY_R, KEY_R, Right +15, KEYBOARD, KEY_N, KEY_N, R1 +2, KEYBOARD, KEY_M, KEY_M, L1 +12, KEYBOARD, KEY_B, KEY_B, B +14, KEYBOARD, KEY_A, KEY_A, A +13, KEYBOARD, KEY_X, KEY_X, X +11, KEYBOARD, KEY_Y, KEY_Y, Y +7+7, KEYBOARD, KEY_K, KEY_K, Select +7+15, KEYBOARD, KEY_V, KEY_V, L2 +7+2, KEYBOARD, KEY_O, KEY_O, R2 +#7+3, SHELL_COMMAND, echo "Volume up", 6+3, Volume up +#7+1, SHELL_COMMAND, echo "Volume down", 6+1, Volume down +#7+0, SHELL_COMMAND, echo "Brightness up", 6+0, Brightness up +#7+4, SHELL_COMMAND, echo "Brightness down", 6+4, Brightness down +7+3, SHELL_COMMAND, /root/shell_cmds/start_gpsp.sh, 6+3, start gpsp +7+1, SHELL_COMMAND, /root/shell_cmds/start_pcsx.sh, 6+1, start pcsx +7+0, SHELL_COMMAND, /root/shell_cmds/start_psnes.sh, 6+0, start psnes +7+4, SHELL_COMMAND, /root/shell_cmds/start_mednafen.sh, 6+4, start mednafen GBC +7+11, SHELL_COMMAND, /root/shell_cmds/stop_all_emulators.sh, 6+4, stop all emulators + + diff --git a/gpio-utils.c b/gpio-utils.c new file mode 100644 index 0000000..3355e95 --- /dev/null +++ b/gpio-utils.c @@ -0,0 +1,241 @@ +/* Copyright (c) 2011, RidgeRun + * All rights reserved. + * +From https://www.ridgerun.com/developer/wiki/index.php/Gpio-int-test.c + + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the RidgeRun. + * 4. Neither the name of the RidgeRun nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RIDGERUN ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL RIDGERUN BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "gpio-utils.h" +#include +#include +#include +#include +#include + +/**************************************************************** + * gpio_export + ****************************************************************/ +int gpio_export(unsigned int gpio) +{ + int fd, len; + char buf[MAX_BUF]; + + fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY); + if (fd < 0) { + perror("gpio/export"); + return fd; + } + + len = snprintf(buf, sizeof(buf), "%d", gpio); + write(fd, buf, len); + close(fd); + + return 0; +} + +/**************************************************************** + * gpio_unexport + ****************************************************************/ +int gpio_unexport(unsigned int gpio) +{ + int fd, len; + char buf[MAX_BUF]; + + fd = open(SYSFS_GPIO_DIR "/unexport", O_WRONLY); + if (fd < 0) { + perror("gpio/export"); + return fd; + } + + len = snprintf(buf, sizeof(buf), "%d", gpio); + write(fd, buf, len); + close(fd); + return 0; +} + +/**************************************************************** + * gpio_set_dir + ****************************************************************/ +int gpio_set_dir(unsigned int gpio, const char* dir) +{ + int fd, len; + char buf[MAX_BUF]; + + len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/direction", gpio); + + fd = open(buf, O_WRONLY); + if (fd < 0) { + perror("gpio/direction"); + return fd; + } + + write(fd, dir, sizeof(dir)+1); + + close(fd); + return 0; +} + +/**************************************************************** + * gpio_set_value + ****************************************************************/ +int gpio_set_value(unsigned int gpio, unsigned int value) +{ + int fd, len; + char buf[MAX_BUF]; + + len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio); + + fd = open(buf, O_WRONLY); + if (fd < 0) { + perror("gpio/set-value"); + return fd; + } + + if (value) + write(fd, "1", 2); + else + write(fd, "0", 2); + + close(fd); + return 0; +} + +/**************************************************************** + * gpio_get_value + ****************************************************************/ +int gpio_get_value(unsigned int gpio, unsigned int *value) +{ + int fd, len; + char buf[MAX_BUF]; + char ch; + + len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio); + + fd = open(buf, O_RDONLY); + if (fd < 0) { + perror("gpio/get-value"); + return fd; + } + + read(fd, &ch, 1); + + if (ch != '0') { + *value = 1; + } else { + *value = 0; + } + + close(fd); + return 0; +} + + +/**************************************************************** + * gpio_set_edge + ****************************************************************/ + +int gpio_set_edge(unsigned int gpio, const char *edge) +{ + int fd, len; + char buf[MAX_BUF]; + + len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/edge", gpio); + + fd = open(buf, O_WRONLY); + if (fd < 0) { + perror("gpio/set-edge"); + return fd; + } + + write(fd, edge, strlen(edge) + 1); + close(fd); + return 0; +} + +/**************************************************************** + * gpio_fd_open + ****************************************************************/ + +int gpio_fd_open(unsigned int gpio, unsigned int dir) +{ + int fd, len; + char buf[MAX_BUF]; + + len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio); + + fd = open(buf, dir | O_NONBLOCK ); + if (fd < 0) { + perror("gpio/fd_open"); + } + return fd; +} + +/**************************************************************** + * gpio_fd_close + ****************************************************************/ + +int gpio_fd_close(int fd) +{ + return close(fd); +} + +/**************************************************************** + * ain_get_value (from Mike McDonald) + * https://groups.google.com/forum/#!topic/beagleboard-ece497/SLJ5nQQ_GoU + ****************************************************************/ +int ain_get_value(unsigned int ain, unsigned int *value) +{ + int fd, len, bytesRead; + char buf[MAX_BUF]; + char adc_in[ADC_BUF]; + + len = snprintf(buf, sizeof(buf), SYSFS_AIN_DIR "/AIN%d", ain); + + fd = open(buf, O_RDONLY); + if (fd < 0) { + perror(buf); + return fd; + } + + // Read from the + bytesRead = read(fd, adc_in, ADC_BUF); + + // Turn the buffer value (a string) into an integer + if (bytesRead != -1) { + *value = atoi(adc_in); + adc_in[bytesRead] = (int)NULL; + lseek(fd, 0, SEEK_SET); + } + + // Sleep for a little to ensure that we get good ADC values + usleep(1000); + + close(fd); + return bytesRead; +} + diff --git a/gpio-utils.h b/gpio-utils.h new file mode 100644 index 0000000..1da6ed8 --- /dev/null +++ b/gpio-utils.h @@ -0,0 +1,17 @@ + +#define SYSFS_GPIO_DIR "/sys/class/gpio" +#define MAX_BUF 64 + +int gpio_export(unsigned int gpio); +int gpio_unexport(unsigned int gpio); +int gpio_set_dir(unsigned int gpio, const char* dir); +int gpio_set_value(unsigned int gpio, unsigned int value); +int gpio_get_value(unsigned int gpio, unsigned int *value); +int gpio_set_edge(unsigned int gpio, const char *edge); +int gpio_fd_open(unsigned int gpio, unsigned int dir); +int gpio_fd_close(int fd); + +// Analog in +#define ADC_BUF 1024 +#define SYSFS_AIN_DIR "/sys/devices/ocp.2/helper.11" +int ain_get_value(unsigned int ain, unsigned int *value); diff --git a/gpio_mapping.c b/gpio_mapping.c new file mode 100644 index 0000000..2125126 --- /dev/null +++ b/gpio_mapping.c @@ -0,0 +1,353 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gpio_mapping.h" +#include "driver_pcal6416a.h" + +/**************************************************************** + * Defines + ****************************************************************/ +#define die(str, args...) do { \ + perror(str); \ + return(EXIT_FAILURE); \ + } while(0) + +#define DEBUG_GPIO_PRINTF (0) +#define ERROR_GPIO_PRINTF (1) + +#if (DEBUG_GPIO_PRINTF) +#define GPIO_PRINTF(...) printf(__VA_ARGS__); +#else +#define GPIO_PRINTF(...) +#endif + +#if (ERROR_GPIO_PRINTF) +#define GPIO_ERROR_PRINTF(...) printf(__VA_ARGS__); +#else +#define GPIO_ERROR_PRINTF(...) +#endif + + +/**************************************************************** + * Static variables + ****************************************************************/ +static int nb_mapped_gpios; +static int * gpio_pins; +STRUCT_MAPPED_GPIO * chained_list_mapping; +static int max_fd = 0; +static int gpio_fd_interrupt_i2c; +static fd_set fds; +static bool * mask_gpio_value; + + +/**************************************************************** + * Static functions + ****************************************************************/ + /***** Add a fd to fd_set, and update max_fd_nb ******/ +static int safe_fd_set(int fd, fd_set* list_fds, int* max_fd_nb) { + assert(max_fd_nb != NULL); + + FD_SET(fd, list_fds); + if (fd > *max_fd_nb) { + *max_fd_nb = fd; + } + return 0; +} + +/***** Clear fd from fds, update max fd if needed *****/ +static int safe_fd_clr(int fd, fd_set* list_fds, int* max_fd_nb) { + assert(max_fd_nb != NULL); + + FD_CLR(fd, list_fds); + if (fd == *max_fd_nb) { + (*max_fd_nb)--; + } + return 0; +} + +/***** Apply mapping activation fuction *****/ +static void apply_mapping_activation(STRUCT_MAPPED_GPIO * node_mapping){ + node_mapping->activated = true; + if(node_mapping->type_mapping == TYPE_MAPPING_KEYBOARD){ + GPIO_PRINTF("Apply mapping activation fct: Key event: %d\n", node_mapping->key_value); + sendKey(node_mapping->key_value, 1); + //sendKeyAndStopKey(node_mapping->key_value); + } + else if(node_mapping->type_mapping == TYPE_MAPPING_SHELL_COMMAND){ + GPIO_PRINTF("Apply mapping activation fct: shell command \"%s\"\n", node_mapping->shell_command); + system(node_mapping->shell_command); + } +} + +/***** Apply mapping desactivation function *****/ +static void apply_mapping_desactivation(STRUCT_MAPPED_GPIO * node_mapping){ + node_mapping->activated = false; + if(node_mapping->type_mapping == TYPE_MAPPING_KEYBOARD){ + GPIO_PRINTF("Apply mapping desactivation fct: Key event: %d\n", node_mapping->key_value); + sendKey(node_mapping->key_value, 0); + } + else if(node_mapping->type_mapping == TYPE_MAPPING_SHELL_COMMAND){ + //GPIO_PRINTF("Apply mapping desactivation fct: shell command \"%s\"\n", node_mapping->shell_command); + } +} + + +static void find_and_call_mapping_function(int idx_gpio_interrupted, + STRUCT_MAPPED_GPIO * cl_mapping, + bool* mask_gpio_current_interrupts, + bool * mask_gpio_values, + bool activation){ + // Init variables + STRUCT_MAPPED_GPIO * current = cl_mapping; + bool mapping_found = false; + + // Search if there are mappings to deactivate + while (current != NULL) { + int i; + bool gpio_found_pin_in_mapping = false; + bool all_gpio_activated_in_mapping = true; + + + //GPIO_PRINTF(" Mapping searching for idx_gpio_interrupted: %d, and %s: \n", idx_gpio_interrupted, activation?"activation":"deactivation") + for (i=0; i < current->nb_simultaneous_pins; i++){ + //GPIO_PRINTF(" Pin in mapping: %d, pin_idx=%d\n", gpio_pins[current->pins_idx[i]], current->pins_idx[i]); + // Find if current mapping contains interrupted pin + if (current->pins_idx[i] == idx_gpio_interrupted){ + gpio_found_pin_in_mapping = true; + } + + // Check if all other pins of current mapping were already activated in previous mask + if(!mask_gpio_values[current->pins_idx[i]]){ + all_gpio_activated_in_mapping = false; + } + } + + + // If this mapping contains the interrupted pin and all other pins activated: + if(gpio_found_pin_in_mapping && all_gpio_activated_in_mapping){ + // Treating activation cases + if(activation){ + // if real mapping already found => need to deactivate previously activated ones + if(mapping_found && current->activated){ + GPIO_PRINTF(" Mapping Deactivation because real one already found: GPIO %d found in following activated mapping: \n", + gpio_pins[idx_gpio_interrupted]); + print_chained_list_node(current); + apply_mapping_desactivation(current); + } + else if(!mapping_found){ // If this is the real mapping + if(current->activated){ + GPIO_ERROR_PRINTF("WARNING in find_and_call_mapping_function - Activating already activated mapping %s\n", + current->fct_help_str); + } + // Print information and activate mapping + GPIO_PRINTF(" Mapping Activation: GPIO %d found in following deactivated mapping: \n", + gpio_pins[idx_gpio_interrupted]); + print_chained_list_node(current); + apply_mapping_activation(current); + } + } + else{ // Treating deactivation cases + if(current->activated){ + GPIO_PRINTF(" Mapping Desactivation: GPIO %d found in following activated mapping: \n", + gpio_pins[idx_gpio_interrupted]); + print_chained_list_node(current); + apply_mapping_desactivation(current); + } + } + + // Set that mapping has been found + mapping_found = true; + } + + // Next node in chained list + current = current->next_mapped_gpio; + } +} + +/***** Init GPIO Interrupt i2c expander fd *****/ +static int init_gpio_interrupt(void) +{ + // Variables + int cur_pin_nb = GPIO_PIN_I2C_EXPANDER_INTERRUPT; + GPIO_PRINTF("Initializing Interrupt i2c expander GPIO pin fd: %d\n", cur_pin_nb); + + // Init fds fd_set + FD_ZERO(&fds); + + //Initializing I2C interrupt GPIO + gpio_export(cur_pin_nb); + //gpio_set_edge(cur_pin_nb, "both"); // Can be rising, falling or both + gpio_set_edge(cur_pin_nb, "falling"); // Can be rising, falling or both + gpio_fd_interrupt_i2c = gpio_fd_open(cur_pin_nb, O_RDONLY); + + // add stdin and the sock fd to fds fd_set + safe_fd_set(gpio_fd_interrupt_i2c, &fds, &max_fd); + + return 0; +} + +/***** DeInit GPIO Interrupt i2c expander fd *****/ +static int deinit_gpio_interrupt(void) +{ + GPIO_PRINTF("DeInitializing Interrupt i2c expander GPIO pin fd\n"); + + // Remove stdin and sock fd from fds fd_set + safe_fd_clr(gpio_fd_interrupt_i2c, &fds, &max_fd); + + // Unexport GPIO + gpio_fd_close(gpio_fd_interrupt_i2c); + + return 0; +} + + +/**************************************************************** + * Public functions + ****************************************************************/ +/***** Init I2C expander pin mappings *****/ +int init_mapping_gpios(int * gpio_pins_to_declare, int nb_gpios_to_declare, + STRUCT_MAPPED_GPIO * chained_list_mapping_gpios) +{ + // Variables + int idx_gpio; + unsigned int cur_pin_nb; + + // Store arguments + nb_mapped_gpios = nb_gpios_to_declare; + gpio_pins = gpio_pins_to_declare; + chained_list_mapping = chained_list_mapping_gpios; + + // Init values + mask_gpio_value = malloc(nb_mapped_gpios*sizeof(bool)); + memset(mask_gpio_value, false, nb_mapped_gpios*sizeof(bool)); + STRUCT_MAPPED_GPIO * current = chained_list_mapping; + do{ + //set mapping deactivated + current->activated = false; + + // Next node in chained list + current = current->next_mapped_gpio; + } while(current != NULL); + + // Init GPIO interrupt from I2C expander + init_gpio_interrupt(); + + // Init I2C expander + pcal6416a_init(); + + return 0; +} + +/***** DeInit GPIO fds *****/ +int deinit_mapping_gpios(void) +{ + // DeInit GPIO interrupt from I2C expander + deinit_gpio_interrupt(); + + // DeInit I2C expander + pcal6416a_deinit(); + return 0; +} + + +/***** Listen GPIOs interrupts *****/ +int listen_gpios_interrupts(void) +{ + // Variables + char buffer[2]; + int idx_gpio, value; + bool previous_mask_gpio_value[nb_mapped_gpios]; + bool mask_gpio_current_interrupts[nb_mapped_gpios]; + uint16_t val_i2c_mask_interrupted, val_i2c_mask_active; + bool interrupt_found = false; + + // Back up master + fd_set dup = fds; + + // Init masks + memcpy(previous_mask_gpio_value, mask_gpio_value, nb_mapped_gpios*sizeof(bool)); + memset(mask_gpio_value, false, nb_mapped_gpios*sizeof(bool)); + memset(mask_gpio_current_interrupts, false, nb_mapped_gpios*sizeof(bool)); + + // Note the max_fd+1 + if (select(max_fd+1, NULL, NULL, &dup, NULL) < 0) { + perror("select"); + return -1; + } + + // Check if interrupt from I2C expander + // Check which cur_fd is available for read + for (int cur_fd = 0; cur_fd <= max_fd; cur_fd++) { + if (FD_ISSET(cur_fd, &dup)) { + // Revenir au debut du fichier (lectures successives). + lseek(cur_fd, 0, SEEK_SET); + + // Read current gpio value + if (read(cur_fd, & buffer, 2) != 2) { + perror("read"); + break; + } + + // Effacer le retour-chariot. + buffer[1] = '\0'; + value = 1-atoi(buffer); + + // Found interrupt + interrupt_found = true; + } + } + + if(interrupt_found){ + // Read I2C GPIO masks: + val_i2c_mask_interrupted = pcal6416a_read_mask_interrupts(); + val_i2c_mask_active = pcal6416a_read_mask_active_GPIOs(); + + // Find GPIO idx correspondance + for (idx_gpio=0; idx_gpio Interrupt GPIO: %d, idx_pin: %d\n", gpio_pins[idx_gpio], idx_gpio); + mask_gpio_current_interrupts[idx_gpio] = true; + } + } + + + // Find and call mapping functions (after all active gpios have been assigned): + for (idx_gpio=0; idx_gpio +#include "gpio_mapping.h" + +const key_names_s key_names[] = { +/* + a subset of + sed 's/^#define \([^ \t]\+\)[ \t]*\([^\ \t]\+\)/ { \"\1\",\t\2 },/' /usr/include/linux/input.h + */ + { "KEY_RESERVED", 0 }, + { "KEY_ESC", 1 }, + { "KEY_1", 2 }, + { "KEY_2", 3 }, + { "KEY_3", 4 }, + { "KEY_4", 5 }, + { "KEY_5", 6 }, + { "KEY_6", 7 }, + { "KEY_7", 8 }, + { "KEY_8", 9 }, + { "KEY_9", 10 }, + { "KEY_0", 11 }, + { "KEY_MINUS", 12 }, + { "KEY_EQUAL", 13 }, + { "KEY_BACKSPACE", 14 }, + { "KEY_TAB", 15 }, + { "KEY_Q", 16 }, + { "KEY_W", 17 }, + { "KEY_E", 18 }, + { "KEY_R", 19 }, + { "KEY_T", 20 }, + { "KEY_Y", 21 }, + { "KEY_U", 22 }, + { "KEY_I", 23 }, + { "KEY_O", 24 }, + { "KEY_P", 25 }, + { "KEY_LEFTBRACE", 26 }, + { "KEY_RIGHTBRACE", 27 }, + { "KEY_ENTER", 28 }, + { "KEY_LEFTCTRL", 29 }, + { "KEY_A", 30 }, + { "KEY_S", 31 }, + { "KEY_D", 32 }, + { "KEY_F", 33 }, + { "KEY_G", 34 }, + { "KEY_H", 35 }, + { "KEY_J", 36 }, + { "KEY_K", 37 }, + { "KEY_L", 38 }, + { "KEY_SEMICOLON", 39 }, + { "KEY_APOSTROPHE", 40 }, + { "KEY_GRAVE", 41 }, + { "KEY_LEFTSHIFT", 42 }, + { "KEY_BACKSLASH", 43 }, + { "KEY_Z", 44 }, + { "KEY_X", 45 }, + { "KEY_C", 46 }, + { "KEY_V", 47 }, + { "KEY_B", 48 }, + { "KEY_N", 49 }, + { "KEY_M", 50 }, + { "KEY_COMMA", 51 }, + { "KEY_DOT", 52 }, + { "KEY_SLASH", 53 }, + { "KEY_RIGHTSHIFT", 54 }, + { "KEY_KPASTERISK", 55 }, + { "KEY_LEFTALT", 56 }, + { "KEY_SPACE", 57 }, + { "KEY_CAPSLOCK", 58 }, + { "KEY_F1", 59 }, + { "KEY_F2", 60 }, + { "KEY_F3", 61 }, + { "KEY_F4", 62 }, + { "KEY_F5", 63 }, + { "KEY_F6", 64 }, + { "KEY_F7", 65 }, + { "KEY_F8", 66 }, + { "KEY_F9", 67 }, + { "KEY_F10", 68 }, + { "KEY_NUMLOCK", 69 }, + { "KEY_SCROLLLOCK", 70 }, + { "KEY_KP7", 71 }, + { "KEY_KP8", 72 }, + { "KEY_KP9", 73 }, + { "KEY_KPMINUS", 74 }, + { "KEY_KP4", 75 }, + { "KEY_KP5", 76 }, + { "KEY_KP6", 77 }, + { "KEY_KPPLUS", 78 }, + { "KEY_KP1", 79 }, + { "KEY_KP2", 80 }, + { "KEY_KP3", 81 }, + { "KEY_KP0", 82 }, + { "KEY_KPDOT", 83 }, + + { "KEY_ZENKAKUHANKAKU", 85 }, + { "KEY_102ND", 86 }, + { "KEY_F11", 87 }, + { "KEY_F12", 88 }, + { "KEY_RO", 89 }, + { "KEY_KATAKANA", 90 }, + { "KEY_HIRAGANA", 91 }, + { "KEY_HENKAN", 92 }, + { "KEY_KATAKANAHIRAGANA", 93 }, + { "KEY_MUHENKAN", 94 }, + { "KEY_KPJPCOMMA", 95 }, + { "KEY_KPENTER", 96 }, + { "KEY_RIGHTCTRL", 97 }, + { "KEY_KPSLASH", 98 }, + { "KEY_SYSRQ", 99 }, + { "KEY_RIGHTALT", 100 }, + { "KEY_LINEFEED", 101 }, + { "KEY_HOME", 102 }, + { "KEY_UP", 103 }, + { "KEY_PAGEUP", 104 }, + { "KEY_LEFT", 105 }, + { "KEY_RIGHT", 106 }, + { "KEY_END", 107 }, + { "KEY_DOWN", 108 }, + { "KEY_PAGEDOWN", 109 }, + { "KEY_INSERT", 110 }, + { "KEY_DELETE", 111 }, + { "KEY_MACRO", 112 }, + { "KEY_MUTE", 113 }, + { "KEY_VOLUMEDOWN", 114 }, + { "KEY_VOLUMEUP", 115 }, + { "KEY_POWER", 116 }, /* SC System Power Down */ + { "KEY_KPEQUAL", 117 }, + { "KEY_KPPLUSMINUS", 118 }, + { "KEY_PAUSE", 119 }, + { "KEY_SCALE", 120 }, /* AL Compiz Scale (Expose) */ + + { "KEY_KPCOMMA", 121 }, + { "KEY_HANGEUL", 122 }, + { "KEY_HANGUEL", 122 /*KEY_HANGEUL*/ }, + { "KEY_HANJA", 123 }, + { "KEY_YEN", 124 }, + { "KEY_LEFTMETA", 125 }, + { "KEY_RIGHTMETA", 126 }, + { "KEY_COMPOSE", 127 }, + + { "KEY_STOP", 128 }, /* AC Stop */ + { "KEY_AGAIN", 129 }, + { "KEY_PROPS", 130 }, /* AC Properties */ + { "KEY_UNDO", 131 }, /* AC Undo */ + { "KEY_FRONT", 132 }, + { "KEY_COPY", 133 }, /* AC Copy */ + { "KEY_OPEN", 134 }, /* AC Open */ + { "KEY_PASTE", 135 }, /* AC Paste */ + { "KEY_FIND", 136 }, /* AC Search */ + { "KEY_CUT", 137 }, /* AC Cut */ + { "KEY_HELP", 138 }, /* AL Integrated Help Center */ + { "KEY_MENU", 139 }, /* Menu (show menu) */ + { "KEY_CALC", 140 }, /* AL Calculator */ + { "KEY_SETUP", 141 }, + { "KEY_SLEEP", 142 }, /* SC System Sleep */ + { "KEY_WAKEUP", 143 }, /* System Wake Up */ + { "KEY_FILE", 144 }, /* AL Local Machine Browser */ + { "KEY_SENDFILE", 145 }, + { "KEY_DELETEFILE", 146 }, + { "KEY_XFER", 147 }, + { "KEY_PROG1", 148 }, + { "KEY_PROG2", 149 }, + { "KEY_WWW", 150 }, /* AL Internet Browser */ + { "KEY_MSDOS", 151 }, + { "KEY_COFFEE", 152 }, /* AL Terminal Lock/Screensaver */ + { "KEY_SCREENLOCK", 152 /*KEY_COFFEE*/ }, + { "KEY_DIRECTION", 153 }, + { "KEY_CYCLEWINDOWS", 154 }, + { "KEY_MAIL", 155 }, + { "KEY_BOOKMARKS", 156 }, /* AC Bookmarks */ + { "KEY_COMPUTER", 157 }, + { "KEY_BACK", 158 }, /* AC Back */ + { "KEY_FORWARD", 159 }, /* AC Forward */ + { "KEY_CLOSECD", 160 }, + { "KEY_EJECTCD", 161 }, + { "KEY_EJECTCLOSECD", 162 }, + { "KEY_NEXTSONG", 163 }, + { "KEY_PLAYPAUSE", 164 }, + { "KEY_PREVIOUSSONG", 165 }, + { "KEY_STOPCD", 166 }, + { "KEY_RECORD", 167 }, + { "KEY_REWIND", 168 }, + { "KEY_PHONE", 169 }, /* Media Select Telephone */ + { "KEY_ISO", 170 }, + { "KEY_CONFIG", 171 }, /* AL Consumer Control Configuration */ + { "KEY_HOMEPAGE", 172 }, /* AC Home */ + { "KEY_REFRESH", 173 }, /* AC Refresh */ + { "KEY_EXIT", 174 }, /* AC Exit */ + { "KEY_MOVE", 175 }, + { "KEY_EDIT", 176 }, + { "KEY_SCROLLUP", 177 }, + { "KEY_SCROLLDOWN", 178 }, + { "KEY_KPLEFTPAREN", 179 }, + { "KEY_KPRIGHTPAREN", 180 }, + { "KEY_NEW", 181 }, /* AC New */ + { "KEY_REDO", 182 }, /* AC Redo/Repeat */ + + { "KEY_F13", 183 }, + { "KEY_F14", 184 }, + { "KEY_F15", 185 }, + { "KEY_F16", 186 }, + { "KEY_F17", 187 }, + { "KEY_F18", 188 }, + { "KEY_F19", 189 }, + { "KEY_F20", 190 }, + { "KEY_F21", 191 }, + { "KEY_F22", 192 }, + { "KEY_F23", 193 }, + { "KEY_F24", 194 }, + + { "KEY_PLAYCD", 200 }, + { "KEY_PAUSECD", 201 }, + { "KEY_PROG3", 202 }, + { "KEY_PROG4", 203 }, + { "KEY_DASHBOARD", 204 }, /* AL Dashboard */ + { "KEY_SUSPEND", 205 }, + { "KEY_CLOSE", 206 }, /* AC Close */ + { "KEY_PLAY", 207 }, + { "KEY_FASTFORWARD", 208 }, + { "KEY_BASSBOOST", 209 }, + { "KEY_PRINT", 210 }, /* AC Print */ + { "KEY_HP", 211 }, + { "KEY_CAMERA", 212 }, + { "KEY_SOUND", 213 }, + { "KEY_QUESTION", 214 }, + { "KEY_EMAIL", 215 }, + { "KEY_CHAT", 216 }, + { "KEY_SEARCH", 217 }, + { "KEY_CONNECT", 218 }, + { "KEY_FINANCE", 219 }, /* AL Checkbook/Finance */ + { "KEY_SPORT", 220 }, + { "KEY_SHOP", 221 }, + { "KEY_ALTERASE", 222 }, + { "KEY_CANCEL", 223 }, /* AC Cancel */ + { "KEY_BRIGHTNESSDOWN", 224 }, + { "KEY_BRIGHTNESSUP", 225 }, + { "KEY_MEDIA", 226 }, + + { "KEY_SWITCHVIDEOMODE", 227 }, /* Cycle between available video + outputs (Monitor/LCD/TV-out/etc) */ + { "KEY_KBDILLUMTOGGLE", 228 }, + { "KEY_KBDILLUMDOWN", 229 }, + { "KEY_KBDILLUMUP", 230 }, + + { "KEY_SEND", 231 }, /* AC Send */ + { "KEY_REPLY", 232 }, /* AC Reply */ + { "KEY_FORWARDMAIL", 233 }, /* AC Forward Msg */ + { "KEY_SAVE", 234 }, /* AC Save */ + { "KEY_DOCUMENTS", 235 }, + + { "KEY_BATTERY", 236 }, + + { "KEY_BLUETOOTH", 237 }, + { "KEY_WLAN", 238 }, + { "KEY_UWB", 239 }, + + { "KEY_UNKNOWN", 240 }, + + { "KEY_VIDEO_NEXT", 241 }, /* drive next video source */ + { "KEY_VIDEO_PREV", 242 }, /* drive previous video source */ + { "KEY_BRIGHTNESS_CYCLE", 243 }, /* brightness up, after max is min */ + { "KEY_BRIGHTNESS_ZERO", 244 }, /* brightness off, use ambient */ + { "KEY_DISPLAY_OFF", 245 }, /* display device to off state */ + + { "KEY_WIMAX", 246 }, + { "KEY_RFKILL", 247 }, /* Key that controls all radios */ + + { "KEY_MICMUTE", 248 }, /* Mute / unmute the microphone */ + +/* Code 255 is reserved for special needs of AT keyboard driver */ + + { "BTN_MISC", 0x100 }, + { "BTN_0", 0x100 }, + { "BTN_1", 0x101 }, + { "BTN_2", 0x102 }, + { "BTN_3", 0x103 }, + { "BTN_4", 0x104 }, + { "BTN_5", 0x105 }, + { "BTN_6", 0x106 }, + { "BTN_7", 0x107 }, + { "BTN_8", 0x108 }, + { "BTN_9", 0x109 }, + + { "BTN_MOUSE", 0x110 }, + { "BTN_LEFT", 0x110 }, + { "BTN_RIGHT", 0x111 }, + { "BTN_MIDDLE", 0x112 }, + { "BTN_SIDE", 0x113 }, + { "BTN_EXTRA", 0x114 }, + { "BTN_FORWARD", 0x115 }, + { "BTN_BACK", 0x116 }, + { "BTN_TASK", 0x117 }, + + { "BTN_JOYSTICK", 0x120 }, + { "BTN_TRIGGER", 0x120 }, + { "BTN_THUMB", 0x121 }, + { "BTN_THUMB2", 0x122 }, + { "BTN_TOP", 0x123 }, + { "BTN_TOP2", 0x124 }, + { "BTN_PINKIE", 0x125 }, + { "BTN_BASE", 0x126 }, + { "BTN_BASE2", 0x127 }, + { "BTN_BASE3", 0x128 }, + { "BTN_BASE4", 0x129 }, + { "BTN_BASE5", 0x12a }, + { "BTN_BASE6", 0x12b }, + { "BTN_DEAD", 0x12f }, + + { "BTN_GAMEPAD", 0x130 }, + { "BTN_A", 0x130 }, + { "BTN_B", 0x131 }, + { "BTN_C", 0x132 }, + { "BTN_X", 0x133 }, + { "BTN_Y", 0x134 }, + { "BTN_Z", 0x135 }, + { "BTN_TL", 0x136 }, + { "BTN_TR", 0x137 }, + { "BTN_TL2", 0x138 }, + { "BTN_TR2", 0x139 }, + { "BTN_SELECT", 0x13a }, + { "BTN_START", 0x13b }, + { "BTN_MODE", 0x13c }, + { "BTN_THUMBL", 0x13d }, + { "BTN_THUMBR", 0x13e }, + +#if 0 + + { "BTN_DIGI", 0x140 }, + { "BTN_TOOL_PEN", 0x140 }, + { "BTN_TOOL_RUBBER", 0x141 }, + { "BTN_TOOL_BRUSH", 0x142 }, + { "BTN_TOOL_PENCIL", 0x143 }, + { "BTN_TOOL_AIRBRUSH", 0x144 }, + { "BTN_TOOL_FINGER", 0x145 }, + { "BTN_TOOL_MOUSE", 0x146 }, + { "BTN_TOOL_LENS", 0x147 }, + { "BTN_TOOL_QUINTTAP", 0x148 }, /* Five fingers on trackpad */ + { "BTN_TOUCH", 0x14a }, + { "BTN_STYLUS", 0x14b }, + { "BTN_STYLUS2", 0x14c }, + { "BTN_TOOL_DOUBLETAP", 0x14d }, + { "BTN_TOOL_TRIPLETAP", 0x14e }, + { "BTN_TOOL_QUADTAP", 0x14f }, /* Four fingers on trackpad */ + + { "BTN_WHEEL", 0x150 }, + { "BTN_GEAR_DOWN", 0x150 }, + { "BTN_GEAR_UP", 0x151 }, + + { "KEY_OK", 0x160 }, + { "KEY_SELECT", 0x161 }, + { "KEY_GOTO", 0x162 }, + { "KEY_CLEAR", 0x163 }, + { "KEY_POWER2", 0x164 }, + { "KEY_OPTION", 0x165 }, + { "KEY_INFO", 0x166 }, /* AL OEM Features/Tips/Tutorial */ + { "KEY_TIME", 0x167 }, + { "KEY_VENDOR", 0x168 }, + { "KEY_ARCHIVE", 0x169 }, + { "KEY_PROGRAM", 0x16a }, /* Media Select Program Guide */ + { "KEY_CHANNEL", 0x16b }, + { "KEY_FAVORITES", 0x16c }, + { "KEY_EPG", 0x16d }, + { "KEY_PVR", 0x16e }, /* Media Select Home */ + { "KEY_MHP", 0x16f }, + { "KEY_LANGUAGE", 0x170 }, + { "KEY_TITLE", 0x171 }, + { "KEY_SUBTITLE", 0x172 }, + { "KEY_ANGLE", 0x173 }, + { "KEY_ZOOM", 0x174 }, + { "KEY_MODE", 0x175 }, + { "KEY_KEYBOARD", 0x176 }, + { "KEY_SCREEN", 0x177 }, + { "KEY_PC", 0x178 }, /* Media Select Computer */ + { "KEY_TV", 0x179 }, /* Media Select TV */ + { "KEY_TV2", 0x17a }, /* Media Select Cable */ + { "KEY_VCR", 0x17b }, /* Media Select VCR */ + { "KEY_VCR2", 0x17c }, /* VCR Plus */ + { "KEY_SAT", 0x17d }, /* Media Select Satellite */ + { "KEY_SAT2", 0x17e }, + { "KEY_CD", 0x17f }, /* Media Select CD */ + { "KEY_TAPE", 0x180 }, /* Media Select Tape */ + { "KEY_RADIO", 0x181 }, + { "KEY_TUNER", 0x182 }, /* Media Select Tuner */ + { "KEY_PLAYER", 0x183 }, + { "KEY_TEXT", 0x184 }, + { "KEY_DVD", 0x185 }, /* Media Select DVD */ + { "KEY_AUX", 0x186 }, + { "KEY_MP3", 0x187 }, + { "KEY_AUDIO", 0x188 }, /* AL Audio Browser */ + { "KEY_VIDEO", 0x189 }, /* AL Movie Browser */ + { "KEY_DIRECTORY", 0x18a }, + { "KEY_LIST", 0x18b }, + { "KEY_MEMO", 0x18c }, /* Media Select Messages */ + { "KEY_CALENDAR", 0x18d }, + { "KEY_RED", 0x18e }, + { "KEY_GREEN", 0x18f }, + { "KEY_YELLOW", 0x190 }, + { "KEY_BLUE", 0x191 }, + { "KEY_CHANNELUP", 0x192 }, /* Channel Increment */ + { "KEY_CHANNELDOWN", 0x193 }, /* Channel Decrement */ + { "KEY_FIRST", 0x194 }, + { "KEY_LAST", 0x195 }, /* Recall Last */ + { "KEY_AB", 0x196 }, + { "KEY_NEXT", 0x197 }, + { "KEY_RESTART", 0x198 }, + { "KEY_SLOW", 0x199 }, + { "KEY_SHUFFLE", 0x19a }, + { "KEY_BREAK", 0x19b }, + { "KEY_PREVIOUS", 0x19c }, + { "KEY_DIGITS", 0x19d }, + { "KEY_TEEN", 0x19e }, + { "KEY_TWEN", 0x19f }, + { "KEY_VIDEOPHONE", 0x1a0 }, /* Media Select Video Phone */ + { "KEY_GAMES", 0x1a1 }, /* Media Select Games */ + { "KEY_ZOOMIN", 0x1a2 }, /* AC Zoom In */ + { "KEY_ZOOMOUT", 0x1a3 }, /* AC Zoom Out */ + { "KEY_ZOOMRESET", 0x1a4 }, /* AC Zoom */ + { "KEY_WORDPROCESSOR", 0x1a5 }, /* AL Word Processor */ + { "KEY_EDITOR", 0x1a6 }, /* AL Text Editor */ + { "KEY_SPREADSHEET", 0x1a7 }, /* AL Spreadsheet */ + { "KEY_GRAPHICSEDITOR", 0x1a8 }, /* AL Graphics Editor */ + { "KEY_PRESENTATION", 0x1a9 }, /* AL Presentation App */ + { "KEY_DATABASE", 0x1aa }, /* AL Database App */ + { "KEY_NEWS", 0x1ab }, /* AL Newsreader */ + { "KEY_VOICEMAIL", 0x1ac }, /* AL Voicemail */ + { "KEY_ADDRESSBOOK", 0x1ad }, /* AL Contacts/Address Book */ + { "KEY_MESSENGER", 0x1ae }, /* AL Instant Messaging */ + { "KEY_DISPLAYTOGGLE", 0x1af }, /* Turn display (LCD) on and off */ + { "KEY_SPELLCHECK", 0x1b0 }, /* AL Spell Check */ + { "KEY_LOGOFF", 0x1b1 }, /* AL Logoff */ + + { "KEY_DOLLAR", 0x1b2 }, + { "KEY_EURO", 0x1b3 }, + + { "KEY_FRAMEBACK", 0x1b4 }, /* Consumer - transport controls */ + { "KEY_FRAMEFORWARD", 0x1b5 }, + { "KEY_CONTEXT_MENU", 0x1b6 }, /* GenDesc - system context menu */ + { "KEY_MEDIA_REPEAT", 0x1b7 }, /* Consumer - transport control */ + { "KEY_10CHANNELSUP", 0x1b8 }, /* 10 channels up (10+) */ + { "KEY_10CHANNELSDOWN", 0x1b9 }, /* 10 channels down (10-) */ + { "KEY_IMAGES", 0x1ba }, /* AL Image Browser */ + + { "KEY_DEL_EOL", 0x1c0 }, + { "KEY_DEL_EOS", 0x1c1 }, + { "KEY_INS_LINE", 0x1c2 }, + { "KEY_DEL_LINE", 0x1c3 }, + + { "KEY_FN", 0x1d0 }, + { "KEY_FN_ESC", 0x1d1 }, + { "KEY_FN_F1", 0x1d2 }, + { "KEY_FN_F2", 0x1d3 }, + { "KEY_FN_F3", 0x1d4 }, + { "KEY_FN_F4", 0x1d5 }, + { "KEY_FN_F5", 0x1d6 }, + { "KEY_FN_F6", 0x1d7 }, + { "KEY_FN_F7", 0x1d8 }, + { "KEY_FN_F8", 0x1d9 }, + { "KEY_FN_F9", 0x1da }, + { "KEY_FN_F10", 0x1db }, + { "KEY_FN_F11", 0x1dc }, + { "KEY_FN_F12", 0x1dd }, + { "KEY_FN_1", 0x1de }, + { "KEY_FN_2", 0x1df }, + { "KEY_FN_D", 0x1e0 }, + { "KEY_FN_E", 0x1e1 }, + { "KEY_FN_F", 0x1e2 }, + { "KEY_FN_S", 0x1e3 }, + { "KEY_FN_B", 0x1e4 }, + + { "KEY_BRL_DOT1", 0x1f1 }, + { "KEY_BRL_DOT2", 0x1f2 }, + { "KEY_BRL_DOT3", 0x1f3 }, + { "KEY_BRL_DOT4", 0x1f4 }, + { "KEY_BRL_DOT5", 0x1f5 }, + { "KEY_BRL_DOT6", 0x1f6 }, + { "KEY_BRL_DOT7", 0x1f7 }, + { "KEY_BRL_DOT8", 0x1f8 }, + { "KEY_BRL_DOT9", 0x1f9 }, + { "KEY_BRL_DOT10", 0x1fa }, + + { "KEY_NUMERIC_0", 0x200 }, /* used by phones, remote controls, */ + { "KEY_NUMERIC_1", 0x201 }, /* and other keypads */ + { "KEY_NUMERIC_2", 0x202 }, + { "KEY_NUMERIC_3", 0x203 }, + { "KEY_NUMERIC_4", 0x204 }, + { "KEY_NUMERIC_5", 0x205 }, + { "KEY_NUMERIC_6", 0x206 }, + { "KEY_NUMERIC_7", 0x207 }, + { "KEY_NUMERIC_8", 0x208 }, + { "KEY_NUMERIC_9", 0x209 }, + { "KEY_NUMERIC_STAR", 0x20a }, + { "KEY_NUMERIC_POUND", 0x20b }, + + { "KEY_CAMERA_FOCUS", 0x210 }, + { "KEY_WPS_BUTTON", 0x211 }, /* WiFi Protected Setup key */ + + { "KEY_TOUCHPAD_TOGGLE", 0x212 }, /* Request switch touchpad on or off */ + { "KEY_TOUCHPAD_ON", 0x213 }, + { "KEY_TOUCHPAD_OFF", 0x214 }, + + { "KEY_CAMERA_ZOOMIN", 0x215 }, + { "KEY_CAMERA_ZOOMOUT", 0x216 }, + { "KEY_CAMERA_UP", 0x217 }, + { "KEY_CAMERA_DOWN", 0x218 }, + { "KEY_CAMERA_LEFT", 0x219 }, + { "KEY_CAMERA_RIGHT", 0x21a }, + + { "BTN_TRIGGER_HAPPY", 0x2c0 }, + { "BTN_TRIGGER_HAPPY1", 0x2c0 }, + { "BTN_TRIGGER_HAPPY2", 0x2c1 }, + { "BTN_TRIGGER_HAPPY3", 0x2c2 }, + { "BTN_TRIGGER_HAPPY4", 0x2c3 }, + { "BTN_TRIGGER_HAPPY5", 0x2c4 }, + { "BTN_TRIGGER_HAPPY6", 0x2c5 }, + { "BTN_TRIGGER_HAPPY7", 0x2c6 }, + { "BTN_TRIGGER_HAPPY8", 0x2c7 }, + { "BTN_TRIGGER_HAPPY9", 0x2c8 }, + { "BTN_TRIGGER_HAPPY10", 0x2c9 }, + { "BTN_TRIGGER_HAPPY11", 0x2ca }, + { "BTN_TRIGGER_HAPPY12", 0x2cb }, + { "BTN_TRIGGER_HAPPY13", 0x2cc }, + { "BTN_TRIGGER_HAPPY14", 0x2cd }, + { "BTN_TRIGGER_HAPPY15", 0x2ce }, + { "BTN_TRIGGER_HAPPY16", 0x2cf }, + { "BTN_TRIGGER_HAPPY17", 0x2d0 }, + { "BTN_TRIGGER_HAPPY18", 0x2d1 }, + { "BTN_TRIGGER_HAPPY19", 0x2d2 }, + { "BTN_TRIGGER_HAPPY20", 0x2d3 }, + { "BTN_TRIGGER_HAPPY21", 0x2d4 }, + { "BTN_TRIGGER_HAPPY22", 0x2d5 }, + { "BTN_TRIGGER_HAPPY23", 0x2d6 }, + { "BTN_TRIGGER_HAPPY24", 0x2d7 }, + { "BTN_TRIGGER_HAPPY25", 0x2d8 }, + { "BTN_TRIGGER_HAPPY26", 0x2d9 }, + { "BTN_TRIGGER_HAPPY27", 0x2da }, + { "BTN_TRIGGER_HAPPY28", 0x2db }, + { "BTN_TRIGGER_HAPPY29", 0x2dc }, + { "BTN_TRIGGER_HAPPY30", 0x2dd }, + { "BTN_TRIGGER_HAPPY31", 0x2de }, + { "BTN_TRIGGER_HAPPY32", 0x2df }, + { "BTN_TRIGGER_HAPPY33", 0x2e0 }, + { "BTN_TRIGGER_HAPPY34", 0x2e1 }, + { "BTN_TRIGGER_HAPPY35", 0x2e2 }, + { "BTN_TRIGGER_HAPPY36", 0x2e3 }, + { "BTN_TRIGGER_HAPPY37", 0x2e4 }, + { "BTN_TRIGGER_HAPPY38", 0x2e5 }, + { "BTN_TRIGGER_HAPPY39", 0x2e6 }, + { "BTN_TRIGGER_HAPPY40", 0x2e7 }, +#endif + + { "LAST", -1 }, +}; diff --git a/read_conf_file.c b/read_conf_file.c new file mode 100644 index 0000000..187c402 --- /dev/null +++ b/read_conf_file.c @@ -0,0 +1,382 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "read_conf_file.h" +#include + +/**************************************************************** + * Defines + ****************************************************************/ +#define die(str, args...) do { \ + perror(str); \ + return(EXIT_FAILURE); \ + } while(0) + +#define DEBUG_READ_CONF_FILE_PRINTF (0) + +#if (DEBUG_READ_CONF_FILE_PRINTF) + #define READ_CONF_FILE_PRINTF(...) printf(__VA_ARGS__); +#else + #define READ_CONF_FILE_PRINTF(...) +#endif + + +/**************************************************************** + * Extern variables + ****************************************************************/ +extern key_names_s key_names[]; + + +/**************************************************************** + * Static variables + ****************************************************************/ +static STRUCT_MAPPED_GPIO * head_chained_list_mapping_gpio = NULL; +static int count_valid_gpios = 0; +static int gpio_pins[MAX_NUM_GPIO]; + + +/**************************************************************** + * Static functions + ****************************************************************/ +/* Return linux Key code from corresponding str*/ +static int find_key(const char *name) +{ + int i=0; + while(key_names[i].code >= 0){ + if(!strncmp(key_names[i].name, name, 32))break; + i++; + } + if(key_names[i].code < 0){ + i = 0; + } + return i; +} + +/* Return pin idx in the array of instanciated pins */ +static int get_idx_pin(int* ptr_list_gpio_pins, int size_list_gpio_pins, int pin_number_to_find){ + int idx; + + for (idx=0; idxnext_mapped_gpio = a->next_mapped_gpio; + a->next_mapped_gpio = b; +} + + +/* Bubble sort the given linked list */ +static void bubbleSort(STRUCT_MAPPED_GPIO *head) +{ + int swapped, i; + STRUCT_MAPPED_GPIO *ptr1 = head; + STRUCT_MAPPED_GPIO *lptr = NULL; + + /* Checking for empty list */ + if (ptr1 == NULL) + return; + + do + { + swapped = 0; + ptr1 = head; + + while (ptr1->next_mapped_gpio != lptr) + { + if (ptr1->nb_simultaneous_pins < ptr1->next_mapped_gpio->nb_simultaneous_pins) + { + swap(ptr1, ptr1->next_mapped_gpio); + swapped = 1; + } + ptr1 = ptr1->next_mapped_gpio; + } + lptr = ptr1; + } + while (swapped); +} + + +/* Push new node in chained list */ +static void push(STRUCT_MAPPED_GPIO ** head, STRUCT_MAPPED_GPIO * new_mapping) { + STRUCT_MAPPED_GPIO ** current = head; + while (*current != NULL) { + current = &(*current)->next_mapped_gpio; + } + + /* now we can add a new variable */ + *current = malloc(sizeof(STRUCT_MAPPED_GPIO)); + memcpy(*current, new_mapping, sizeof(STRUCT_MAPPED_GPIO)); + (*current)->next_mapped_gpio = NULL; +} + + + +/**************************************************************** + * Public functions + ****************************************************************/ + void get_mapping_from_conf_file(STRUCT_MAPPED_GPIO ** chained_list_mapping, + int* nb_valid_gpios, int ** valid_gpio_pins){ + /* Variables */ + READ_CONF_FILE_PRINTF("Reading the config file:\n"); + FILE *fp; + char ln[MAX_LN_LENGTH]; + int lnno = 0; + char conffile[80]; + bool pins_declaration_line = true; + + /* search for the conf file in ~/.funkey_gpio_mapping.conf and /etc/funkey_gpio_mapping.conf */ + sprintf(conffile, "%s/.funkey_gpio_mapping.conf", getenv("HOME")); + fp = fopen(conffile, "r"); + if(!fp){ + fp = fopen("/etc/funkey_gpio_mapping.conf", "r"); + if(!fp){ + fp = fopen("./funkey_gpio_mapping.conf", "r"); + if(!fp){ + perror(conffile); + perror("/etc/funkey_gpio_mapping.conf"); + perror("./funkey_gpio_mapping.conf"); + } + sprintf(conffile, "./funkey_gpio_mapping.conf"); + } + else{ + sprintf(conffile, "/etc/funkey_gpio_mapping.conf"); + } + } + if(fp){ + READ_CONF_FILE_PRINTF("Config file found at %s\n", conffile); + } + + /* Main loop to read conf file (purposely exploded and not in multiple sub-functions) */ + while(fp && !feof(fp)){ + if(fgets(ln, MAX_LN_LENGTH, fp)){ + lnno++; + if(strlen(ln) > 1){ + if(ln[0]=='#')continue; + READ_CONF_FILE_PRINTF("\nCurrent line : %s\n", ln); + + // ************ Pins declaration ************ + if(pins_declaration_line){ + char* token; + int token_int; + + //count nb valid GPIOs and store them + token = strtok(ln, ","); + while(token != NULL){ + token_int = atoi(token); + if(token_int || !strcmp(token, "0")){ + gpio_pins[count_valid_gpios] = token_int; + count_valid_gpios++; + READ_CONF_FILE_PRINTF("GPIO %d declared\n", token_int); + } + else{ + READ_CONF_FILE_PRINTF("Could not declare GPIO: %s\n", token); + } + token = strtok(NULL, ","); + } + pins_declaration_line = false; + } + + // ************ Pins mapping ************ + else{ + STRUCT_MAPPED_GPIO cur_gpio_mapping; + char* end_ln; + char* token_comma; + char * current_arg; + int cur_arg_idx = MAPPING_ARG_PINS; + int idx_pin = 0; + int cur_pin; + bool add_current_mapping = true; + + token_comma = strtok_r(ln, ",", &end_ln); + while(token_comma != NULL && cur_arg_idx < NB_MAPPING_ARG_NAMES && add_current_mapping){ + current_arg= token_comma; + //remove_initial_spaces: + while(*current_arg==' ' && *current_arg!=0){current_arg++;} + READ_CONF_FILE_PRINTF(" Current arg: %s\n", current_arg); + //Handle current mapping arg + switch(cur_arg_idx){ + case MAPPING_ARG_PINS: + //count nb valid GPIOs and store them + ;char* end_arg; + char * token_plus = strtok_r(current_arg, "+", &end_arg); + while(token_plus != NULL){ + cur_pin = atoi(token_plus); + if(cur_pin || !strcmp(token_plus, "0")){ + int idx_cur_pin = get_idx_pin(gpio_pins, count_valid_gpios, cur_pin); + if(idx_cur_pin == -1){ + READ_CONF_FILE_PRINTF(" Could not find GPIO: %s in previously instanciated GPIOs\n", token_plus); + add_current_mapping = false; + break; + } + cur_gpio_mapping.pins_idx[idx_pin] = idx_cur_pin; + idx_pin++; + READ_CONF_FILE_PRINTF(" GPIO %d in current mapping\n", cur_pin); + } + else{ + READ_CONF_FILE_PRINTF(" Could not find GPIO: %s\n", token_plus); + add_current_mapping = false; + break; + } + token_plus = strtok_r(NULL, "+", &end_arg);//strtok_r(NULL, ","); + } + cur_gpio_mapping.nb_simultaneous_pins = idx_pin; + break; + case MAPPING_ARG_TYPE: + if(!strcmp(current_arg, "KEYBOARD")){ + cur_gpio_mapping.type_mapping = TYPE_MAPPING_KEYBOARD; + } + else if(!strcmp(current_arg, "SHELL_COMMAND")){ + cur_gpio_mapping.type_mapping = TYPE_MAPPING_SHELL_COMMAND; + } + else{ + READ_CONF_FILE_PRINTF(" %s is not a valid mapping type\n", current_arg); + add_current_mapping = false; + break; + } + break; + case MAPPING_ARG_VALUE: + switch (cur_gpio_mapping.type_mapping){ + case TYPE_MAPPING_KEYBOARD: + cur_gpio_mapping.key_value = find_key(current_arg); + if(!cur_gpio_mapping.key_value){ + READ_CONF_FILE_PRINTF(" Could not find Key: %s\n", current_arg); + add_current_mapping = false; + } + break; + case TYPE_MAPPING_SHELL_COMMAND: + cur_gpio_mapping.shell_command = malloc(strlen(current_arg)*sizeof(char)); + strcpy(cur_gpio_mapping.shell_command, current_arg); + break; + default: + READ_CONF_FILE_PRINTF(" %d is not a valid mapping type\n", cur_gpio_mapping.type_mapping); + add_current_mapping = false; + break; + } + break; + case MAPPING_ARG_STR_HELP_PIN_NAME: + if(!strlen(current_arg)) continue; + cur_gpio_mapping.pin_help_str = malloc(strlen(current_arg)*sizeof(char)); + strcpy(cur_gpio_mapping.pin_help_str, current_arg); + break; + case MAPPING_ARG_STR_HELP_PIN_FCT: + if(!strlen(current_arg)) continue; + cur_gpio_mapping.fct_help_str = malloc(strlen(current_arg)*sizeof(char)); + strcpy(cur_gpio_mapping.fct_help_str, current_arg); + break; + default: + break; + } + + + //next arg + token_comma = strtok_r(NULL, ",", &end_ln); + cur_arg_idx++; + } + + if(add_current_mapping){ + push(&head_chained_list_mapping_gpio, &cur_gpio_mapping); + READ_CONF_FILE_PRINTF("Current Mapping added successfully\n\n"); + } + else{ + READ_CONF_FILE_PRINTF("Current Mapping not added\n\n"); + } + } + } + } + } + + READ_CONF_FILE_PRINTF("Sorting Chained list...\n"); + bubbleSort(head_chained_list_mapping_gpio); + print_all_chained_list(head_chained_list_mapping_gpio); + print_gpios(gpio_pins, count_valid_gpios); + + if(fp){ + fclose(fp); + } + + /* Return parameters */ + *chained_list_mapping = head_chained_list_mapping_gpio; + *nb_valid_gpios = count_valid_gpios; + *valid_gpio_pins = gpio_pins; + } + + +/* Print all chained list content */ +void print_all_chained_list(STRUCT_MAPPED_GPIO * head){ + STRUCT_MAPPED_GPIO * current = head; + int idx = 0; + + READ_CONF_FILE_PRINTF("Printing Chained list:\n "); + while (current != NULL) { + READ_CONF_FILE_PRINTF("Chained list Mapping GPIOs, idx: %d\n", idx); + + print_chained_list_node(current); + + idx++; + current = current->next_mapped_gpio; + } +} + + +/* Print one chained list node */ +void print_chained_list_node(STRUCT_MAPPED_GPIO * node){ + // Print Pins: + if(node->nb_simultaneous_pins > 1){ + READ_CONF_FILE_PRINTF(" %d Simultaneous pins at following idx: [", node->nb_simultaneous_pins); + int i; + for (i=0; i < node->nb_simultaneous_pins; i++){ + READ_CONF_FILE_PRINTF("%d%s", node->pins_idx[i], (i==node->nb_simultaneous_pins-1)?"]\n":","); + } + } + else{ + READ_CONF_FILE_PRINTF(" idx pin: %d\n", node->pins_idx[0]); + } + + // Type mapping and Value + if (node->type_mapping == TYPE_MAPPING_KEYBOARD){ + READ_CONF_FILE_PRINTF(" Type mapping: TYPE_MAPPING_KEYBOARD\n"); + READ_CONF_FILE_PRINTF(" Key: %d\n", node->key_value); + } + else if (node->type_mapping == TYPE_MAPPING_SHELL_COMMAND){ + READ_CONF_FILE_PRINTF(" Type mapping: TYPE_MAPPING_SHELL_COMMAND\n"); + READ_CONF_FILE_PRINTF(" Shell command: %s\n", node->shell_command); + } + + // Pin help String + if(node->pin_help_str[0]){ + READ_CONF_FILE_PRINTF(" Pins: %s\n", node->pin_help_str); + } + + // Function help String + if(node->fct_help_str[0]){ + READ_CONF_FILE_PRINTF(" Function: %s\n", node->fct_help_str); + } +} + +void print_gpios(int * valid_gpio_pins, int nb_valid_gpios){ + READ_CONF_FILE_PRINTF("GPIO pins:\n["); + for(int i = 0; i< nb_valid_gpios; i++) { + READ_CONF_FILE_PRINTF("%d%s", valid_gpio_pins[i], (i==nb_valid_gpios-1)?"]\n":","); + } +} \ No newline at end of file diff --git a/read_conf_file.h b/read_conf_file.h new file mode 100644 index 0000000..8bd87df --- /dev/null +++ b/read_conf_file.h @@ -0,0 +1,74 @@ +#ifndef _READ_CONF_FILE_H_ +#define _READ_CONF_FILE_H_ + + +/**************************************************************** +* Includes +****************************************************************/ +#include + +/**************************************************************** +* Defines +****************************************************************/ +#define MAX_NUM_GPIO 32 + +#define MAX_SIMULTANEOUS_GPIO 12 + +#define MAX_LENGTH_STR_PINS 48 +#define MAX_LENGTH_STR_TYPE 16 +#define MAX_LENGTH_STR_VALUE 256 +#define MAX_LENGTH_STR_HELP_PIN_NAME 48 +#define MAX_LENGTH_STR_HELP_PIN_FCT 256 +#define MAX_LN_LENGTH (MAX_LENGTH_STR_PINS+\ + MAX_LENGTH_STR_TYPE+\ + MAX_LENGTH_STR_VALUE+\ + MAX_LENGTH_STR_HELP_PIN_NAME+\ + MAX_LENGTH_STR_HELP_PIN_FCT) + +typedef enum{ + TYPE_MAPPING_KEYBOARD, + TYPE_MAPPING_SHELL_COMMAND, + NB_TYPE_MAPPING, +} ENUM_TYPE_MAPPING; + + +typedef enum{ + MAPPING_ARG_PINS = 0, + MAPPING_ARG_TYPE, + MAPPING_ARG_VALUE, + MAPPING_ARG_STR_HELP_PIN_NAME, + MAPPING_ARG_STR_HELP_PIN_FCT, + NB_MAPPING_ARG_NAMES, +} ENUM_MAPPING_ARGS_NAMES; + + +typedef struct{ + char name[32]; + int code; +}key_names_s; + + +typedef struct gpio_mapping_s{ + int pins_idx[MAX_SIMULTANEOUS_GPIO]; + int nb_simultaneous_pins; + ENUM_TYPE_MAPPING type_mapping; + int key_value; + char *shell_command; + char *pin_help_str; + char *fct_help_str; + struct gpio_mapping_s * next_mapped_gpio; + bool activated; +}STRUCT_MAPPED_GPIO; + + + +/**************************************************************** +* Public functions +****************************************************************/ +void get_mapping_from_conf_file(STRUCT_MAPPED_GPIO ** chained_list_mapping, + int* nb_valid_gpios, int ** valid_gpio_pins); +void print_all_chained_list(STRUCT_MAPPED_GPIO * head); +void print_chained_list_node(STRUCT_MAPPED_GPIO * node); +void print_gpios(int * valid_gpio_pins, int nb_valid_gpios); + +#endif //_READ_CONF_FILE_H_ diff --git a/uinput.c b/uinput.c new file mode 100644 index 0000000..4e1b808 --- /dev/null +++ b/uinput.c @@ -0,0 +1,230 @@ +/**** uinput.c *****************************/ +/* M. Moller 2013-01-16 */ +/* Universal RPi GPIO keyboard daemon */ +/*******************************************/ + +/* + Copyright (C) 2013 Michael Moller. + This file is part of the Universal Raspberry Pi GPIO keyboard daemon. + + This is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +//#include "config.h" +//include "daemon.h" +#include "uinput.h" + +static int sendRel(int dx, int dy); +static int sendSync(void); + +static struct input_event uidev_ev; +static int uidev_fd; +/*static keyinfo_s lastkey;*/ + +#define die(str, args...) do { \ + perror(str); \ + return(EXIT_FAILURE); \ + } while(0) + +#define DEBUG_UINPUT_PRINTF (0) +#if DEBUG_UINPUT_PRINTF + #define UINPUT_PRINTF(...) printf(__VA_ARGS__); +#else + #define UINPUT_PRINTF(...) +#endif + +int init_uinput(void) +{ + int fd; + struct uinput_user_dev uidev; + int i; + + fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + if(fd < 0) + die("/dev/uinput"); + + if(ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0) + die("error: ioctl"); + if(ioctl(fd, UI_SET_EVBIT, EV_REP) < 0) + die("error: ioctl"); + if(ioctl(fd, UI_SET_KEYBIT, BTN_LEFT) < 0) + die("error: ioctl"); + + if(ioctl(fd, UI_SET_EVBIT, EV_REL) < 0) + die("error: ioctl"); + if(ioctl(fd, UI_SET_RELBIT, REL_X) < 0) + die("error: ioctl"); + if(ioctl(fd, UI_SET_RELBIT, REL_Y) < 0) + die("error: ioctl"); + + /* don't forget to add all the keys! */ + for(i=0; i<256; i++){ + if(ioctl(fd, UI_SET_KEYBIT, i) < 0) + die("error: ioctl"); + } + + memset(&uidev, 0, sizeof(uidev)); + snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-sample"); + uidev.id.bustype = BUS_USB; + uidev.id.vendor = 0x1; + uidev.id.product = 0x1; + uidev.id.version = 1; + + /*if(write(fd, &uidev, sizeof(uidev)) < 0) + die("error: write");*/ + + if(ioctl(fd, UI_DEV_SETUP, &uidev) < 0) + die("error: ioctl"); + + if(ioctl(fd, UI_DEV_CREATE) < 0) + die("error: ioctl"); + + uidev_fd = fd; + + sleep(1); + + return 0; +} + +int test_uinput(void) +{ + int dx, dy; + int i,k; + + //srand(time(NULL)); + int op = 1; + + while(1) { + //switch(rand() % 4) { + switch(op) { + case 0: + dx = -10; + dy = -1; + break; + case 1: + dx = 10; + dy = 1; + break; + case 2: + dx = -1; + dy = 10; + break; + case 3: + dx = 1; + dy = -10; + break; + } + + /*k = send_gpio_keys(21, 1); + sendKey(k, 0);*/ + sendKey(KEY_D, 0); + + for(i = 0; i < 20; i++) { + //sendKey(KEY_D, 1); + //sendKey(KEY_D, 0); + sendRel(dx, dy); + + usleep(15000); + } + + sleep(10); + } +} + +int close_uinput(void) +{ + sleep(2); + + if(ioctl(uidev_fd, UI_DEV_DESTROY) < 0) + die("error: ioctl"); + + close(uidev_fd); + + return 0; +} + +int sendKey(int key, int value) +{ + struct input_event ie; + //memset(&uidev_ev, 0, sizeof(struct input_event)); + //gettimeofday(&uidev_ev.time, NULL); + ie.type = EV_KEY; + ie.code = key; + ie.value = value; + ie.time.tv_sec = 0; + ie.time.tv_usec = 0; + UINPUT_PRINTF("sendKey: %d = %d\n", key, value); + if(write(uidev_fd, &ie, sizeof(struct input_event)) < 0) + die("error: write"); + + sendSync(); + + return 0; +} + +int sendKeyAndStopKey(int key) +{ + sendKey(key, 1); + sendKey(key, 0); + + return 0; +} + +static int sendRel(int dx, int dy) +{ + memset(&uidev_ev, 0, sizeof(struct input_event)); + uidev_ev.type = EV_REL; + uidev_ev.code = REL_X; + uidev_ev.value = dx; + if(write(uidev_fd, &uidev_ev, sizeof(struct input_event)) < 0) + die("error: write"); + + memset(&uidev_ev, 0, sizeof(struct input_event)); + uidev_ev.type = EV_REL; + uidev_ev.code = REL_Y; + uidev_ev.value = dy; + if(write(uidev_fd, &uidev_ev, sizeof(struct input_event)) < 0) + die("error: write"); + + sendSync(); + + return 0; +} + +static int sendSync(void) +{ + UINPUT_PRINTF("sendSync\n"); + //memset(&uidev_ev, 0, sizeof(struct input_event)); + struct input_event ie; + ie.type = EV_SYN; + ie.code = SYN_REPORT; + ie.value = 0; + ie.time.tv_sec = 0; + ie.time.tv_usec = 0; + if(write(uidev_fd, &ie, sizeof(struct input_event)) < 0) + die("error: sendSync - write"); + + return 0; +} + diff --git a/uinput.h b/uinput.h new file mode 100644 index 0000000..be507e6 --- /dev/null +++ b/uinput.h @@ -0,0 +1,37 @@ +/**** uinput.h *****************************/ +/* M. Moller 2013-01-16 */ +/* Universal RPi GPIO keyboard daemon */ +/*******************************************/ + +/* + Copyright (C) 2013 Michael Moller. + This file is part of the Universal Raspberry Pi GPIO keyboard daemon. + + This is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. +*/ + +#ifndef _UINPUT_H_ +#define _UINPUT_H_ + +int init_uinput(void); +int test_uinput(void); +int close_uinput(void); +int send_gpio_keys(int gpio, int value); +int sendKey(int key, int value); +int sendKeyAndStopKey(int key); +//void get_last_key(keyinfo_s *kp); + +#endif //_UINPUT_H_