1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2018 Christian Kramer
5 * Copyright (c) 2020 Ian Lepore <ian@FreeBSD.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: stable/12/tools/test/gpioevents/gpioevents.c 372826 2022-12-29 17:03:22Z rene $
29 *
30 * make LDFLAGS+=-lgpio gpioevents
31 */
32
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <limits.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <signal.h>
40 #include <aio.h>
41 #include <string.h>
42 #include <stdbool.h>
43 #include <errno.h>
44 #include <err.h>
45
46 #include <sys/endian.h>
47 #include <sys/event.h>
48 #include <sys/poll.h>
49 #include <sys/select.h>
50 #include <sys/time.h>
51
52 #include <libgpio.h>
53
54 static bool be_verbose = false;
55 static int report_format = GPIO_EVENT_REPORT_DETAIL;
56 static struct timespec utc_offset;
57
58 static volatile sig_atomic_t sigio = 0;
59
60 static void
sigio_handler(int sig __unused)61 sigio_handler(int sig __unused){
62 sigio = 1;
63 }
64
65 static void
usage()66 usage()
67 {
68 fprintf(stderr, "usage: %s [-f ctldev] [-m method] [-s] [-n] [-S] [-u]"
69 "[-t timeout] [-d delay-usec] pin intr-config pin-mode [pin intr-config pin-mode ...]\n\n",
70 getprogname());
71 fprintf(stderr, " -d delay before each call to read/poll/select/etc\n");
72 fprintf(stderr, " -n Non-blocking IO\n");
73 fprintf(stderr, " -s Single-shot (else loop continuously)\n");
74 fprintf(stderr, " -S Report summary data (else report each event)\n");
75 fprintf(stderr, " -u Show timestamps as UTC (else monotonic time)\n");
76 fprintf(stderr, "\n");
77 fprintf(stderr, "Possible options for method:\n\n");
78 fprintf(stderr, " r\tread (default)\n");
79 fprintf(stderr, " p\tpoll\n");
80 fprintf(stderr, " s\tselect\n");
81 fprintf(stderr, " k\tkqueue\n");
82 fprintf(stderr, " a\taio_read (needs sysctl vfs.aio.enable_unsafe=1)\n");
83 fprintf(stderr, " i\tsignal-driven I/O\n\n");
84 fprintf(stderr, "Possible options for intr-config:\n\n");
85 fprintf(stderr, " no\t no interrupt\n");
86 fprintf(stderr, " er\t edge rising\n");
87 fprintf(stderr, " ef\t edge falling\n");
88 fprintf(stderr, " eb\t edge both\n\n");
89 fprintf(stderr, "Possible options for pin-mode:\n\n");
90 fprintf(stderr, " ft\t floating\n");
91 fprintf(stderr, " pd\t pull-down\n");
92 fprintf(stderr, " pu\t pull-up\n");
93 }
94
95 static void
verbose(const char * fmt,...)96 verbose(const char *fmt, ...)
97 {
98 va_list args;
99
100 if (!be_verbose)
101 return;
102
103 va_start(args, fmt);
104 vprintf(fmt, args);
105 va_end(args);
106 }
107
108 static const char*
poll_event_to_str(short event)109 poll_event_to_str(short event)
110 {
111 switch (event) {
112 case POLLIN:
113 return "POLLIN";
114 case POLLPRI:
115 return "POLLPRI:";
116 case POLLOUT:
117 return "POLLOUT:";
118 case POLLRDNORM:
119 return "POLLRDNORM";
120 case POLLRDBAND:
121 return "POLLRDBAND";
122 case POLLWRBAND:
123 return "POLLWRBAND";
124 case POLLINIGNEOF:
125 return "POLLINIGNEOF";
126 case POLLERR:
127 return "POLLERR";
128 case POLLHUP:
129 return "POLLHUP";
130 case POLLNVAL:
131 return "POLLNVAL";
132 default:
133 return "unknown event";
134 }
135 }
136
137 static void
print_poll_events(short event)138 print_poll_events(short event)
139 {
140 short curr_event = 0;
141 bool first = true;
142
143 for (size_t i = 0; i <= sizeof(short) * CHAR_BIT - 1; i++) {
144 curr_event = 1 << i;
145 if ((event & curr_event) == 0)
146 continue;
147 if (!first) {
148 printf(" | ");
149 } else {
150 first = false;
151 }
152 printf("%s", poll_event_to_str(curr_event));
153 }
154 }
155
156 static void
calc_utc_offset()157 calc_utc_offset()
158 {
159 struct timespec monotime, utctime;
160
161 clock_gettime(CLOCK_MONOTONIC, &monotime);
162 clock_gettime(CLOCK_REALTIME, &utctime);
163 timespecsub(&utctime, &monotime, &utc_offset);
164 }
165
166 static void
print_timestamp(const char * str,sbintime_t timestamp)167 print_timestamp(const char *str, sbintime_t timestamp)
168 {
169 struct timespec ts;
170 char timebuf[32];
171
172 ts = sbttots(timestamp);
173
174 if (!timespecisset(&utc_offset)) {
175 printf("%s %jd.%09ld ", str, (intmax_t)ts.tv_sec, ts.tv_nsec);
176 } else {
177 timespecadd(&utc_offset, &ts, &ts);
178 strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S",
179 gmtime(&ts.tv_sec));
180 printf("%s %s.%09ld ", str, timebuf, ts.tv_nsec);
181 }
182 }
183
184 static void
print_event_detail(const struct gpio_event_detail * det)185 print_event_detail(const struct gpio_event_detail *det)
186 {
187 print_timestamp("time", det->gp_time);
188 printf("pin %hu state %u\n", det->gp_pin, det->gp_pinstate);
189 }
190
191 static void
print_event_summary(const struct gpio_event_summary * sum)192 print_event_summary(const struct gpio_event_summary *sum)
193 {
194 print_timestamp("first_time", sum->gp_first_time);
195 print_timestamp("last_time", sum->gp_last_time);
196 printf("pin %hu count %hu first state %u last state %u\n",
197 sum->gp_pin, sum->gp_count,
198 sum->gp_first_state, sum->gp_last_state);
199 }
200
201 static void
print_gpio_event(const void * buf)202 print_gpio_event(const void *buf)
203 {
204 if (report_format == GPIO_EVENT_REPORT_DETAIL)
205 print_event_detail((const struct gpio_event_detail *)buf);
206 else
207 print_event_summary((const struct gpio_event_summary *)buf);
208 }
209
210 static void
run_read(bool loop,int handle,const char * file,u_int delayus)211 run_read(bool loop, int handle, const char *file, u_int delayus)
212 {
213 const size_t numrecs = 64;
214 union {
215 const struct gpio_event_summary sum[numrecs];
216 const struct gpio_event_detail det[numrecs];
217 uint8_t data[1];
218 } buffer;
219 ssize_t reccount, recsize, res;
220
221 if (report_format == GPIO_EVENT_REPORT_DETAIL)
222 recsize = sizeof(struct gpio_event_detail);
223 else
224 recsize = sizeof(struct gpio_event_summary);
225
226 do {
227 if (delayus != 0) {
228 verbose("sleep %f seconds before read()\n",
229 delayus / 1000000.0);
230 usleep(delayus);
231 }
232 verbose("read into %zd byte buffer\n", sizeof(buffer));
233 res = read(handle, buffer.data, sizeof(buffer));
234 if (res < 0)
235 err(EXIT_FAILURE, "Cannot read from %s", file);
236
237 if ((res % recsize) != 0) {
238 fprintf(stderr, "%s: read() %zd bytes from %s; "
239 "expected a multiple of %zu\n",
240 getprogname(), res, file, recsize);
241 } else {
242 reccount = res / recsize;
243 verbose("read returned %zd bytes; %zd events\n", res,
244 reccount);
245 for (ssize_t i = 0; i < reccount; ++i) {
246 if (report_format == GPIO_EVENT_REPORT_DETAIL)
247 print_event_detail(&buffer.det[i]);
248 else
249 print_event_summary(&buffer.sum[i]);
250 }
251 }
252 } while (loop);
253 }
254
255 static void
run_poll(bool loop,int handle,const char * file,int timeout,u_int delayus)256 run_poll(bool loop, int handle, const char *file, int timeout, u_int delayus)
257 {
258 struct pollfd fds;
259 int res;
260
261 fds.fd = handle;
262 fds.events = POLLIN | POLLRDNORM;
263 fds.revents = 0;
264
265 do {
266 if (delayus != 0) {
267 verbose("sleep %f seconds before poll()\n",
268 delayus / 1000000.0);
269 usleep(delayus);
270 }
271 res = poll(&fds, 1, timeout);
272 if (res < 0) {
273 err(EXIT_FAILURE, "Cannot poll() %s", file);
274 } else if (res == 0) {
275 printf("%s: poll() timed out on %s\n", getprogname(),
276 file);
277 } else {
278 printf("%s: poll() returned %i (revents: ",
279 getprogname(), res);
280 print_poll_events(fds.revents);
281 printf(") on %s\n", file);
282 if (fds.revents & (POLLHUP | POLLERR)) {
283 err(EXIT_FAILURE, "Recieved POLLHUP or POLLERR "
284 "on %s", file);
285 }
286 run_read(false, handle, file, 0);
287 }
288 } while (loop);
289 }
290
291 static void
run_select(bool loop,int handle,const char * file,int timeout,u_int delayus)292 run_select(bool loop, int handle, const char *file, int timeout, u_int delayus)
293 {
294 fd_set readfds;
295 struct timeval tv;
296 struct timeval *tv_ptr;
297 int res;
298
299 FD_ZERO(&readfds);
300 FD_SET(handle, &readfds);
301 if (timeout != INFTIM) {
302 tv.tv_sec = timeout / 1000;
303 tv.tv_usec = (timeout % 1000) * 1000;
304 tv_ptr = &tv;
305 } else {
306 tv_ptr = NULL;
307 }
308
309 do {
310 if (delayus != 0) {
311 verbose("sleep %f seconds before select()\n",
312 delayus / 1000000.0);
313 usleep(delayus);
314 }
315 res = select(FD_SETSIZE, &readfds, NULL, NULL, tv_ptr);
316 if (res < 0) {
317 err(EXIT_FAILURE, "Cannot select() %s", file);
318 } else if (res == 0) {
319 printf("%s: select() timed out on %s\n", getprogname(),
320 file);
321 } else {
322 printf("%s: select() returned %i on %s\n",
323 getprogname(), res, file);
324 run_read(false, handle, file, 0);
325 }
326 } while (loop);
327 }
328
329 static void
run_kqueue(bool loop,int handle,const char * file,int timeout,u_int delayus)330 run_kqueue(bool loop, int handle, const char *file, int timeout, u_int delayus)
331 {
332 struct kevent event[1];
333 struct kevent tevent[1];
334 int kq = -1;
335 int nev = -1;
336 struct timespec tv;
337 struct timespec *tv_ptr;
338
339 if (timeout != INFTIM) {
340 tv.tv_sec = timeout / 1000;
341 tv.tv_nsec = (timeout % 1000) * 10000000;
342 tv_ptr = &tv;
343 } else {
344 tv_ptr = NULL;
345 }
346
347 kq = kqueue();
348 if (kq == -1)
349 err(EXIT_FAILURE, "kqueue() %s", file);
350
351 EV_SET(&event[0], handle, EVFILT_READ, EV_ADD, 0, 0, NULL);
352 nev = kevent(kq, event, 1, NULL, 0, NULL);
353 if (nev == -1)
354 err(EXIT_FAILURE, "kevent() %s", file);
355
356 do {
357 if (delayus != 0) {
358 verbose("sleep %f seconds before kevent()\n",
359 delayus / 1000000.0);
360 usleep(delayus);
361 }
362 nev = kevent(kq, NULL, 0, tevent, 1, tv_ptr);
363 if (nev == -1) {
364 err(EXIT_FAILURE, "kevent() %s", file);
365 } else if (nev == 0) {
366 printf("%s: kevent() timed out on %s\n", getprogname(),
367 file);
368 } else {
369 printf("%s: kevent() returned %i events (flags: %d) on "
370 "%s\n", getprogname(), nev, tevent[0].flags, file);
371 if (tevent[0].flags & EV_EOF) {
372 err(EXIT_FAILURE, "Recieved EV_EOF on %s",
373 file);
374 }
375 run_read(false, handle, file, 0);
376 }
377 } while (loop);
378 }
379
380 static void
run_aio_read(bool loop,int handle,const char * file,u_int delayus)381 run_aio_read(bool loop, int handle, const char *file, u_int delayus)
382 {
383 uint8_t buffer[1024];
384 size_t recsize;
385 ssize_t res;
386 struct aiocb iocb;
387
388 /*
389 * Note that async IO to character devices is no longer allowed by
390 * default (since freebsd 11). This code is still here (for now)
391 * because you can use sysctl vfs.aio.enable_unsafe=1 to bypass the
392 * prohibition and run this code.
393 */
394
395 if (report_format == GPIO_EVENT_REPORT_DETAIL)
396 recsize = sizeof(struct gpio_event_detail);
397 else
398 recsize = sizeof(struct gpio_event_summary);
399
400 bzero(&iocb, sizeof(iocb));
401
402 iocb.aio_fildes = handle;
403 iocb.aio_nbytes = sizeof(buffer);
404 iocb.aio_offset = 0;
405 iocb.aio_buf = buffer;
406
407 do {
408 if (delayus != 0) {
409 verbose("sleep %f seconds before aio_read()\n",
410 delayus / 1000000.0);
411 usleep(delayus);
412 }
413 res = aio_read(&iocb);
414 if (res < 0)
415 err(EXIT_FAILURE, "Cannot aio_read from %s", file);
416 do {
417 res = aio_error(&iocb);
418 } while (res == EINPROGRESS);
419 if (res < 0)
420 err(EXIT_FAILURE, "aio_error on %s", file);
421 res = aio_return(&iocb);
422 if (res < 0)
423 err(EXIT_FAILURE, "aio_return on %s", file);
424 if ((res % recsize) != 0) {
425 fprintf(stderr, "%s: aio_read() %zd bytes from %s; "
426 "expected a multiple of %zu\n",
427 getprogname(), res, file, recsize);
428 } else {
429 for (ssize_t i = 0; i < res; i += recsize)
430 print_gpio_event(&buffer[i]);
431 }
432 } while (loop);
433 }
434
435
436 static void
run_sigio(bool loop,int handle,const char * file)437 run_sigio(bool loop, int handle, const char *file)
438 {
439 int res;
440 struct sigaction sigact;
441 int flags;
442 int pid;
443
444 bzero(&sigact, sizeof(sigact));
445 sigact.sa_handler = sigio_handler;
446 if (sigaction(SIGIO, &sigact, NULL) < 0)
447 err(EXIT_FAILURE, "cannot set SIGIO handler on %s", file);
448 flags = fcntl(handle, F_GETFL);
449 flags |= O_ASYNC;
450 res = fcntl(handle, F_SETFL, flags);
451 if (res < 0)
452 err(EXIT_FAILURE, "fcntl(F_SETFL) on %s", file);
453 pid = getpid();
454 res = fcntl(handle, F_SETOWN, pid);
455 if (res < 0)
456 err(EXIT_FAILURE, "fnctl(F_SETOWN) on %s", file);
457
458 do {
459 if (sigio == 1) {
460 sigio = 0;
461 printf("%s: recieved SIGIO on %s\n", getprogname(),
462 file);
463 run_read(false, handle, file, 0);
464 }
465 pause();
466 } while (loop);
467 }
468
469 int
main(int argc,char * argv[])470 main(int argc, char *argv[])
471 {
472 int ch;
473 const char *file = "/dev/gpioc0";
474 char method = 'r';
475 bool loop = true;
476 bool nonblock = false;
477 u_int delayus = 0;
478 int flags;
479 int timeout = INFTIM;
480 int handle;
481 int res;
482 gpio_config_t pin_config;
483
484 while ((ch = getopt(argc, argv, "d:f:m:sSnt:uv")) != -1) {
485 switch (ch) {
486 case 'd':
487 delayus = strtol(optarg, NULL, 10);
488 if (errno != 0) {
489 warn("Invalid delay value");
490 usage();
491 return EXIT_FAILURE;
492 }
493 break;
494 case 'f':
495 file = optarg;
496 break;
497 case 'm':
498 method = optarg[0];
499 break;
500 case 's':
501 loop = false;
502 break;
503 case 'S':
504 report_format = GPIO_EVENT_REPORT_SUMMARY;
505 break;
506 case 'n':
507 nonblock= true;
508 break;
509 case 't':
510 errno = 0;
511 timeout = strtol(optarg, NULL, 10);
512 if (errno != 0) {
513 warn("Invalid timeout value");
514 usage();
515 return EXIT_FAILURE;
516 }
517 break;
518 case 'u':
519 calc_utc_offset();
520 break;
521 case 'v':
522 be_verbose = true;
523 break;
524 default:
525 usage();
526 return EXIT_FAILURE;
527 }
528 }
529 argv += optind;
530 argc -= optind;
531
532 if (argc == 0) {
533 fprintf(stderr, "%s: No pin number specified.\n",
534 getprogname());
535 usage();
536 return EXIT_FAILURE;
537 }
538
539 if (argc == 1) {
540 fprintf(stderr, "%s: No trigger type specified.\n",
541 getprogname());
542 usage();
543 return EXIT_FAILURE;
544 }
545
546 if (argc == 1) {
547 fprintf(stderr, "%s: No trigger type specified.\n",
548 getprogname());
549 usage();
550 return EXIT_FAILURE;
551 }
552
553 if (argc % 3 != 0) {
554 fprintf(stderr, "%s: Invalid number of (pin intr-conf mode) triplets.\n",
555 getprogname());
556 usage();
557 return EXIT_FAILURE;
558 }
559
560 handle = gpio_open_device(file);
561 if (handle == GPIO_INVALID_HANDLE)
562 err(EXIT_FAILURE, "Cannot open %s", file);
563
564 if (report_format == GPIO_EVENT_REPORT_SUMMARY) {
565 struct gpio_event_config cfg =
566 {GPIO_EVENT_REPORT_SUMMARY, 0};
567
568 res = ioctl(handle, GPIOCONFIGEVENTS, &cfg);
569 if (res < 0)
570 err(EXIT_FAILURE, "GPIOCONFIGEVENTS failed on %s", file);
571 }
572
573 if (nonblock == true) {
574 flags = fcntl(handle, F_GETFL);
575 flags |= O_NONBLOCK;
576 res = fcntl(handle, F_SETFL, flags);
577 if (res < 0)
578 err(EXIT_FAILURE, "cannot set O_NONBLOCK on %s", file);
579 }
580
581 for (int i = 0; i <= argc - 3; i += 3) {
582
583 errno = 0;
584 pin_config.g_pin = strtol(argv[i], NULL, 10);
585 if (errno != 0) {
586 warn("Invalid pin number");
587 usage();
588 return EXIT_FAILURE;
589 }
590
591 if (strnlen(argv[i + 1], 2) < 2) {
592 fprintf(stderr, "%s: Invalid trigger type (argument "
593 "too short).\n", getprogname());
594 usage();
595 return EXIT_FAILURE;
596 }
597
598 switch((argv[i + 1][0] << 8) + argv[i + 1][1]) {
599 case ('n' << 8) + 'o':
600 pin_config.g_flags = GPIO_INTR_NONE;
601 break;
602 case ('e' << 8) + 'r':
603 pin_config.g_flags = GPIO_INTR_EDGE_RISING;
604 break;
605 case ('e' << 8) + 'f':
606 pin_config.g_flags = GPIO_INTR_EDGE_FALLING;
607 break;
608 case ('e' << 8) + 'b':
609 pin_config.g_flags = GPIO_INTR_EDGE_BOTH;
610 break;
611 default:
612 fprintf(stderr, "%s: Invalid trigger type.\n",
613 getprogname());
614 usage();
615 return EXIT_FAILURE;
616 }
617
618 if (strnlen(argv[i + 2], 2) < 2) {
619 fprintf(stderr, "%s: Invalid pin mode (argument "
620 "too short).\n", getprogname());
621 usage();
622 return EXIT_FAILURE;
623 }
624
625 switch((argv[i + 2][0] << 8) + argv[i + 2][1]) {
626 case ('f' << 8) + 't':
627 /* no changes to pin_config */
628 break;
629 case ('p' << 8) + 'd':
630 pin_config.g_flags |= GPIO_PIN_PULLDOWN;
631 break;
632 case ('p' << 8) + 'u':
633 pin_config.g_flags |= GPIO_PIN_PULLUP;
634 break;
635 default:
636 fprintf(stderr, "%s: Invalid pin mode.\n",
637 getprogname());
638 usage();
639 return EXIT_FAILURE;
640 }
641
642 pin_config.g_flags |= GPIO_PIN_INPUT;
643
644 res = gpio_pin_set_flags(handle, &pin_config);
645 if (res < 0)
646 err(EXIT_FAILURE, "configuration of pin %d on %s "
647 "failed (flags=%d)", pin_config.g_pin, file,
648 pin_config.g_flags);
649 }
650
651 switch (method) {
652 case 'r':
653 run_read(loop, handle, file, delayus);
654 break;
655 case 'p':
656 run_poll(loop, handle, file, timeout, delayus);
657 break;
658 case 's':
659 run_select(loop, handle, file, timeout, delayus);
660 break;
661 case 'k':
662 run_kqueue(loop, handle, file, timeout, delayus);
663 break;
664 case 'a':
665 run_aio_read(loop, handle, file, delayus);
666 break;
667 case 'i':
668 run_sigio(loop, handle, file);
669 break;
670 default:
671 fprintf(stderr, "%s: Unknown method.\n", getprogname());
672 usage();
673 return EXIT_FAILURE;
674 }
675
676 return EXIT_SUCCESS;
677 }
678