/* Copyright (C) 2020-2021 Michel Stempin Mostly inspired from the Linux kernel lists This file is part of the FunKey S 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. */ /** * @file mapping_list.c * This file contains the mapping list handling functions */ #include #include #include #include #include #include "mapping_list.h" #include "parse_config.h" #include "uinput.h" //#define DEBUG_MAPPING_LIST #define NOTICE_MAPPING_LIST #define ERROR_MAPPING_LIST #ifdef DEBUG_MAPPING_LIST #define FK_DEBUG(...) syslog(LOG_DEBUG, __VA_ARGS__); #else #define FK_DEBUG(...) #endif #ifdef NOTICE_MAPPING_LIST #define FK_NOTICE(...) syslog(LOG_NOTICE, __VA_ARGS__); #else #define FK_NOTICE(...) #endif #ifdef ERROR_MAPPING_LIST #define FK_ERROR(...) syslog(LOG_ERR, __VA_ARGS__); #else #define FK_ERROR(...) #endif /* Compute the byte offset of a structure member */ #undef offsetof #ifdef __compiler_offsetof #define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER) #else #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) #endif /* Retrieve a typed pointer to the structure containing the given member */ #define container_of(ptr, type, member) ({ \ void *__mptr = (void *)(ptr); \ ((type *)(__mptr - offsetof(type, member))); }) /* Retrieve a typed list entry containing the given node as member */ #define list_entry(ptr, type, member) \ container_of(ptr, type, member) /* Loop over the list nodes */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /* Loop over the list nodes backwards */ #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; pos != (head); pos = pos->prev) /* Loop over the nodes of a list, the node may be destroyed safely during the * process */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) /* Add a mapping between a previous and a next one */ static inline void list_add_between(struct mapping_list_t *new, struct mapping_list_t *prev, struct mapping_list_t *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /* Add a mapping to the mapping list head */ static inline void list_add(struct mapping_list_t *new, struct mapping_list_t *head) { list_add_between(new, head, head->next); } /* Add a mapping to the mapping list tail */ static inline void list_add_tail(struct mapping_list_t *new, struct mapping_list_t *head) { list_add_between(new, head->prev, head); } /* Delete a mapping between a previous and a next one */ static inline void list_delete_between(struct mapping_list_t *prev, struct mapping_list_t *next) { next->prev = prev; prev->next = next; } /* Delete a mapping from the mapping list */ static inline void list_delete(struct mapping_list_t *entry) { list_delete_between(entry->prev, entry->next); } /* Initalize a mapping list */ void init_mapping_list(mapping_list_t *list) { list->next = list; list->prev = list; } /* Clear a mapping list */ void clear_mapping_list(mapping_list_t *list) { struct mapping_list_t *p, *n; mapping_t *tmp; list_for_each_safe(p, n, list) { tmp = list_entry(p, mapping_t, mappings); list_delete(&tmp->mappings); if (tmp->type == MAPPING_COMMAND) { if (tmp->value.command != NULL) { free(tmp->value.command); } } else if (tmp->activated == true) { sendKey(tmp->value.keycode, 0); } free(tmp); } } /* Get a type pointer on the first mapping in a mapping list */ mapping_t *first_mapping(mapping_list_t *list) { return list_entry(list->next, mapping_t, mappings); } /* Get a type pointer on the next mapping in a mapping list */ mapping_t *next_mapping(mapping_t *mapping) { return list_entry(mapping->mappings.next, __typeof__(*(mapping)), mappings); } /* Check if the mapping is the last in the mapping list */ bool last_mapping(const mapping_list_t *list, const mapping_t *mapping) { return mapping->mappings.next->prev == list; } /* Allocated and insert a mapping in the mapping list */ bool insert_mapping(mapping_list_t *list, mapping_t *mapping) { mapping_t *new_mapping, *next_mapping; struct mapping_list_t *cur; new_mapping = (mapping_t *) malloc(sizeof (mapping_t)); if (new_mapping == NULL) { return false; } new_mapping->gpio_mask = mapping->gpio_mask; new_mapping->type = mapping->type; new_mapping->bit_count = mapping->bit_count; new_mapping->activated = mapping->activated; switch (mapping->type) { case MAPPING_COMMAND: new_mapping->value.command = strdup(mapping->value.command); break; case MAPPING_KEY: new_mapping->value.keycode = mapping->value.keycode; break; default: FK_ERROR("Unknown mapping type %d\n", mapping->type); return false; } /* Insert the mapping before any mapping with the same count of simultaneous * GPIOs, the list is thus kept in this order */ list_for_each(cur, list) { next_mapping = list_entry(cur, mapping_t, mappings); if (next_mapping->bit_count <= mapping->bit_count) { list_add_between(&new_mapping->mappings, cur->prev, cur); return true; } } /* By default, the ne mapping is inserted at the head position */ list_add(&new_mapping->mappings, list); return true; } /* Find a mapping in a mappining list with the exact same GPIO mask */ mapping_t *find_mapping(mapping_list_t *list, uint32_t gpio_mask) { struct mapping_list_t *cur; mapping_t *mapping; list_for_each(cur, list) { mapping = list_entry(cur, mapping_t, mappings); if (mapping->gpio_mask == gpio_mask) { return mapping; } } return NULL; } /* Remove a mapping from the mapping list */ bool remove_mapping(mapping_list_t *list, mapping_t *mapping) { struct mapping_list_t *p; mapping_t *tmp; list_for_each(p, list) { tmp = list_entry(p, mapping_t, mappings); if (tmp == mapping) { list_delete(&tmp->mappings); switch (tmp->type) { case MAPPING_COMMAND: if (tmp->value.command != NULL) { free(tmp->value.command); } break; case MAPPING_KEY: if (tmp->activated == true) { sendKey(tmp->value.keycode, 0); } break; default: FK_ERROR("Unknown mapping type %d\n", tmp->type); return false; } free(tmp); return true; } } return false; } /* Dump a mapping */ void dump_mapping(mapping_t *mapping) { int i; uint32_t gpio_mask; printf("mapping %p, prev %p, next %p\n", mapping, mapping->mappings.prev, mapping->mappings.next); printf("gpio_mask 0x%04X bit_count %d activated %s\n", mapping->gpio_mask, mapping->bit_count, mapping->activated ? "true" : "false"); printf("button%s ", mapping->bit_count == 1 ? " " : "s"); for (i = 0, gpio_mask = mapping->gpio_mask; i < MAX_NUM_GPIO; i++, gpio_mask >>= 1) { if (gpio_mask & 1) { printf("%s%s", gpio_name(i), gpio_mask == 1 ? "\n" : "+"); } } switch (mapping->type) { case MAPPING_COMMAND: printf("command \"%s\"\n", mapping->value.command); break; case MAPPING_KEY: printf("keycode %s (%d)\n", keycode_name(mapping->value.keycode), mapping->value.keycode); break; default: FK_ERROR("Unknown mapping type %d\n", mapping->type); break; } } /* Dump a mapping list */ void dump_mapping_list(mapping_list_t *list) { struct mapping_list_t *p; mapping_t *tmp; list_for_each(p, list) { tmp = list_entry(p, mapping_t, mappings); dump_mapping(tmp); printf("\n"); } } /* Save a mapping */ bool save_mapping(FILE *fp, mapping_t *mapping) { int i, length; uint32_t gpio_mask; if (fprintf(fp, "MAP ") < 0) { return false; } for (i = 0, length = 0, gpio_mask = mapping->gpio_mask; i < MAX_NUM_GPIO; i++, gpio_mask >>= 1) { if (gpio_mask & 1) { if (fprintf(fp, "%s%s", gpio_name(i), gpio_mask == 1 ? " " : "+") < 0) { return false; } length += strlen(gpio_name(i)) + 1; } } for (i = 9 - length; i > 0; i--) { if (fprintf(fp, " ") < 0) { return false; } } switch (mapping->type) { case MAPPING_COMMAND: if (fprintf(fp, "TO COMMAND %s\n", mapping->value.command) < 0) { return false; } break; case MAPPING_KEY: if (fprintf(fp, "TO KEY %s\n", keycode_name(mapping->value.keycode)) < 0) { return false; } break; default: FK_ERROR("Unknown mapping type %d\n", mapping->type); return false; break; } return true; } /* Save a mapping list */ bool save_mapping_list(const char *name, mapping_list_t *list) { struct mapping_list_t *p; mapping_t *tmp; FILE *fp; if (name[0] == '\0') { fp = stdout; } else { fp = fopen(name, "w"); FK_NOTICE("SAVE file %s\n", name); } if (fp == NULL) { FK_ERROR("Cannot open save file \"%s\": %s\n", name, strerror(errno)); return false; } fprintf(fp, "CLEAR\n"); list_for_each_prev(p, list) { tmp = list_entry(p, mapping_t, mappings); if (save_mapping(fp, tmp) == false) { FK_ERROR("Cannot write to save file \"%s\": %s\n", name, strerror(errno)); if (fp != stdout) { fclose(fp); } return false; } } if (fp == stdout) { return true; } if (fclose(fp) < 0) { FK_ERROR("Cannot close save file \"%s\": %s\n", name, strerror(errno)); return false; } return true; }