]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
97f69747 | 2 | /* |
925baf39 | 3 | * gpio-event-mon - monitor GPIO line events from userspace |
97f69747 LW |
4 | * |
5 | * Copyright (C) 2016 Linus Walleij | |
6 | * | |
97f69747 LW |
7 | * Usage: |
8 | * gpio-event-mon -n <device-name> -o <offset> | |
9 | */ | |
10 | ||
11 | #include <unistd.h> | |
12 | #include <stdlib.h> | |
13 | #include <stdbool.h> | |
92e70b83 | 14 | #include <stdint.h> |
97f69747 LW |
15 | #include <stdio.h> |
16 | #include <dirent.h> | |
17 | #include <errno.h> | |
18 | #include <string.h> | |
19 | #include <poll.h> | |
20 | #include <fcntl.h> | |
21 | #include <getopt.h> | |
22 | #include <inttypes.h> | |
23 | #include <sys/ioctl.h> | |
1696784e | 24 | #include <sys/types.h> |
97f69747 | 25 | #include <linux/gpio.h> |
0acda979 | 26 | #include "gpio-utils.h" |
97f69747 LW |
27 | |
28 | int monitor_device(const char *device_name, | |
62757c32 KG |
29 | unsigned int *lines, |
30 | unsigned int num_lines, | |
0acda979 | 31 | struct gpio_v2_line_config *config, |
97f69747 LW |
32 | unsigned int loops) |
33 | { | |
0acda979 | 34 | struct gpio_v2_line_values values; |
97f69747 | 35 | char *chrdev_name; |
0acda979 | 36 | int cfd, lfd; |
97f69747 LW |
37 | int ret; |
38 | int i = 0; | |
39 | ||
40 | ret = asprintf(&chrdev_name, "/dev/%s", device_name); | |
41 | if (ret < 0) | |
42 | return -ENOMEM; | |
43 | ||
0acda979 KG |
44 | cfd = open(chrdev_name, 0); |
45 | if (cfd == -1) { | |
97f69747 LW |
46 | ret = -errno; |
47 | fprintf(stderr, "Failed to open %s\n", chrdev_name); | |
df51f402 | 48 | goto exit_free_name; |
97f69747 LW |
49 | } |
50 | ||
62757c32 | 51 | ret = gpiotools_request_line(device_name, lines, num_lines, config, |
0acda979 KG |
52 | "gpio-event-mon"); |
53 | if (ret < 0) | |
54 | goto exit_device_close; | |
55 | else | |
56 | lfd = ret; | |
97f69747 LW |
57 | |
58 | /* Read initial states */ | |
62757c32 | 59 | values.mask = 0; |
0acda979 | 60 | values.bits = 0; |
62757c32 KG |
61 | for (i = 0; i < num_lines; i++) |
62 | gpiotools_set_bit(&values.mask, i); | |
0acda979 KG |
63 | ret = gpiotools_get_values(lfd, &values); |
64 | if (ret < 0) { | |
65 | fprintf(stderr, | |
66 | "Failed to issue GPIO LINE GET VALUES IOCTL (%d)\n", | |
97f69747 | 67 | ret); |
0acda979 | 68 | goto exit_line_close; |
97f69747 LW |
69 | } |
70 | ||
62757c32 KG |
71 | if (num_lines == 1) { |
72 | fprintf(stdout, "Monitoring line %d on %s\n", lines[0], device_name); | |
73 | fprintf(stdout, "Initial line value: %d\n", | |
74 | gpiotools_test_bit(values.bits, 0)); | |
75 | } else { | |
76 | fprintf(stdout, "Monitoring lines %d", lines[0]); | |
77 | for (i = 1; i < num_lines - 1; i++) | |
78 | fprintf(stdout, ", %d", lines[i]); | |
79 | fprintf(stdout, " and %d on %s\n", lines[i], device_name); | |
80 | fprintf(stdout, "Initial line values: %d", | |
81 | gpiotools_test_bit(values.bits, 0)); | |
82 | for (i = 1; i < num_lines - 1; i++) | |
83 | fprintf(stdout, ", %d", | |
84 | gpiotools_test_bit(values.bits, i)); | |
85 | fprintf(stdout, " and %d\n", | |
86 | gpiotools_test_bit(values.bits, i)); | |
87 | } | |
97f69747 LW |
88 | |
89 | while (1) { | |
0acda979 | 90 | struct gpio_v2_line_event event; |
97f69747 | 91 | |
0acda979 | 92 | ret = read(lfd, &event, sizeof(event)); |
97f69747 LW |
93 | if (ret == -1) { |
94 | if (errno == -EAGAIN) { | |
95 | fprintf(stderr, "nothing available\n"); | |
96 | continue; | |
97 | } else { | |
98 | ret = -errno; | |
99 | fprintf(stderr, "Failed to read event (%d)\n", | |
100 | ret); | |
101 | break; | |
102 | } | |
103 | } | |
104 | ||
105 | if (ret != sizeof(event)) { | |
106 | fprintf(stderr, "Reading event failed\n"); | |
107 | ret = -EIO; | |
108 | break; | |
109 | } | |
0acda979 KG |
110 | fprintf(stdout, "GPIO EVENT at %llu on line %d (%d|%d) ", |
111 | event.timestamp_ns, event.offset, event.line_seqno, | |
112 | event.seqno); | |
97f69747 | 113 | switch (event.id) { |
0acda979 | 114 | case GPIO_V2_LINE_EVENT_RISING_EDGE: |
97f69747 LW |
115 | fprintf(stdout, "rising edge"); |
116 | break; | |
0acda979 | 117 | case GPIO_V2_LINE_EVENT_FALLING_EDGE: |
97f69747 LW |
118 | fprintf(stdout, "falling edge"); |
119 | break; | |
120 | default: | |
121 | fprintf(stdout, "unknown event"); | |
122 | } | |
123 | fprintf(stdout, "\n"); | |
124 | ||
125 | i++; | |
126 | if (i == loops) | |
127 | break; | |
128 | } | |
129 | ||
0acda979 KG |
130 | exit_line_close: |
131 | if (close(lfd) == -1) | |
132 | perror("Failed to close line file"); | |
133 | exit_device_close: | |
134 | if (close(cfd) == -1) | |
97f69747 | 135 | perror("Failed to close GPIO character device file"); |
df51f402 | 136 | exit_free_name: |
97f69747 LW |
137 | free(chrdev_name); |
138 | return ret; | |
139 | } | |
140 | ||
141 | void print_usage(void) | |
142 | { | |
143 | fprintf(stderr, "Usage: gpio-event-mon [options]...\n" | |
144 | "Listen to events on GPIO lines, 0->1 1->0\n" | |
145 | " -n <name> Listen on GPIOs on a named device (must be stated)\n" | |
62757c32 | 146 | " -o <n> Offset of line to monitor (may be repeated)\n" |
97f69747 LW |
147 | " -d Set line as open drain\n" |
148 | " -s Set line as open source\n" | |
149 | " -r Listen for rising edges\n" | |
150 | " -f Listen for falling edges\n" | |
cf048e05 | 151 | " -b <n> Debounce the line with period n microseconds\n" |
97f69747 LW |
152 | " [-c <n>] Do <n> loops (optional, infinite loop if not stated)\n" |
153 | " -? This helptext\n" | |
154 | "\n" | |
155 | "Example:\n" | |
cf048e05 | 156 | "gpio-event-mon -n gpiochip0 -o 4 -r -f -b 10000\n" |
97f69747 LW |
157 | ); |
158 | } | |
159 | ||
0acda979 KG |
160 | #define EDGE_FLAGS \ |
161 | (GPIO_V2_LINE_FLAG_EDGE_RISING | \ | |
162 | GPIO_V2_LINE_FLAG_EDGE_FALLING) | |
163 | ||
97f69747 LW |
164 | int main(int argc, char **argv) |
165 | { | |
166 | const char *device_name = NULL; | |
62757c32 KG |
167 | unsigned int lines[GPIO_V2_LINES_MAX]; |
168 | unsigned int num_lines = 0; | |
97f69747 | 169 | unsigned int loops = 0; |
0acda979 | 170 | struct gpio_v2_line_config config; |
cf048e05 KG |
171 | int c, attr, i; |
172 | unsigned long debounce_period_us = 0; | |
97f69747 | 173 | |
0acda979 KG |
174 | memset(&config, 0, sizeof(config)); |
175 | config.flags = GPIO_V2_LINE_FLAG_INPUT; | |
cf048e05 | 176 | while ((c = getopt(argc, argv, "c:n:o:b:dsrf?")) != -1) { |
97f69747 LW |
177 | switch (c) { |
178 | case 'c': | |
179 | loops = strtoul(optarg, NULL, 10); | |
180 | break; | |
181 | case 'n': | |
182 | device_name = optarg; | |
183 | break; | |
184 | case 'o': | |
62757c32 KG |
185 | if (num_lines >= GPIO_V2_LINES_MAX) { |
186 | print_usage(); | |
187 | return -1; | |
188 | } | |
189 | lines[num_lines] = strtoul(optarg, NULL, 10); | |
190 | num_lines++; | |
97f69747 | 191 | break; |
cf048e05 KG |
192 | case 'b': |
193 | debounce_period_us = strtoul(optarg, NULL, 10); | |
194 | break; | |
97f69747 | 195 | case 'd': |
0acda979 | 196 | config.flags |= GPIO_V2_LINE_FLAG_OPEN_DRAIN; |
97f69747 LW |
197 | break; |
198 | case 's': | |
0acda979 | 199 | config.flags |= GPIO_V2_LINE_FLAG_OPEN_SOURCE; |
97f69747 LW |
200 | break; |
201 | case 'r': | |
0acda979 | 202 | config.flags |= GPIO_V2_LINE_FLAG_EDGE_RISING; |
97f69747 LW |
203 | break; |
204 | case 'f': | |
0acda979 | 205 | config.flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING; |
97f69747 LW |
206 | break; |
207 | case '?': | |
208 | print_usage(); | |
209 | return -1; | |
210 | } | |
211 | } | |
212 | ||
cf048e05 KG |
213 | if (debounce_period_us) { |
214 | attr = config.num_attrs; | |
215 | config.num_attrs++; | |
216 | for (i = 0; i < num_lines; i++) | |
217 | gpiotools_set_bit(&config.attrs[attr].mask, i); | |
218 | config.attrs[attr].attr.id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE; | |
219 | config.attrs[attr].attr.debounce_period_us = debounce_period_us; | |
220 | } | |
221 | ||
62757c32 | 222 | if (!device_name || num_lines == 0) { |
97f69747 LW |
223 | print_usage(); |
224 | return -1; | |
225 | } | |
0acda979 | 226 | if (!(config.flags & EDGE_FLAGS)) { |
97f69747 LW |
227 | printf("No flags specified, listening on both rising and " |
228 | "falling edges\n"); | |
0acda979 | 229 | config.flags |= EDGE_FLAGS; |
97f69747 | 230 | } |
62757c32 | 231 | return monitor_device(device_name, lines, num_lines, &config, loops); |
97f69747 | 232 | } |