18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * gpio-event-mon - monitor GPIO line events from userspace 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Linus Walleij 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Usage: 88c2ecf20Sopenharmony_ci * gpio-event-mon -n <device-name> -o <offset> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <unistd.h> 128c2ecf20Sopenharmony_ci#include <stdlib.h> 138c2ecf20Sopenharmony_ci#include <stdbool.h> 148c2ecf20Sopenharmony_ci#include <stdint.h> 158c2ecf20Sopenharmony_ci#include <stdio.h> 168c2ecf20Sopenharmony_ci#include <dirent.h> 178c2ecf20Sopenharmony_ci#include <errno.h> 188c2ecf20Sopenharmony_ci#include <string.h> 198c2ecf20Sopenharmony_ci#include <poll.h> 208c2ecf20Sopenharmony_ci#include <fcntl.h> 218c2ecf20Sopenharmony_ci#include <getopt.h> 228c2ecf20Sopenharmony_ci#include <inttypes.h> 238c2ecf20Sopenharmony_ci#include <sys/ioctl.h> 248c2ecf20Sopenharmony_ci#include <sys/types.h> 258c2ecf20Sopenharmony_ci#include <linux/gpio.h> 268c2ecf20Sopenharmony_ci#include "gpio-utils.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ciint monitor_device(const char *device_name, 298c2ecf20Sopenharmony_ci unsigned int *lines, 308c2ecf20Sopenharmony_ci unsigned int num_lines, 318c2ecf20Sopenharmony_ci struct gpio_v2_line_config *config, 328c2ecf20Sopenharmony_ci unsigned int loops) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct gpio_v2_line_values values; 358c2ecf20Sopenharmony_ci char *chrdev_name; 368c2ecf20Sopenharmony_ci int cfd, lfd; 378c2ecf20Sopenharmony_ci int ret; 388c2ecf20Sopenharmony_ci int i = 0; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci ret = asprintf(&chrdev_name, "/dev/%s", device_name); 418c2ecf20Sopenharmony_ci if (ret < 0) 428c2ecf20Sopenharmony_ci return -ENOMEM; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci cfd = open(chrdev_name, 0); 458c2ecf20Sopenharmony_ci if (cfd == -1) { 468c2ecf20Sopenharmony_ci ret = -errno; 478c2ecf20Sopenharmony_ci fprintf(stderr, "Failed to open %s\n", chrdev_name); 488c2ecf20Sopenharmony_ci goto exit_free_name; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci ret = gpiotools_request_line(device_name, lines, num_lines, config, 528c2ecf20Sopenharmony_ci "gpio-event-mon"); 538c2ecf20Sopenharmony_ci if (ret < 0) 548c2ecf20Sopenharmony_ci goto exit_device_close; 558c2ecf20Sopenharmony_ci else 568c2ecf20Sopenharmony_ci lfd = ret; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* Read initial states */ 598c2ecf20Sopenharmony_ci values.mask = 0; 608c2ecf20Sopenharmony_ci values.bits = 0; 618c2ecf20Sopenharmony_ci for (i = 0; i < num_lines; i++) 628c2ecf20Sopenharmony_ci gpiotools_set_bit(&values.mask, i); 638c2ecf20Sopenharmony_ci ret = gpiotools_get_values(lfd, &values); 648c2ecf20Sopenharmony_ci if (ret < 0) { 658c2ecf20Sopenharmony_ci fprintf(stderr, 668c2ecf20Sopenharmony_ci "Failed to issue GPIO LINE GET VALUES IOCTL (%d)\n", 678c2ecf20Sopenharmony_ci ret); 688c2ecf20Sopenharmony_ci goto exit_line_close; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (num_lines == 1) { 728c2ecf20Sopenharmony_ci fprintf(stdout, "Monitoring line %d on %s\n", lines[0], device_name); 738c2ecf20Sopenharmony_ci fprintf(stdout, "Initial line value: %d\n", 748c2ecf20Sopenharmony_ci gpiotools_test_bit(values.bits, 0)); 758c2ecf20Sopenharmony_ci } else { 768c2ecf20Sopenharmony_ci fprintf(stdout, "Monitoring lines %d", lines[0]); 778c2ecf20Sopenharmony_ci for (i = 1; i < num_lines - 1; i++) 788c2ecf20Sopenharmony_ci fprintf(stdout, ", %d", lines[i]); 798c2ecf20Sopenharmony_ci fprintf(stdout, " and %d on %s\n", lines[i], device_name); 808c2ecf20Sopenharmony_ci fprintf(stdout, "Initial line values: %d", 818c2ecf20Sopenharmony_ci gpiotools_test_bit(values.bits, 0)); 828c2ecf20Sopenharmony_ci for (i = 1; i < num_lines - 1; i++) 838c2ecf20Sopenharmony_ci fprintf(stdout, ", %d", 848c2ecf20Sopenharmony_ci gpiotools_test_bit(values.bits, i)); 858c2ecf20Sopenharmony_ci fprintf(stdout, " and %d\n", 868c2ecf20Sopenharmony_ci gpiotools_test_bit(values.bits, i)); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci i = 0; 908c2ecf20Sopenharmony_ci while (1) { 918c2ecf20Sopenharmony_ci struct gpio_v2_line_event event; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ret = read(lfd, &event, sizeof(event)); 948c2ecf20Sopenharmony_ci if (ret == -1) { 958c2ecf20Sopenharmony_ci if (errno == -EAGAIN) { 968c2ecf20Sopenharmony_ci fprintf(stderr, "nothing available\n"); 978c2ecf20Sopenharmony_ci continue; 988c2ecf20Sopenharmony_ci } else { 998c2ecf20Sopenharmony_ci ret = -errno; 1008c2ecf20Sopenharmony_ci fprintf(stderr, "Failed to read event (%d)\n", 1018c2ecf20Sopenharmony_ci ret); 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (ret != sizeof(event)) { 1078c2ecf20Sopenharmony_ci fprintf(stderr, "Reading event failed\n"); 1088c2ecf20Sopenharmony_ci ret = -EIO; 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci fprintf(stdout, "GPIO EVENT at %" PRIu64 " on line %d (%d|%d) ", 1128c2ecf20Sopenharmony_ci (uint64_t)event.timestamp_ns, event.offset, event.line_seqno, 1138c2ecf20Sopenharmony_ci event.seqno); 1148c2ecf20Sopenharmony_ci switch (event.id) { 1158c2ecf20Sopenharmony_ci case GPIO_V2_LINE_EVENT_RISING_EDGE: 1168c2ecf20Sopenharmony_ci fprintf(stdout, "rising edge"); 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci case GPIO_V2_LINE_EVENT_FALLING_EDGE: 1198c2ecf20Sopenharmony_ci fprintf(stdout, "falling edge"); 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci default: 1228c2ecf20Sopenharmony_ci fprintf(stdout, "unknown event"); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci fprintf(stdout, "\n"); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci i++; 1278c2ecf20Sopenharmony_ci if (i == loops) 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciexit_line_close: 1328c2ecf20Sopenharmony_ci if (close(lfd) == -1) 1338c2ecf20Sopenharmony_ci perror("Failed to close line file"); 1348c2ecf20Sopenharmony_ciexit_device_close: 1358c2ecf20Sopenharmony_ci if (close(cfd) == -1) 1368c2ecf20Sopenharmony_ci perror("Failed to close GPIO character device file"); 1378c2ecf20Sopenharmony_ciexit_free_name: 1388c2ecf20Sopenharmony_ci free(chrdev_name); 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_civoid print_usage(void) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci fprintf(stderr, "Usage: gpio-event-mon [options]...\n" 1458c2ecf20Sopenharmony_ci "Listen to events on GPIO lines, 0->1 1->0\n" 1468c2ecf20Sopenharmony_ci " -n <name> Listen on GPIOs on a named device (must be stated)\n" 1478c2ecf20Sopenharmony_ci " -o <n> Offset of line to monitor (may be repeated)\n" 1488c2ecf20Sopenharmony_ci " -d Set line as open drain\n" 1498c2ecf20Sopenharmony_ci " -s Set line as open source\n" 1508c2ecf20Sopenharmony_ci " -r Listen for rising edges\n" 1518c2ecf20Sopenharmony_ci " -f Listen for falling edges\n" 1528c2ecf20Sopenharmony_ci " -b <n> Debounce the line with period n microseconds\n" 1538c2ecf20Sopenharmony_ci " [-c <n>] Do <n> loops (optional, infinite loop if not stated)\n" 1548c2ecf20Sopenharmony_ci " -? This helptext\n" 1558c2ecf20Sopenharmony_ci "\n" 1568c2ecf20Sopenharmony_ci "Example:\n" 1578c2ecf20Sopenharmony_ci "gpio-event-mon -n gpiochip0 -o 4 -r -f -b 10000\n" 1588c2ecf20Sopenharmony_ci ); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci#define EDGE_FLAGS \ 1628c2ecf20Sopenharmony_ci (GPIO_V2_LINE_FLAG_EDGE_RISING | \ 1638c2ecf20Sopenharmony_ci GPIO_V2_LINE_FLAG_EDGE_FALLING) 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ciint main(int argc, char **argv) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci const char *device_name = NULL; 1688c2ecf20Sopenharmony_ci unsigned int lines[GPIO_V2_LINES_MAX]; 1698c2ecf20Sopenharmony_ci unsigned int num_lines = 0; 1708c2ecf20Sopenharmony_ci unsigned int loops = 0; 1718c2ecf20Sopenharmony_ci struct gpio_v2_line_config config; 1728c2ecf20Sopenharmony_ci int c, attr, i; 1738c2ecf20Sopenharmony_ci unsigned long debounce_period_us = 0; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci memset(&config, 0, sizeof(config)); 1768c2ecf20Sopenharmony_ci config.flags = GPIO_V2_LINE_FLAG_INPUT; 1778c2ecf20Sopenharmony_ci while ((c = getopt(argc, argv, "c:n:o:b:dsrf?")) != -1) { 1788c2ecf20Sopenharmony_ci switch (c) { 1798c2ecf20Sopenharmony_ci case 'c': 1808c2ecf20Sopenharmony_ci loops = strtoul(optarg, NULL, 10); 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci case 'n': 1838c2ecf20Sopenharmony_ci device_name = optarg; 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci case 'o': 1868c2ecf20Sopenharmony_ci if (num_lines >= GPIO_V2_LINES_MAX) { 1878c2ecf20Sopenharmony_ci print_usage(); 1888c2ecf20Sopenharmony_ci return -1; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci lines[num_lines] = strtoul(optarg, NULL, 10); 1918c2ecf20Sopenharmony_ci num_lines++; 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci case 'b': 1948c2ecf20Sopenharmony_ci debounce_period_us = strtoul(optarg, NULL, 10); 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case 'd': 1978c2ecf20Sopenharmony_ci config.flags |= GPIO_V2_LINE_FLAG_OPEN_DRAIN; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case 's': 2008c2ecf20Sopenharmony_ci config.flags |= GPIO_V2_LINE_FLAG_OPEN_SOURCE; 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci case 'r': 2038c2ecf20Sopenharmony_ci config.flags |= GPIO_V2_LINE_FLAG_EDGE_RISING; 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci case 'f': 2068c2ecf20Sopenharmony_ci config.flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING; 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci case '?': 2098c2ecf20Sopenharmony_ci print_usage(); 2108c2ecf20Sopenharmony_ci return -1; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (debounce_period_us) { 2158c2ecf20Sopenharmony_ci attr = config.num_attrs; 2168c2ecf20Sopenharmony_ci config.num_attrs++; 2178c2ecf20Sopenharmony_ci for (i = 0; i < num_lines; i++) 2188c2ecf20Sopenharmony_ci gpiotools_set_bit(&config.attrs[attr].mask, i); 2198c2ecf20Sopenharmony_ci config.attrs[attr].attr.id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE; 2208c2ecf20Sopenharmony_ci config.attrs[attr].attr.debounce_period_us = debounce_period_us; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (!device_name || num_lines == 0) { 2248c2ecf20Sopenharmony_ci print_usage(); 2258c2ecf20Sopenharmony_ci return -1; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci if (!(config.flags & EDGE_FLAGS)) { 2288c2ecf20Sopenharmony_ci printf("No flags specified, listening on both rising and " 2298c2ecf20Sopenharmony_ci "falling edges\n"); 2308c2ecf20Sopenharmony_ci config.flags |= EDGE_FLAGS; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci return monitor_device(device_name, lines, num_lines, &config, loops); 2338c2ecf20Sopenharmony_ci} 234