diff options
Diffstat (limited to 'tools/gpio/gpio-event-mon.c')
-rw-r--r-- | tools/gpio/gpio-event-mon.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/tools/gpio/gpio-event-mon.c b/tools/gpio/gpio-event-mon.c new file mode 100644 index 000000000..5dee2b98a --- /dev/null +++ b/tools/gpio/gpio-event-mon.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * gpio-event-mon - monitor GPIO line events from userspace + * + * Copyright (C) 2016 Linus Walleij + * + * Usage: + * gpio-event-mon -n <device-name> -o <offset> + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> +#include <poll.h> +#include <fcntl.h> +#include <getopt.h> +#include <inttypes.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <linux/gpio.h> +#include "gpio-utils.h" + +int monitor_device(const char *device_name, + unsigned int *lines, + unsigned int num_lines, + struct gpio_v2_line_config *config, + unsigned int loops) +{ + struct gpio_v2_line_values values; + char *chrdev_name; + int cfd, lfd; + int ret; + int i = 0; + + ret = asprintf(&chrdev_name, "/dev/%s", device_name); + if (ret < 0) + return -ENOMEM; + + cfd = open(chrdev_name, 0); + if (cfd == -1) { + ret = -errno; + fprintf(stderr, "Failed to open %s\n", chrdev_name); + goto exit_free_name; + } + + ret = gpiotools_request_line(device_name, lines, num_lines, config, + "gpio-event-mon"); + if (ret < 0) + goto exit_device_close; + else + lfd = ret; + + /* Read initial states */ + values.mask = 0; + values.bits = 0; + for (i = 0; i < num_lines; i++) + gpiotools_set_bit(&values.mask, i); + ret = gpiotools_get_values(lfd, &values); + if (ret < 0) { + fprintf(stderr, + "Failed to issue GPIO LINE GET VALUES IOCTL (%d)\n", + ret); + goto exit_line_close; + } + + if (num_lines == 1) { + fprintf(stdout, "Monitoring line %d on %s\n", lines[0], device_name); + fprintf(stdout, "Initial line value: %d\n", + gpiotools_test_bit(values.bits, 0)); + } else { + fprintf(stdout, "Monitoring lines %d", lines[0]); + for (i = 1; i < num_lines - 1; i++) + fprintf(stdout, ", %d", lines[i]); + fprintf(stdout, " and %d on %s\n", lines[i], device_name); + fprintf(stdout, "Initial line values: %d", + gpiotools_test_bit(values.bits, 0)); + for (i = 1; i < num_lines - 1; i++) + fprintf(stdout, ", %d", + gpiotools_test_bit(values.bits, i)); + fprintf(stdout, " and %d\n", + gpiotools_test_bit(values.bits, i)); + } + + i = 0; + while (1) { + struct gpio_v2_line_event event; + + ret = read(lfd, &event, sizeof(event)); + if (ret == -1) { + if (errno == -EAGAIN) { + fprintf(stderr, "nothing available\n"); + continue; + } else { + ret = -errno; + fprintf(stderr, "Failed to read event (%d)\n", + ret); + break; + } + } + + if (ret != sizeof(event)) { + fprintf(stderr, "Reading event failed\n"); + ret = -EIO; + break; + } + fprintf(stdout, "GPIO EVENT at %" PRIu64 " on line %d (%d|%d) ", + (uint64_t)event.timestamp_ns, event.offset, event.line_seqno, + event.seqno); + switch (event.id) { + case GPIO_V2_LINE_EVENT_RISING_EDGE: + fprintf(stdout, "rising edge"); + break; + case GPIO_V2_LINE_EVENT_FALLING_EDGE: + fprintf(stdout, "falling edge"); + break; + default: + fprintf(stdout, "unknown event"); + } + fprintf(stdout, "\n"); + + i++; + if (i == loops) + break; + } + +exit_line_close: + if (close(lfd) == -1) + perror("Failed to close line file"); +exit_device_close: + if (close(cfd) == -1) + perror("Failed to close GPIO character device file"); +exit_free_name: + free(chrdev_name); + return ret; +} + +void print_usage(void) +{ + fprintf(stderr, "Usage: gpio-event-mon [options]...\n" + "Listen to events on GPIO lines, 0->1 1->0\n" + " -n <name> Listen on GPIOs on a named device (must be stated)\n" + " -o <n> Offset of line to monitor (may be repeated)\n" + " -d Set line as open drain\n" + " -s Set line as open source\n" + " -r Listen for rising edges\n" + " -f Listen for falling edges\n" + " -w Report the wall-clock time for events\n" + " -t Report the hardware timestamp for events\n" + " -b <n> Debounce the line with period n microseconds\n" + " [-c <n>] Do <n> loops (optional, infinite loop if not stated)\n" + " -? This helptext\n" + "\n" + "Example:\n" + "gpio-event-mon -n gpiochip0 -o 4 -r -f -b 10000\n" + ); +} + +#define EDGE_FLAGS \ + (GPIO_V2_LINE_FLAG_EDGE_RISING | \ + GPIO_V2_LINE_FLAG_EDGE_FALLING) + +int main(int argc, char **argv) +{ + const char *device_name = NULL; + unsigned int lines[GPIO_V2_LINES_MAX]; + unsigned int num_lines = 0; + unsigned int loops = 0; + struct gpio_v2_line_config config; + int c, attr, i; + unsigned long debounce_period_us = 0; + + memset(&config, 0, sizeof(config)); + config.flags = GPIO_V2_LINE_FLAG_INPUT; + while ((c = getopt(argc, argv, "c:n:o:b:dsrfwt?")) != -1) { + switch (c) { + case 'c': + loops = strtoul(optarg, NULL, 10); + break; + case 'n': + device_name = optarg; + break; + case 'o': + if (num_lines >= GPIO_V2_LINES_MAX) { + print_usage(); + return -1; + } + lines[num_lines] = strtoul(optarg, NULL, 10); + num_lines++; + break; + case 'b': + debounce_period_us = strtoul(optarg, NULL, 10); + break; + case 'd': + config.flags |= GPIO_V2_LINE_FLAG_OPEN_DRAIN; + break; + case 's': + config.flags |= GPIO_V2_LINE_FLAG_OPEN_SOURCE; + break; + case 'r': + config.flags |= GPIO_V2_LINE_FLAG_EDGE_RISING; + break; + case 'f': + config.flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING; + break; + case 'w': + config.flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME; + break; + case 't': + config.flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE; + break; + case '?': + print_usage(); + return -1; + } + } + + if (debounce_period_us) { + attr = config.num_attrs; + config.num_attrs++; + for (i = 0; i < num_lines; i++) + gpiotools_set_bit(&config.attrs[attr].mask, i); + config.attrs[attr].attr.id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE; + config.attrs[attr].attr.debounce_period_us = debounce_period_us; + } + + if (!device_name || num_lines == 0) { + print_usage(); + return -1; + } + if (!(config.flags & EDGE_FLAGS)) { + printf("No flags specified, listening on both rising and " + "falling edges\n"); + config.flags |= EDGE_FLAGS; + } + return monitor_device(device_name, lines, num_lines, &config, loops); +} |