From f18d2fbdff11798cd457ac75684f7dbff6812cad Mon Sep 17 00:00:00 2001 From: Andre Kovacs Date: Sat, 9 Nov 2019 19:14:13 +0100 Subject: [PATCH] gpiodaemon basics The program read the config file and configure the IOs. Over a UNIX socket it is possible to set the configured output(s). Some little modifications was needed in the lib. Signed-off-by: Andre Kovacs --- include/gpiod.h | 16 ++ lib/core.c | 5 + lib/iter.c | 5 + tools/Makefile.am | 4 +- tools/daemonconfig | 2 + tools/gpiodaemon.c | 466 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 497 insertions(+), 1 deletion(-) create mode 100644 tools/daemonconfig create mode 100644 tools/gpiodaemon.c diff --git a/include/gpiod.h b/include/gpiod.h index 9860ea8..ad231b9 100644 --- a/include/gpiod.h +++ b/include/gpiod.h @@ -659,6 +659,13 @@ enum { }; /** + * @brief Get the line's chip name. + * @param line GPIO line object. + * @return Chip name. + */ +const char *gpiod_line_chip_name(struct gpiod_line *line) GPIOD_API; + +/** * @brief Read the GPIO line offset. * @param line GPIO line object. * @return Line offset. @@ -1321,6 +1328,15 @@ gpiod_chip_iter_next(struct gpiod_chip_iter *iter) GPIOD_API; struct gpiod_chip * gpiod_chip_iter_next_noclose(struct gpiod_chip_iter *iter) GPIOD_API; + +/** + * @brief Set offset to zero. + * @param iter The gpiochip iterator object. + */ + + +void gpiod_chip_iter_reset_offset(struct gpiod_chip_iter *iter) GPIOD_API; + /** * @brief Iterate over all GPIO chips present in the system. * @param iter An initialized GPIO chip iterator. diff --git a/lib/core.c b/lib/core.c index a04514e..fd4e17d 100644 --- a/lib/core.c +++ b/lib/core.c @@ -334,6 +334,11 @@ struct gpiod_chip *gpiod_line_get_chip(struct gpiod_line *line) return line->chip; } +const char *gpiod_line_chip_name(struct gpiod_line *line) +{ + return line->chip->name[0] == '\0' ? NULL : line->chip->name; +} + unsigned int gpiod_line_offset(struct gpiod_line *line) { return line->offset; diff --git a/lib/iter.c b/lib/iter.c index a4d883a..afaf567 100644 --- a/lib/iter.c +++ b/lib/iter.c @@ -169,3 +169,8 @@ struct gpiod_line *gpiod_line_iter_next(struct gpiod_line_iter *iter) return iter->offset < (iter->num_lines) ? iter->lines[iter->offset++] : NULL; } + +void gpiod_chip_iter_reset_offset(struct gpiod_chip_iter *iter) +{ + iter->offset = 0; +} diff --git a/tools/Makefile.am b/tools/Makefile.am index 897ff32..bbc8950 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -14,7 +14,7 @@ libtools_common_la_SOURCES = tools-common.c tools-common.h LDADD = libtools-common.la $(top_builddir)/lib/libgpiod.la -bin_PROGRAMS = gpiodetect gpioinfo gpioget gpioset gpiomon gpiofind +bin_PROGRAMS = gpiodetect gpioinfo gpioget gpioset gpiomon gpiofind gpiodaemon gpiodetect_SOURCES = gpiodetect.c @@ -28,6 +28,8 @@ gpiomon_SOURCES = gpiomon.c gpiofind_SOURCES = gpiofind.c +gpiodaemon_SOURCES = gpiodaemon.c + EXTRA_DIST = gpio-tools-test gpio-tools-test.bats if WITH_TESTS diff --git a/tools/daemonconfig b/tools/daemonconfig new file mode 100644 index 0000000..7bbc00d --- /dev/null +++ b/tools/daemonconfig @@ -0,0 +1,2 @@ +gpiochip1:PD21 direction=out,defval=0,consumer=asd; +gpiochip1:118 direction=in,consumer=asd2; diff --git a/tools/gpiodaemon.c b/tools/gpiodaemon.c new file mode 100644 index 0000000..70d9a4f --- /dev/null +++ b/tools/gpiodaemon.c @@ -0,0 +1,466 @@ +/* + * 2019 André Kovács + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCK_PATH "/var/run/gpiodaemon.sock" +#define MAX_CLIENTS 10 + +#define CONSUMER_MAX 32 + +volatile sig_atomic_t exit_flag = 0; + +void exit_program(int s) +{ + exit_flag = 1; +} + +static int match_keyword(char *kw, char *opt, + char *consumer, int *val, + struct gpiod_line_request_config *line) +{ + int rv = 0; + + if (!strcmp(kw, "direction")) { + if (!strcmp(opt, "in")) { + puts("directon set to input"); + line->request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT; + } + else if (!strcmp(opt, "out")) { + puts("directon set to output"); + line->request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; + } + else { + fprintf(stderr, "Wrong option for \"%s\"\n", kw); + rv = 1; + } + } + else if (!strcmp(kw, "defval")) { + if (atoi(opt) == 0) { + puts("default value LOW"); + *val = 0; + } + else { + puts("default value HIGH"); + *val = 1; + } + } + else if (!strcmp(kw, "consumer")) { + printf("consumer is \"%s\"\n", opt); + strncpy(consumer, opt, CONSUMER_MAX); + } + else if (!strcmp(kw, "flags")) { + if (!strcmp(opt, "open_drain")) { + line->flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN; + puts("flag open drain"); + } + else if (!strcmp(opt, "open_source")) { + line->flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE; + puts("flag open source"); + } + else if (!strcmp(opt, "active_low")) { + line->flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW; + puts("flag active low"); + } + else { + fprintf(stderr, "Wrong option for \"%s\"\n", kw); + rv = 1; + } + } + else if (!strcmp(kw, "edge")) { + if (!strcmp(opt, "rising")) { + line->request_type = GPIOD_LINE_REQUEST_EVENT_RISING_EDGE; + puts("rising edge"); + } + else if (!strcmp(opt, "falling")) { + line->request_type = GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE; + puts("falling edge"); + } + else if (!strcmp(opt, "both")) { + line->request_type = GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES; + puts("both edges"); + } + else { + fprintf(stderr, "Wrong option for \"%s\"\n", kw); + rv = 1; + } + } + else { + fprintf(stderr, "Wrong kw \"%s\"\n", kw); + rv = 1; + } + + return rv; +} + +static int set_gpio_line(const char *chip_name, int line_num, int defval, + struct gpiod_line_request_config *linecfg, + struct gpiod_chip_iter *iter, + struct gpiod_line_bulk *entries) +{ + int rv = -1, found = 0; + struct gpiod_chip *gpio_chip; + + gpiod_foreach_chip_noclose(iter, gpio_chip) { + if (!strcmp(gpiod_chip_name(gpio_chip), chip_name)) { + found = 1; + break; + } + } + gpiod_chip_iter_reset_offset(iter); + + if (found != 1) { + fprintf(stderr, "No chip found by name \"%s\"\n", chip_name); + goto out; + } else + printf("chip_name is \"%s\"\n", gpiod_chip_name(gpio_chip)); + + entries->lines[entries->num_lines] = gpiod_chip_get_line(gpio_chip, line_num); + if (entries->lines[entries->num_lines] == NULL) { + fprintf(stderr, "gpiod_chip_get_line(): %s\n", strerror(errno)); + goto out; + } + + if (!gpiod_line_is_free(entries->lines[entries->num_lines])) { + fprintf(stderr, "GPIO line (%s) is used\n", + gpiod_line_consumer(entries->lines[entries->num_lines])); + free(entries->lines[entries->num_lines]); + goto out; + } + + if (gpiod_line_request(entries->lines[entries->num_lines], linecfg, defval) < 0) { + fprintf(stderr, "gpiod_line_request(): %s\n", strerror(errno)); + free(entries->lines[entries->num_lines]); + goto out; + } + + entries->num_lines += 1; + rv = 0; + +out: + return rv; +} + +static int find_chip_name(char **lineptr, char **arri, + char *line, char *gpiochip, size_t len) +{ + while (**lineptr && **lineptr != ':') + (*lineptr)++; + + if (**lineptr == '\0') { + fprintf(stderr, "Missing gpiochip\n"); + return -1; + } + + **lineptr = '\0'; + (*lineptr)++; + *arri = *lineptr; + strncpy(gpiochip, line, len); + + return 0; +} + +static int find_pin(char **lineptr, char **arri, unsigned int *line_num) +{ + char pin[12]; + int port = -1; + + while (**lineptr && !isspace(**lineptr)) + (*lineptr)++; + + if (**lineptr == '\n') { + fprintf(stderr, "Missing pin\n"); + return -1; + } + + **lineptr = '\0'; + (*lineptr)++; + strncpy(pin, *arri, sizeof(pin)-1); + + if (sscanf(pin, "%d", line_num) != 1) { + for (int i = 'A'; i < 'Z'; i++) { + if (pin[1] == i) { + port = i - 65; + break; + } + } + + if (port < 0) { + fprintf(stderr, "No valid pin found."); + return -1; + } + else + /* Line number calculation. This is the pin identifier in the GPIO block device */ + *line_num = port * 32 + atoi(&pin[2]); + } + + return 0; +} + +static int parser(const char *config_file, + struct gpiod_chip_iter *iter, + struct gpiod_line_bulk *entries) +{ + FILE *fp; + char line[1024], property[20], keyword[20], + gpiochip[32], consumer_str[CONSUMER_MAX]; + char *lineptr, *arri; + int defval = 0; + unsigned int pin; + + struct gpiod_line_request_config config = {.flags = 0}; + + if ((fp = fopen(config_file, "r")) == NULL) { + perror("fopen()"); + return -1; + } + + bzero(property, sizeof(property)); + bzero(consumer_str, sizeof(consumer_str)); + + config.consumer = consumer_str; + + while (fgets(line, sizeof(line), fp) != NULL) { + lineptr = line; + printf("%s\n", lineptr); + + if (*lineptr == '#') + continue; + + if (line[strlen(line)-2] != ';') { + fprintf(stderr, "%s\n", "Missing semicolon or newline"); + return -1; + } + + if (find_chip_name(&lineptr, &arri, line, gpiochip, sizeof(gpiochip))) + return -1; + + if (find_pin(&lineptr, &arri, &pin)) + return -1; + + /* find keyword */ + arri = property; + while (*lineptr && *lineptr != '\n') { + if (*lineptr == ',' || *lineptr == ';') { + match_keyword(keyword, property, + consumer_str, &defval, &config); + + bzero(property, sizeof(property)); + arri = property; + } + else if (*lineptr == '=') { + strncpy(keyword, property, sizeof(keyword)); + bzero(property, sizeof(property)); + arri = property; + } + else if (isspace(*lineptr)) {} + else { + *arri = *lineptr; + arri++; + } + lineptr++; + } + + if (set_gpio_line(gpiochip, pin, defval, &config, iter, entries) < 0) { + fprintf(stderr, "Skip line: %d\n", pin); + } + config.flags = 0; + config.request_type = 0; + } + + fclose(fp); + + return 0; +} + +static int processing_client_req(char *message, struct gpiod_line_bulk *entries) +{ + char chipname[32]; + char *ptr, *start; + const char *lines_chipname; + int value = 0; + unsigned int line_num, offset; + + start = ptr = message; + + if (find_chip_name(&ptr, &start, message, chipname, sizeof(chipname))) + return -1; + printf("chip: %s\n", chipname); + + if (find_pin(&ptr, &start, &line_num)) + return -1; + printf("line: %d\n", line_num); + + if (!strcmp(ptr, "set=1\n")) + value = 1; + else if (!strcmp(ptr, "set=0\n")) + value = 0; + else { + fprintf(stderr, "Wrong property\n"); + return -1; + } + + for (size_t i = 0; i <= entries->num_lines; i++) { + offset = gpiod_line_offset(entries->lines[i]); + lines_chipname = gpiod_line_chip_name(entries->lines[i]); + + if (!strcmp(lines_chipname, chipname) && offset == line_num) { + gpiod_line_set_value(entries->lines[i], value); + break; + } + } + + return 0; +} + +int main(int argc, const char *argv[]) +{ + int client_socket[MAX_CLIENTS], master_socket, + new_socket, sd, max_sd, srv, len; + socklen_t t; + fd_set rdfs; + struct sockaddr_un local, remote; + + struct gpiod_chip *gpio_chip; + struct gpiod_chip_iter *iter; + struct gpiod_line_bulk entries = { .num_lines = 0 }; + + struct sigaction sa_exit; + + argc -= optind; + if (argc != 1) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(EXIT_FAILURE); + } + + sa_exit.sa_handler = exit_program; + sa_exit.sa_flags = 0; + sigemptyset(&sa_exit.sa_mask); + if (sigaction(SIGINT, &sa_exit, NULL) < 0) { + perror("sigaction()"); + exit(EXIT_FAILURE); + } + + exit_flag = 0; + + iter = gpiod_chip_iter_new(); + if (!iter) { + fprintf(stderr, "%s\n", "unable to access GPIO chips"); + exit(EXIT_FAILURE); + } + + if (parser(argv[1], iter, &entries) < 0) + goto out; + + /* Init socket */ + for (size_t i = 0; i < MAX_CLIENTS; i++) + client_socket[i] = 0; + + if ((master_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + perror("socket()"); + goto out; + } + + local.sun_family = AF_UNIX; + strcpy(local.sun_path, SOCK_PATH); + unlink(local.sun_path); + len = strlen(local.sun_path) + sizeof(local.sun_family); + if (bind(master_socket, (struct sockaddr *)&local, len) == -1) { + perror("bind()"); + goto out; + } + + if (listen(master_socket, 5) == -1) { + perror("listen()"); + goto out; + } + + t = sizeof(remote); + +/* + if (daemon(0,0) < 0) + fprintf(stderr, "daemon(): %s\n", strerror(errno)); +*/ + while (!exit_flag) { + FD_ZERO(&rdfs); + + FD_SET(master_socket, &rdfs); + max_sd = master_socket; + + for (size_t i = 0; i < MAX_CLIENTS; i++) { + sd = client_socket[i]; + if(sd > 0) + FD_SET(sd , &rdfs); + + if(sd > max_sd) + max_sd = sd; + } + + srv = select(max_sd + 1 , &rdfs , NULL , NULL , NULL); + + if (srv < 0) { + if (errno == EINTR) + continue; + perror("select()"); + } + /* New client */ + if (FD_ISSET(master_socket, &rdfs)) { + if ((new_socket = accept(master_socket, + (struct sockaddr *)&remote, (socklen_t*)&t)) < 0) { + perror("accept"); + } + + for (size_t i = 0; i < MAX_CLIENTS; i++) { + if(client_socket[i] == 0) { + client_socket[i] = new_socket; + break; + } + } + } + /* Incoming data from client */ + else { + for (size_t i = 0; i < MAX_CLIENTS; i++) { + sd = client_socket[i]; + char buffer[128]; + bzero(buffer, sizeof(buffer)); + + if (FD_ISSET(sd, &rdfs)) { + /* Somebody disconnected */ + if (read(sd, buffer, 127) == 0) { + close(sd); + client_socket[i] = 0; + } + else { + printf("%s", buffer); + processing_client_req(buffer, &entries); + } + } + } + } + } + + for (size_t i = 0; i < MAX_CLIENTS; i++) { + if (client_socket[i] != 0) + close(client_socket[i]); + } + close(master_socket); + + +out: + unlink(local.sun_path); + /* Creepy free but need */ + gpiod_foreach_chip(iter, gpio_chip) {} + gpiod_chip_iter_free(iter); + + return 0; +} -- 2.11.0