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$ 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 61 sigio_handler(int sig __unused){ 62 sigio = 1; 63 } 64 65 static void 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 intr-config ...]\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"); 89 } 90 91 static void 92 verbose(const char *fmt, ...) 93 { 94 va_list args; 95 96 if (!be_verbose) 97 return; 98 99 va_start(args, fmt); 100 vprintf(fmt, args); 101 va_end(args); 102 } 103 104 static const char* 105 poll_event_to_str(short event) 106 { 107 switch (event) { 108 case POLLIN: 109 return "POLLIN"; 110 case POLLPRI: 111 return "POLLPRI:"; 112 case POLLOUT: 113 return "POLLOUT:"; 114 case POLLRDNORM: 115 return "POLLRDNORM"; 116 case POLLRDBAND: 117 return "POLLRDBAND"; 118 case POLLWRBAND: 119 return "POLLWRBAND"; 120 case POLLINIGNEOF: 121 return "POLLINIGNEOF"; 122 case POLLERR: 123 return "POLLERR"; 124 case POLLHUP: 125 return "POLLHUP"; 126 case POLLNVAL: 127 return "POLLNVAL"; 128 default: 129 return "unknown event"; 130 } 131 } 132 133 static void 134 print_poll_events(short event) 135 { 136 short curr_event = 0; 137 bool first = true; 138 139 for (size_t i = 0; i <= sizeof(short) * CHAR_BIT - 1; i++) { 140 curr_event = 1 << i; 141 if ((event & curr_event) == 0) 142 continue; 143 if (!first) { 144 printf(" | "); 145 } else { 146 first = false; 147 } 148 printf("%s", poll_event_to_str(curr_event)); 149 } 150 } 151 152 static void 153 calc_utc_offset() 154 { 155 struct timespec monotime, utctime; 156 157 clock_gettime(CLOCK_MONOTONIC, &monotime); 158 clock_gettime(CLOCK_REALTIME, &utctime); 159 timespecsub(&utctime, &monotime, &utc_offset); 160 } 161 162 static void 163 print_timestamp(const char *str, sbintime_t timestamp) 164 { 165 struct timespec ts; 166 char timebuf[32]; 167 168 ts = sbttots(timestamp); 169 170 if (!timespecisset(&utc_offset)) { 171 printf("%s %jd.%09ld ", str, (intmax_t)ts.tv_sec, ts.tv_nsec); 172 } else { 173 timespecadd(&utc_offset, &ts, &ts); 174 strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S", 175 gmtime(&ts.tv_sec)); 176 printf("%s %s.%09ld ", str, timebuf, ts.tv_nsec); 177 } 178 } 179 180 static void 181 print_event_detail(const struct gpio_event_detail *det) 182 { 183 print_timestamp("time", det->gp_time); 184 printf("pin %hu state %u\n", det->gp_pin, det->gp_pinstate); 185 } 186 187 static void 188 print_event_summary(const struct gpio_event_summary *sum) 189 { 190 print_timestamp("first_time", sum->gp_first_time); 191 print_timestamp("last_time", sum->gp_last_time); 192 printf("pin %hu count %hu first state %u last state %u\n", 193 sum->gp_pin, sum->gp_count, 194 sum->gp_first_state, sum->gp_last_state); 195 } 196 197 static void 198 print_gpio_event(const void *buf) 199 { 200 if (report_format == GPIO_EVENT_REPORT_DETAIL) 201 print_event_detail((const struct gpio_event_detail *)buf); 202 else 203 print_event_summary((const struct gpio_event_summary *)buf); 204 } 205 206 static void 207 run_read(bool loop, int handle, const char *file, u_int delayus) 208 { 209 const size_t numrecs = 64; 210 union { 211 const struct gpio_event_summary sum[numrecs]; 212 const struct gpio_event_detail det[numrecs]; 213 uint8_t data[1]; 214 } buffer; 215 ssize_t reccount, recsize, res; 216 217 if (report_format == GPIO_EVENT_REPORT_DETAIL) 218 recsize = sizeof(struct gpio_event_detail); 219 else 220 recsize = sizeof(struct gpio_event_summary); 221 222 do { 223 if (delayus != 0) { 224 verbose("sleep %f seconds before read()\n", 225 delayus / 1000000.0); 226 usleep(delayus); 227 } 228 verbose("read into %zd byte buffer\n", sizeof(buffer)); 229 res = read(handle, buffer.data, sizeof(buffer)); 230 if (res < 0) 231 err(EXIT_FAILURE, "Cannot read from %s", file); 232 233 if ((res % recsize) != 0) { 234 fprintf(stderr, "%s: read() %zd bytes from %s; " 235 "expected a multiple of %zu\n", 236 getprogname(), res, file, recsize); 237 } else { 238 reccount = res / recsize; 239 verbose("read returned %zd bytes; %zd events\n", res, 240 reccount); 241 for (ssize_t i = 0; i < reccount; ++i) { 242 if (report_format == GPIO_EVENT_REPORT_DETAIL) 243 print_event_detail(&buffer.det[i]); 244 else 245 print_event_summary(&buffer.sum[i]); 246 } 247 } 248 } while (loop); 249 } 250 251 static void 252 run_poll(bool loop, int handle, const char *file, int timeout, u_int delayus) 253 { 254 struct pollfd fds; 255 int res; 256 257 fds.fd = handle; 258 fds.events = POLLIN | POLLRDNORM; 259 fds.revents = 0; 260 261 do { 262 if (delayus != 0) { 263 verbose("sleep %f seconds before poll()\n", 264 delayus / 1000000.0); 265 usleep(delayus); 266 } 267 res = poll(&fds, 1, timeout); 268 if (res < 0) { 269 err(EXIT_FAILURE, "Cannot poll() %s", file); 270 } else if (res == 0) { 271 printf("%s: poll() timed out on %s\n", getprogname(), 272 file); 273 } else { 274 printf("%s: poll() returned %i (revents: ", 275 getprogname(), res); 276 print_poll_events(fds.revents); 277 printf(") on %s\n", file); 278 if (fds.revents & (POLLHUP | POLLERR)) { 279 err(EXIT_FAILURE, "Recieved POLLHUP or POLLERR " 280 "on %s", file); 281 } 282 run_read(false, handle, file, 0); 283 } 284 } while (loop); 285 } 286 287 static void 288 run_select(bool loop, int handle, const char *file, int timeout, u_int delayus) 289 { 290 fd_set readfds; 291 struct timeval tv; 292 struct timeval *tv_ptr; 293 int res; 294 295 FD_ZERO(&readfds); 296 FD_SET(handle, &readfds); 297 if (timeout != INFTIM) { 298 tv.tv_sec = timeout / 1000; 299 tv.tv_usec = (timeout % 1000) * 1000; 300 tv_ptr = &tv; 301 } else { 302 tv_ptr = NULL; 303 } 304 305 do { 306 if (delayus != 0) { 307 verbose("sleep %f seconds before select()\n", 308 delayus / 1000000.0); 309 usleep(delayus); 310 } 311 res = select(FD_SETSIZE, &readfds, NULL, NULL, tv_ptr); 312 if (res < 0) { 313 err(EXIT_FAILURE, "Cannot select() %s", file); 314 } else if (res == 0) { 315 printf("%s: select() timed out on %s\n", getprogname(), 316 file); 317 } else { 318 printf("%s: select() returned %i on %s\n", 319 getprogname(), res, file); 320 run_read(false, handle, file, 0); 321 } 322 } while (loop); 323 } 324 325 static void 326 run_kqueue(bool loop, int handle, const char *file, int timeout, u_int delayus) 327 { 328 struct kevent event[1]; 329 struct kevent tevent[1]; 330 int kq = -1; 331 int nev = -1; 332 struct timespec tv; 333 struct timespec *tv_ptr; 334 335 if (timeout != INFTIM) { 336 tv.tv_sec = timeout / 1000; 337 tv.tv_nsec = (timeout % 1000) * 10000000; 338 tv_ptr = &tv; 339 } else { 340 tv_ptr = NULL; 341 } 342 343 kq = kqueue(); 344 if (kq == -1) 345 err(EXIT_FAILURE, "kqueue() %s", file); 346 347 EV_SET(&event[0], handle, EVFILT_READ, EV_ADD, 0, 0, NULL); 348 nev = kevent(kq, event, 1, NULL, 0, NULL); 349 if (nev == -1) 350 err(EXIT_FAILURE, "kevent() %s", file); 351 352 do { 353 if (delayus != 0) { 354 verbose("sleep %f seconds before kevent()\n", 355 delayus / 1000000.0); 356 usleep(delayus); 357 } 358 nev = kevent(kq, NULL, 0, tevent, 1, tv_ptr); 359 if (nev == -1) { 360 err(EXIT_FAILURE, "kevent() %s", file); 361 } else if (nev == 0) { 362 printf("%s: kevent() timed out on %s\n", getprogname(), 363 file); 364 } else { 365 printf("%s: kevent() returned %i events (flags: %d) on " 366 "%s\n", getprogname(), nev, tevent[0].flags, file); 367 if (tevent[0].flags & EV_EOF) { 368 err(EXIT_FAILURE, "Recieved EV_EOF on %s", 369 file); 370 } 371 run_read(false, handle, file, 0); 372 } 373 } while (loop); 374 } 375 376 static void 377 run_aio_read(bool loop, int handle, const char *file, u_int delayus) 378 { 379 uint8_t buffer[1024]; 380 size_t recsize; 381 ssize_t res; 382 struct aiocb iocb; 383 384 /* 385 * Note that async IO to character devices is no longer allowed by 386 * default (since freebsd 11). This code is still here (for now) 387 * because you can use sysctl vfs.aio.enable_unsafe=1 to bypass the 388 * prohibition and run this code. 389 */ 390 391 if (report_format == GPIO_EVENT_REPORT_DETAIL) 392 recsize = sizeof(struct gpio_event_detail); 393 else 394 recsize = sizeof(struct gpio_event_summary); 395 396 bzero(&iocb, sizeof(iocb)); 397 398 iocb.aio_fildes = handle; 399 iocb.aio_nbytes = sizeof(buffer); 400 iocb.aio_offset = 0; 401 iocb.aio_buf = buffer; 402 403 do { 404 if (delayus != 0) { 405 verbose("sleep %f seconds before aio_read()\n", 406 delayus / 1000000.0); 407 usleep(delayus); 408 } 409 res = aio_read(&iocb); 410 if (res < 0) 411 err(EXIT_FAILURE, "Cannot aio_read from %s", file); 412 do { 413 res = aio_error(&iocb); 414 } while (res == EINPROGRESS); 415 if (res < 0) 416 err(EXIT_FAILURE, "aio_error on %s", file); 417 res = aio_return(&iocb); 418 if (res < 0) 419 err(EXIT_FAILURE, "aio_return on %s", file); 420 if ((res % recsize) != 0) { 421 fprintf(stderr, "%s: aio_read() %zd bytes from %s; " 422 "expected a multiple of %zu\n", 423 getprogname(), res, file, recsize); 424 } else { 425 for (ssize_t i = 0; i < res; i += recsize) 426 print_gpio_event(&buffer[i]); 427 } 428 } while (loop); 429 } 430 431 432 static void 433 run_sigio(bool loop, int handle, const char *file) 434 { 435 int res; 436 struct sigaction sigact; 437 int flags; 438 int pid; 439 440 bzero(&sigact, sizeof(sigact)); 441 sigact.sa_handler = sigio_handler; 442 if (sigaction(SIGIO, &sigact, NULL) < 0) 443 err(EXIT_FAILURE, "cannot set SIGIO handler on %s", file); 444 flags = fcntl(handle, F_GETFL); 445 flags |= O_ASYNC; 446 res = fcntl(handle, F_SETFL, flags); 447 if (res < 0) 448 err(EXIT_FAILURE, "fcntl(F_SETFL) on %s", file); 449 pid = getpid(); 450 res = fcntl(handle, F_SETOWN, pid); 451 if (res < 0) 452 err(EXIT_FAILURE, "fnctl(F_SETOWN) on %s", file); 453 454 do { 455 if (sigio == 1) { 456 sigio = 0; 457 printf("%s: recieved SIGIO on %s\n", getprogname(), 458 file); 459 run_read(false, handle, file, 0); 460 } 461 pause(); 462 } while (loop); 463 } 464 465 int 466 main(int argc, char *argv[]) 467 { 468 int ch; 469 const char *file = "/dev/gpioc0"; 470 char method = 'r'; 471 bool loop = true; 472 bool nonblock = false; 473 u_int delayus = 0; 474 int flags; 475 int timeout = INFTIM; 476 int handle; 477 int res; 478 gpio_config_t pin_config; 479 480 while ((ch = getopt(argc, argv, "d:f:m:sSnt:uv")) != -1) { 481 switch (ch) { 482 case 'd': 483 delayus = strtol(optarg, NULL, 10); 484 if (errno != 0) { 485 warn("Invalid delay value"); 486 usage(); 487 return EXIT_FAILURE; 488 } 489 break; 490 case 'f': 491 file = optarg; 492 break; 493 case 'm': 494 method = optarg[0]; 495 break; 496 case 's': 497 loop = false; 498 break; 499 case 'S': 500 report_format = GPIO_EVENT_REPORT_SUMMARY; 501 break; 502 case 'n': 503 nonblock= true; 504 break; 505 case 't': 506 errno = 0; 507 timeout = strtol(optarg, NULL, 10); 508 if (errno != 0) { 509 warn("Invalid timeout value"); 510 usage(); 511 return EXIT_FAILURE; 512 } 513 break; 514 case 'u': 515 calc_utc_offset(); 516 break; 517 case 'v': 518 be_verbose = true; 519 break; 520 default: 521 usage(); 522 return EXIT_FAILURE; 523 } 524 } 525 argv += optind; 526 argc -= optind; 527 528 if (argc == 0) { 529 fprintf(stderr, "%s: No pin number specified.\n", 530 getprogname()); 531 usage(); 532 return EXIT_FAILURE; 533 } 534 535 if (argc == 1) { 536 fprintf(stderr, "%s: No trigger type specified.\n", 537 getprogname()); 538 usage(); 539 return EXIT_FAILURE; 540 } 541 542 if (argc % 2 == 1) { 543 fprintf(stderr, "%s: Invalid number of pin intr-conf pairs.\n", 544 getprogname()); 545 usage(); 546 return EXIT_FAILURE; 547 } 548 549 handle = gpio_open_device(file); 550 if (handle == GPIO_INVALID_HANDLE) 551 err(EXIT_FAILURE, "Cannot open %s", file); 552 553 if (report_format == GPIO_EVENT_REPORT_SUMMARY) { 554 struct gpio_event_config cfg = 555 {GPIO_EVENT_REPORT_SUMMARY, 0}; 556 557 res = ioctl(handle, GPIOCONFIGEVENTS, &cfg); 558 if (res < 0) 559 err(EXIT_FAILURE, "GPIOCONFIGEVENTS failed on %s", file); 560 } 561 562 if (nonblock == true) { 563 flags = fcntl(handle, F_GETFL); 564 flags |= O_NONBLOCK; 565 res = fcntl(handle, F_SETFL, flags); 566 if (res < 0) 567 err(EXIT_FAILURE, "cannot set O_NONBLOCK on %s", file); 568 } 569 570 for (int i = 0; i <= argc - 2; i += 2) { 571 572 errno = 0; 573 pin_config.g_pin = strtol(argv[i], NULL, 10); 574 if (errno != 0) { 575 warn("Invalid pin number"); 576 usage(); 577 return EXIT_FAILURE; 578 } 579 580 if (strnlen(argv[i + 1], 2) < 2) { 581 fprintf(stderr, "%s: Invalid trigger type (argument " 582 "too short).\n", getprogname()); 583 usage(); 584 return EXIT_FAILURE; 585 } 586 587 switch((argv[i + 1][0] << 8) + argv[i + 1][1]) { 588 case ('n' << 8) + 'o': 589 pin_config.g_flags = GPIO_INTR_NONE; 590 break; 591 case ('e' << 8) + 'r': 592 pin_config.g_flags = GPIO_INTR_EDGE_RISING; 593 break; 594 case ('e' << 8) + 'f': 595 pin_config.g_flags = GPIO_INTR_EDGE_FALLING; 596 break; 597 case ('e' << 8) + 'b': 598 pin_config.g_flags = GPIO_INTR_EDGE_BOTH; 599 break; 600 default: 601 fprintf(stderr, "%s: Invalid trigger type.\n", 602 getprogname()); 603 usage(); 604 return EXIT_FAILURE; 605 } 606 607 pin_config.g_flags |= GPIO_PIN_INPUT | GPIO_PIN_PULLUP; 608 609 res = gpio_pin_set_flags(handle, &pin_config); 610 if (res < 0) 611 err(EXIT_FAILURE, "configuration of pin %d on %s " 612 "failed (flags=%d)", pin_config.g_pin, file, 613 pin_config.g_flags); 614 } 615 616 switch (method) { 617 case 'r': 618 run_read(loop, handle, file, delayus); 619 break; 620 case 'p': 621 run_poll(loop, handle, file, timeout, delayus); 622 break; 623 case 's': 624 run_select(loop, handle, file, timeout, delayus); 625 break; 626 case 'k': 627 run_kqueue(loop, handle, file, timeout, delayus); 628 break; 629 case 'a': 630 run_aio_read(loop, handle, file, delayus); 631 break; 632 case 'i': 633 run_sigio(loop, handle, file); 634 break; 635 default: 636 fprintf(stderr, "%s: Unknown method.\n", getprogname()); 637 usage(); 638 return EXIT_FAILURE; 639 } 640 641 return EXIT_SUCCESS; 642 } 643