2020-12-12 19:34:15 +01:00
|
|
|
/*-
|
2023-05-10 17:40:58 +02:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-12-12 19:34:15 +01:00
|
|
|
*
|
|
|
|
* Copyright (c) 2018 Christian Kramer
|
|
|
|
* Copyright (c) 2020 Ian Lepore <ian@FreeBSD.org>
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* make LDFLAGS+=-lgpio gpioevents
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <aio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <err.h>
|
|
|
|
|
|
|
|
#include <sys/endian.h>
|
|
|
|
#include <sys/event.h>
|
|
|
|
#include <sys/poll.h>
|
|
|
|
#include <sys/select.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
|
|
#include <libgpio.h>
|
|
|
|
|
|
|
|
static bool be_verbose = false;
|
|
|
|
static int report_format = GPIO_EVENT_REPORT_DETAIL;
|
|
|
|
static struct timespec utc_offset;
|
|
|
|
|
|
|
|
static volatile sig_atomic_t sigio = 0;
|
|
|
|
|
|
|
|
static void
|
|
|
|
sigio_handler(int sig __unused){
|
|
|
|
sigio = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2024-03-02 16:38:04 +01:00
|
|
|
usage(void)
|
2020-12-12 19:34:15 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "usage: %s [-f ctldev] [-m method] [-s] [-n] [-S] [-u]"
|
2022-12-21 22:24:11 +01:00
|
|
|
"[-t timeout] [-d delay-usec] pin intr-config pin-mode [pin intr-config pin-mode ...]\n\n",
|
2020-12-12 19:34:15 +01:00
|
|
|
getprogname());
|
|
|
|
fprintf(stderr, " -d delay before each call to read/poll/select/etc\n");
|
|
|
|
fprintf(stderr, " -n Non-blocking IO\n");
|
|
|
|
fprintf(stderr, " -s Single-shot (else loop continuously)\n");
|
|
|
|
fprintf(stderr, " -S Report summary data (else report each event)\n");
|
|
|
|
fprintf(stderr, " -u Show timestamps as UTC (else monotonic time)\n");
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
fprintf(stderr, "Possible options for method:\n\n");
|
|
|
|
fprintf(stderr, " r\tread (default)\n");
|
|
|
|
fprintf(stderr, " p\tpoll\n");
|
|
|
|
fprintf(stderr, " s\tselect\n");
|
|
|
|
fprintf(stderr, " k\tkqueue\n");
|
|
|
|
fprintf(stderr, " a\taio_read (needs sysctl vfs.aio.enable_unsafe=1)\n");
|
|
|
|
fprintf(stderr, " i\tsignal-driven I/O\n\n");
|
|
|
|
fprintf(stderr, "Possible options for intr-config:\n\n");
|
|
|
|
fprintf(stderr, " no\t no interrupt\n");
|
|
|
|
fprintf(stderr, " er\t edge rising\n");
|
|
|
|
fprintf(stderr, " ef\t edge falling\n");
|
2022-12-21 22:24:11 +01:00
|
|
|
fprintf(stderr, " eb\t edge both\n\n");
|
2022-12-22 11:16:32 +01:00
|
|
|
fprintf(stderr, "Possible options for pin-mode:\n\n");
|
|
|
|
fprintf(stderr, " ft\t floating\n");
|
|
|
|
fprintf(stderr, " pd\t pull-down\n");
|
|
|
|
fprintf(stderr, " pu\t pull-up\n");
|
2020-12-12 19:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
verbose(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
if (!be_verbose)
|
|
|
|
return;
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
vprintf(fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char*
|
|
|
|
poll_event_to_str(short event)
|
|
|
|
{
|
|
|
|
switch (event) {
|
|
|
|
case POLLIN:
|
|
|
|
return "POLLIN";
|
|
|
|
case POLLPRI:
|
|
|
|
return "POLLPRI:";
|
|
|
|
case POLLOUT:
|
|
|
|
return "POLLOUT:";
|
|
|
|
case POLLRDNORM:
|
|
|
|
return "POLLRDNORM";
|
|
|
|
case POLLRDBAND:
|
|
|
|
return "POLLRDBAND";
|
|
|
|
case POLLWRBAND:
|
|
|
|
return "POLLWRBAND";
|
|
|
|
case POLLINIGNEOF:
|
|
|
|
return "POLLINIGNEOF";
|
|
|
|
case POLLERR:
|
|
|
|
return "POLLERR";
|
|
|
|
case POLLHUP:
|
|
|
|
return "POLLHUP";
|
|
|
|
case POLLNVAL:
|
|
|
|
return "POLLNVAL";
|
|
|
|
default:
|
|
|
|
return "unknown event";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
print_poll_events(short event)
|
|
|
|
{
|
|
|
|
short curr_event = 0;
|
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
for (size_t i = 0; i <= sizeof(short) * CHAR_BIT - 1; i++) {
|
|
|
|
curr_event = 1 << i;
|
|
|
|
if ((event & curr_event) == 0)
|
|
|
|
continue;
|
|
|
|
if (!first) {
|
|
|
|
printf(" | ");
|
|
|
|
} else {
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
printf("%s", poll_event_to_str(curr_event));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2024-03-02 16:38:04 +01:00
|
|
|
calc_utc_offset(void)
|
2020-12-12 19:34:15 +01:00
|
|
|
{
|
|
|
|
struct timespec monotime, utctime;
|
|
|
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &monotime);
|
|
|
|
clock_gettime(CLOCK_REALTIME, &utctime);
|
|
|
|
timespecsub(&utctime, &monotime, &utc_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
print_timestamp(const char *str, sbintime_t timestamp)
|
|
|
|
{
|
|
|
|
struct timespec ts;
|
|
|
|
char timebuf[32];
|
|
|
|
|
|
|
|
ts = sbttots(timestamp);
|
|
|
|
|
|
|
|
if (!timespecisset(&utc_offset)) {
|
|
|
|
printf("%s %jd.%09ld ", str, (intmax_t)ts.tv_sec, ts.tv_nsec);
|
|
|
|
} else {
|
|
|
|
timespecadd(&utc_offset, &ts, &ts);
|
|
|
|
strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S",
|
|
|
|
gmtime(&ts.tv_sec));
|
|
|
|
printf("%s %s.%09ld ", str, timebuf, ts.tv_nsec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
print_event_detail(const struct gpio_event_detail *det)
|
|
|
|
{
|
|
|
|
print_timestamp("time", det->gp_time);
|
|
|
|
printf("pin %hu state %u\n", det->gp_pin, det->gp_pinstate);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
print_event_summary(const struct gpio_event_summary *sum)
|
|
|
|
{
|
|
|
|
print_timestamp("first_time", sum->gp_first_time);
|
|
|
|
print_timestamp("last_time", sum->gp_last_time);
|
|
|
|
printf("pin %hu count %hu first state %u last state %u\n",
|
|
|
|
sum->gp_pin, sum->gp_count,
|
|
|
|
sum->gp_first_state, sum->gp_last_state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
print_gpio_event(const void *buf)
|
|
|
|
{
|
|
|
|
if (report_format == GPIO_EVENT_REPORT_DETAIL)
|
|
|
|
print_event_detail((const struct gpio_event_detail *)buf);
|
|
|
|
else
|
|
|
|
print_event_summary((const struct gpio_event_summary *)buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
run_read(bool loop, int handle, const char *file, u_int delayus)
|
|
|
|
{
|
|
|
|
const size_t numrecs = 64;
|
|
|
|
union {
|
|
|
|
const struct gpio_event_summary sum[numrecs];
|
|
|
|
const struct gpio_event_detail det[numrecs];
|
|
|
|
uint8_t data[1];
|
|
|
|
} buffer;
|
|
|
|
ssize_t reccount, recsize, res;
|
|
|
|
|
|
|
|
if (report_format == GPIO_EVENT_REPORT_DETAIL)
|
|
|
|
recsize = sizeof(struct gpio_event_detail);
|
|
|
|
else
|
|
|
|
recsize = sizeof(struct gpio_event_summary);
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (delayus != 0) {
|
|
|
|
verbose("sleep %f seconds before read()\n",
|
|
|
|
delayus / 1000000.0);
|
|
|
|
usleep(delayus);
|
|
|
|
}
|
|
|
|
verbose("read into %zd byte buffer\n", sizeof(buffer));
|
|
|
|
res = read(handle, buffer.data, sizeof(buffer));
|
|
|
|
if (res < 0)
|
|
|
|
err(EXIT_FAILURE, "Cannot read from %s", file);
|
|
|
|
|
|
|
|
if ((res % recsize) != 0) {
|
|
|
|
fprintf(stderr, "%s: read() %zd bytes from %s; "
|
|
|
|
"expected a multiple of %zu\n",
|
|
|
|
getprogname(), res, file, recsize);
|
|
|
|
} else {
|
|
|
|
reccount = res / recsize;
|
|
|
|
verbose("read returned %zd bytes; %zd events\n", res,
|
|
|
|
reccount);
|
|
|
|
for (ssize_t i = 0; i < reccount; ++i) {
|
|
|
|
if (report_format == GPIO_EVENT_REPORT_DETAIL)
|
|
|
|
print_event_detail(&buffer.det[i]);
|
|
|
|
else
|
|
|
|
print_event_summary(&buffer.sum[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
run_poll(bool loop, int handle, const char *file, int timeout, u_int delayus)
|
|
|
|
{
|
|
|
|
struct pollfd fds;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
fds.fd = handle;
|
|
|
|
fds.events = POLLIN | POLLRDNORM;
|
|
|
|
fds.revents = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (delayus != 0) {
|
|
|
|
verbose("sleep %f seconds before poll()\n",
|
|
|
|
delayus / 1000000.0);
|
|
|
|
usleep(delayus);
|
|
|
|
}
|
|
|
|
res = poll(&fds, 1, timeout);
|
|
|
|
if (res < 0) {
|
|
|
|
err(EXIT_FAILURE, "Cannot poll() %s", file);
|
|
|
|
} else if (res == 0) {
|
|
|
|
printf("%s: poll() timed out on %s\n", getprogname(),
|
|
|
|
file);
|
|
|
|
} else {
|
|
|
|
printf("%s: poll() returned %i (revents: ",
|
|
|
|
getprogname(), res);
|
|
|
|
print_poll_events(fds.revents);
|
|
|
|
printf(") on %s\n", file);
|
|
|
|
if (fds.revents & (POLLHUP | POLLERR)) {
|
|
|
|
err(EXIT_FAILURE, "Recieved POLLHUP or POLLERR "
|
|
|
|
"on %s", file);
|
|
|
|
}
|
|
|
|
run_read(false, handle, file, 0);
|
|
|
|
}
|
|
|
|
} while (loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
run_select(bool loop, int handle, const char *file, int timeout, u_int delayus)
|
|
|
|
{
|
|
|
|
fd_set readfds;
|
|
|
|
struct timeval tv;
|
|
|
|
struct timeval *tv_ptr;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
FD_ZERO(&readfds);
|
|
|
|
FD_SET(handle, &readfds);
|
|
|
|
if (timeout != INFTIM) {
|
|
|
|
tv.tv_sec = timeout / 1000;
|
|
|
|
tv.tv_usec = (timeout % 1000) * 1000;
|
|
|
|
tv_ptr = &tv;
|
|
|
|
} else {
|
|
|
|
tv_ptr = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (delayus != 0) {
|
|
|
|
verbose("sleep %f seconds before select()\n",
|
|
|
|
delayus / 1000000.0);
|
|
|
|
usleep(delayus);
|
|
|
|
}
|
|
|
|
res = select(FD_SETSIZE, &readfds, NULL, NULL, tv_ptr);
|
|
|
|
if (res < 0) {
|
|
|
|
err(EXIT_FAILURE, "Cannot select() %s", file);
|
|
|
|
} else if (res == 0) {
|
|
|
|
printf("%s: select() timed out on %s\n", getprogname(),
|
|
|
|
file);
|
|
|
|
} else {
|
|
|
|
printf("%s: select() returned %i on %s\n",
|
|
|
|
getprogname(), res, file);
|
|
|
|
run_read(false, handle, file, 0);
|
|
|
|
}
|
|
|
|
} while (loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
run_kqueue(bool loop, int handle, const char *file, int timeout, u_int delayus)
|
|
|
|
{
|
|
|
|
struct kevent event[1];
|
|
|
|
struct kevent tevent[1];
|
|
|
|
int kq = -1;
|
|
|
|
int nev = -1;
|
|
|
|
struct timespec tv;
|
|
|
|
struct timespec *tv_ptr;
|
|
|
|
|
|
|
|
if (timeout != INFTIM) {
|
|
|
|
tv.tv_sec = timeout / 1000;
|
|
|
|
tv.tv_nsec = (timeout % 1000) * 10000000;
|
|
|
|
tv_ptr = &tv;
|
|
|
|
} else {
|
|
|
|
tv_ptr = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
kq = kqueue();
|
|
|
|
if (kq == -1)
|
|
|
|
err(EXIT_FAILURE, "kqueue() %s", file);
|
|
|
|
|
|
|
|
EV_SET(&event[0], handle, EVFILT_READ, EV_ADD, 0, 0, NULL);
|
|
|
|
nev = kevent(kq, event, 1, NULL, 0, NULL);
|
|
|
|
if (nev == -1)
|
|
|
|
err(EXIT_FAILURE, "kevent() %s", file);
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (delayus != 0) {
|
|
|
|
verbose("sleep %f seconds before kevent()\n",
|
|
|
|
delayus / 1000000.0);
|
|
|
|
usleep(delayus);
|
|
|
|
}
|
|
|
|
nev = kevent(kq, NULL, 0, tevent, 1, tv_ptr);
|
|
|
|
if (nev == -1) {
|
|
|
|
err(EXIT_FAILURE, "kevent() %s", file);
|
|
|
|
} else if (nev == 0) {
|
|
|
|
printf("%s: kevent() timed out on %s\n", getprogname(),
|
|
|
|
file);
|
|
|
|
} else {
|
|
|
|
printf("%s: kevent() returned %i events (flags: %d) on "
|
|
|
|
"%s\n", getprogname(), nev, tevent[0].flags, file);
|
|
|
|
if (tevent[0].flags & EV_EOF) {
|
|
|
|
err(EXIT_FAILURE, "Recieved EV_EOF on %s",
|
|
|
|
file);
|
|
|
|
}
|
|
|
|
run_read(false, handle, file, 0);
|
|
|
|
}
|
|
|
|
} while (loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
run_aio_read(bool loop, int handle, const char *file, u_int delayus)
|
|
|
|
{
|
|
|
|
uint8_t buffer[1024];
|
|
|
|
size_t recsize;
|
|
|
|
ssize_t res;
|
|
|
|
struct aiocb iocb;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that async IO to character devices is no longer allowed by
|
|
|
|
* default (since freebsd 11). This code is still here (for now)
|
|
|
|
* because you can use sysctl vfs.aio.enable_unsafe=1 to bypass the
|
|
|
|
* prohibition and run this code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (report_format == GPIO_EVENT_REPORT_DETAIL)
|
|
|
|
recsize = sizeof(struct gpio_event_detail);
|
|
|
|
else
|
|
|
|
recsize = sizeof(struct gpio_event_summary);
|
|
|
|
|
|
|
|
bzero(&iocb, sizeof(iocb));
|
|
|
|
|
|
|
|
iocb.aio_fildes = handle;
|
|
|
|
iocb.aio_nbytes = sizeof(buffer);
|
|
|
|
iocb.aio_offset = 0;
|
|
|
|
iocb.aio_buf = buffer;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (delayus != 0) {
|
|
|
|
verbose("sleep %f seconds before aio_read()\n",
|
|
|
|
delayus / 1000000.0);
|
|
|
|
usleep(delayus);
|
|
|
|
}
|
|
|
|
res = aio_read(&iocb);
|
|
|
|
if (res < 0)
|
|
|
|
err(EXIT_FAILURE, "Cannot aio_read from %s", file);
|
|
|
|
do {
|
|
|
|
res = aio_error(&iocb);
|
|
|
|
} while (res == EINPROGRESS);
|
|
|
|
if (res < 0)
|
|
|
|
err(EXIT_FAILURE, "aio_error on %s", file);
|
|
|
|
res = aio_return(&iocb);
|
|
|
|
if (res < 0)
|
|
|
|
err(EXIT_FAILURE, "aio_return on %s", file);
|
|
|
|
if ((res % recsize) != 0) {
|
|
|
|
fprintf(stderr, "%s: aio_read() %zd bytes from %s; "
|
|
|
|
"expected a multiple of %zu\n",
|
|
|
|
getprogname(), res, file, recsize);
|
|
|
|
} else {
|
|
|
|
for (ssize_t i = 0; i < res; i += recsize)
|
|
|
|
print_gpio_event(&buffer[i]);
|
|
|
|
}
|
|
|
|
} while (loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
run_sigio(bool loop, int handle, const char *file)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
struct sigaction sigact;
|
|
|
|
int flags;
|
|
|
|
int pid;
|
|
|
|
|
|
|
|
bzero(&sigact, sizeof(sigact));
|
|
|
|
sigact.sa_handler = sigio_handler;
|
|
|
|
if (sigaction(SIGIO, &sigact, NULL) < 0)
|
|
|
|
err(EXIT_FAILURE, "cannot set SIGIO handler on %s", file);
|
|
|
|
flags = fcntl(handle, F_GETFL);
|
|
|
|
flags |= O_ASYNC;
|
|
|
|
res = fcntl(handle, F_SETFL, flags);
|
|
|
|
if (res < 0)
|
|
|
|
err(EXIT_FAILURE, "fcntl(F_SETFL) on %s", file);
|
|
|
|
pid = getpid();
|
|
|
|
res = fcntl(handle, F_SETOWN, pid);
|
|
|
|
if (res < 0)
|
|
|
|
err(EXIT_FAILURE, "fnctl(F_SETOWN) on %s", file);
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (sigio == 1) {
|
|
|
|
sigio = 0;
|
2023-12-27 09:41:48 +01:00
|
|
|
printf("%s: received SIGIO on %s\n", getprogname(),
|
2020-12-12 19:34:15 +01:00
|
|
|
file);
|
|
|
|
run_read(false, handle, file, 0);
|
|
|
|
}
|
|
|
|
pause();
|
|
|
|
} while (loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int ch;
|
|
|
|
const char *file = "/dev/gpioc0";
|
|
|
|
char method = 'r';
|
|
|
|
bool loop = true;
|
|
|
|
bool nonblock = false;
|
|
|
|
u_int delayus = 0;
|
|
|
|
int flags;
|
|
|
|
int timeout = INFTIM;
|
|
|
|
int handle;
|
|
|
|
int res;
|
|
|
|
gpio_config_t pin_config;
|
|
|
|
|
|
|
|
while ((ch = getopt(argc, argv, "d:f:m:sSnt:uv")) != -1) {
|
|
|
|
switch (ch) {
|
|
|
|
case 'd':
|
|
|
|
delayus = strtol(optarg, NULL, 10);
|
|
|
|
if (errno != 0) {
|
|
|
|
warn("Invalid delay value");
|
|
|
|
usage();
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
file = optarg;
|
|
|
|
break;
|
|
|
|
case 'm':
|
|
|
|
method = optarg[0];
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
loop = false;
|
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
report_format = GPIO_EVENT_REPORT_SUMMARY;
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
nonblock= true;
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
errno = 0;
|
|
|
|
timeout = strtol(optarg, NULL, 10);
|
|
|
|
if (errno != 0) {
|
|
|
|
warn("Invalid timeout value");
|
|
|
|
usage();
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
calc_utc_offset();
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
be_verbose = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
argv += optind;
|
|
|
|
argc -= optind;
|
|
|
|
|
|
|
|
if (argc == 0) {
|
|
|
|
fprintf(stderr, "%s: No pin number specified.\n",
|
|
|
|
getprogname());
|
|
|
|
usage();
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc == 1) {
|
|
|
|
fprintf(stderr, "%s: No trigger type specified.\n",
|
|
|
|
getprogname());
|
|
|
|
usage();
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2022-12-21 22:24:11 +01:00
|
|
|
if (argc == 1) {
|
|
|
|
fprintf(stderr, "%s: No trigger type specified.\n",
|
|
|
|
getprogname());
|
|
|
|
usage();
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc % 3 != 0) {
|
|
|
|
fprintf(stderr, "%s: Invalid number of (pin intr-conf mode) triplets.\n",
|
2020-12-12 19:34:15 +01:00
|
|
|
getprogname());
|
|
|
|
usage();
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle = gpio_open_device(file);
|
|
|
|
if (handle == GPIO_INVALID_HANDLE)
|
|
|
|
err(EXIT_FAILURE, "Cannot open %s", file);
|
|
|
|
|
|
|
|
if (report_format == GPIO_EVENT_REPORT_SUMMARY) {
|
|
|
|
struct gpio_event_config cfg =
|
|
|
|
{GPIO_EVENT_REPORT_SUMMARY, 0};
|
|
|
|
|
|
|
|
res = ioctl(handle, GPIOCONFIGEVENTS, &cfg);
|
|
|
|
if (res < 0)
|
|
|
|
err(EXIT_FAILURE, "GPIOCONFIGEVENTS failed on %s", file);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nonblock == true) {
|
|
|
|
flags = fcntl(handle, F_GETFL);
|
|
|
|
flags |= O_NONBLOCK;
|
|
|
|
res = fcntl(handle, F_SETFL, flags);
|
|
|
|
if (res < 0)
|
|
|
|
err(EXIT_FAILURE, "cannot set O_NONBLOCK on %s", file);
|
|
|
|
}
|
|
|
|
|
2022-12-21 22:24:11 +01:00
|
|
|
for (int i = 0; i <= argc - 3; i += 3) {
|
2020-12-12 19:34:15 +01:00
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
pin_config.g_pin = strtol(argv[i], NULL, 10);
|
|
|
|
if (errno != 0) {
|
|
|
|
warn("Invalid pin number");
|
|
|
|
usage();
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strnlen(argv[i + 1], 2) < 2) {
|
|
|
|
fprintf(stderr, "%s: Invalid trigger type (argument "
|
|
|
|
"too short).\n", getprogname());
|
|
|
|
usage();
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch((argv[i + 1][0] << 8) + argv[i + 1][1]) {
|
|
|
|
case ('n' << 8) + 'o':
|
|
|
|
pin_config.g_flags = GPIO_INTR_NONE;
|
|
|
|
break;
|
|
|
|
case ('e' << 8) + 'r':
|
|
|
|
pin_config.g_flags = GPIO_INTR_EDGE_RISING;
|
|
|
|
break;
|
|
|
|
case ('e' << 8) + 'f':
|
|
|
|
pin_config.g_flags = GPIO_INTR_EDGE_FALLING;
|
|
|
|
break;
|
|
|
|
case ('e' << 8) + 'b':
|
|
|
|
pin_config.g_flags = GPIO_INTR_EDGE_BOTH;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "%s: Invalid trigger type.\n",
|
|
|
|
getprogname());
|
|
|
|
usage();
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2022-12-21 22:24:11 +01:00
|
|
|
if (strnlen(argv[i + 2], 2) < 2) {
|
|
|
|
fprintf(stderr, "%s: Invalid pin mode (argument "
|
|
|
|
"too short).\n", getprogname());
|
|
|
|
usage();
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch((argv[i + 2][0] << 8) + argv[i + 2][1]) {
|
|
|
|
case ('f' << 8) + 't':
|
|
|
|
/* no changes to pin_config */
|
|
|
|
break;
|
|
|
|
case ('p' << 8) + 'd':
|
|
|
|
pin_config.g_flags |= GPIO_PIN_PULLDOWN;
|
|
|
|
break;
|
|
|
|
case ('p' << 8) + 'u':
|
|
|
|
pin_config.g_flags |= GPIO_PIN_PULLUP;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "%s: Invalid pin mode.\n",
|
|
|
|
getprogname());
|
|
|
|
usage();
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
pin_config.g_flags |= GPIO_PIN_INPUT;
|
2020-12-12 19:34:15 +01:00
|
|
|
|
|
|
|
res = gpio_pin_set_flags(handle, &pin_config);
|
|
|
|
if (res < 0)
|
|
|
|
err(EXIT_FAILURE, "configuration of pin %d on %s "
|
|
|
|
"failed (flags=%d)", pin_config.g_pin, file,
|
|
|
|
pin_config.g_flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (method) {
|
|
|
|
case 'r':
|
|
|
|
run_read(loop, handle, file, delayus);
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
run_poll(loop, handle, file, timeout, delayus);
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
run_select(loop, handle, file, timeout, delayus);
|
|
|
|
break;
|
|
|
|
case 'k':
|
|
|
|
run_kqueue(loop, handle, file, timeout, delayus);
|
|
|
|
break;
|
|
|
|
case 'a':
|
|
|
|
run_aio_read(loop, handle, file, delayus);
|
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
run_sigio(loop, handle, file);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "%s: Unknown method.\n", getprogname());
|
|
|
|
usage();
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|