so, a how to use gpio interrupts under freebsd?
e.g. on armv7
i had a massive battle
even resorted to chatgpt
which then found me others
still hard. very hard
i have now-iirc-nonworking code from 2014 which i designed to be paired with perl (then poe, now anyevent, quite nice)
so i found
https://reviews.freebsd.org/D52102
https://github.com/cyclaero/shutdd
https://forums.freebsd.org/threads/gpio-api-general-orange-pi-h3.83950/page-3#post-556741
the 2014 code
http://ketas.si.pri.ee/bbb/bbb-poe-daemon/util/gpio-intr-kqueue.c
/*
* Copyright (c) 2014 Luiz Otavio O Souza <loos.br@gmail.com>
* Copyright (c) 2014 Sulev-Madis Silber <ketas@si.pri.ee>
* All rights reserved.
*
* 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.
*/
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/event.h>
int main(int argc, char **argv)
{
char *gpioc;
int ch, fd, kq, nev, pin;
struct kevent chlist, evlist;
/* kqueue example program for userland GPIO interrupt notification. */
gpioc = "/dev/gpioc0";
while ((ch = getopt(argc, argv, "f:")) != -1)
{
switch (ch)
{
case 'f':
gpioc = optarg;
break;
}
}
argc -= optind;
argv += optind;
if (argc < 1)
{
printf("Usage:\n");
printf("gpio-intr-kqueue [-f /dev/gpiocX] interrupt-pin-number\n");
exit(1);
}
pin = atoi(argv[0]);
printf("Awaiting interrupts on pin: %d\n", pin);
kq = kqueue();
if (kq < 0)
{
err(1, "kqueue");
}
fd = open(gpioc, O_RDONLY);
if (fd == -1)
{
err(1, "open");
}
/*
* Initialize our changes list with the pins we want to be notified
* about.
*/
EV_SET(&chlist, fd, EVFILT_READ, EV_ADD, 0, pin, NULL);
for (;;)
{
/* Wait for an event. */
nev = kevent(kq, &chlist, 1, &evlist, 1, NULL);
if (nev < 0)
{
err(2, "kevent()");
}
if (nev == 0)
{
continue;
}
if (evlist.flags & EV_ERROR)
{
printf("error: %s\n", strerror(evlist.data));
break;
}
printf("notified of isr on pin: %d\n", (int)evlist.data);
}
close(fd);
close(kq);
exit(0);
}
http://ketas.si.pri.ee/bbb/bbb-poe-daemon/util/gpio-irq-event.c
/*
* Copyright (c) 2015 Sulev-Madis Silber <ketas@si.pri.ee>
* Copyright (c) 2015 Luiz Otavio O Souza <loos.br@gmail.com>
* All rights reserved.
*
* 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.
*
*
* Example program to read GPIO IRQ events.
*
* Build with:
* cc -Wall -o gpio-irq-event gpio-irq-event.c -lgpio
*/
#include <sys/types.h>
#include <sys/filio.h>
#include <sys/gpio.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgpio.h>
#define MAXIRQS 32
static gpio_handle_t handle;
static int irq[MAXIRQS], nirqs;
static struct pollfd fds[MAXIRQS];
static void
usage(void)
{
printf("usage:\n");
printf("gpio-irq-event [-f /dev/gpioN] [-n] pin_number [pin] [..] [pin]\n");
exit(1);
}
static void
sig_die(int sig)
{
int i;
for (i = 0; i < nirqs; i++)
{
if (fds[i].fd != -1)
{
gpio_close(fds[i].fd);
}
if (gpio_irq_unregister(handle, irq[i]) < 0)
{
perror("gpio_irq_unregister");
}
}
gpio_close(handle);
printf("\n");
exit(1);
}
static int
read_ev(int fd, char *buf, ssize_t buflen, ssize_t *offset)
{
ssize_t len;
struct gpio_irq_event *ev;
len = read(fd, buf + *offset, buflen - *offset);
if (len == -1)
{
perror("read");
return len;
}
else if (len == 0)
{
printf("nothing to read: %d\n", len);
return len;
}
ev = (struct gpio_irq_event *)buf;
*offset = len;
while (*offset >= sizeof(*ev))
{
printf("gpiointerrupt ts=%lld.%06ld ts_last=%lld.%06ld pin=%d level=%d\n",
ev->ev_ts.tv_sec, ev->ev_ts.tv_usec,
ev->ev_ts_last.tv_sec, ev->ev_ts_last.tv_usec,
ev->ev_pin,
ev->ev_level);
ev++;
*offset -= sizeof(*ev);
}
if (offset > 0)
{
bcopy(buf + len - *offset, buf, *offset);
}
return 1;
}
static void
poll_irq_ev(struct pollfd *fds, int nfds)
{
char *buf;
int i, rv;
ssize_t buflen, offset;
offset = 0;
buflen = sizeof(struct gpio_irq_event) * 128;
buf = malloc(buflen);
for (;;)
{
rv = poll(fds, nfds, -1);
if (rv == -1)
{
perror("poll");
break;
}
else if (rv == 0)
{
printf("invalid poll return\n");
break;
}
for (i = 0; i < nfds; i++)
{
if (fds[i].revents & POLLIN)
{
if (read_ev(fds[i].fd, buf, buflen, &offset) <= 0)
{
break;
}
}
}
}
free(buf);
}
static void
read_irq_ev(int fd)
{
char *buf;
ssize_t buflen, offset;
offset = 0;
buflen = sizeof(struct gpio_irq_event) * 128;
buf = malloc(buflen);
for (;;)
{
if (read_ev(fd, buf, buflen, &offset) <= 0)
{
break;
}
}
free(buf);
}
int
main(int argc, char **argv)
{
char *ctlfile;
int ch, flags, i;
unsigned int unit;
flags = 0;
ctlfile = NULL;
while ((ch = getopt(argc, argv, "f:n")) != -1)
{
switch (ch)
{
case 'f':
ctlfile = optarg;
break;
case 'n':
flags |= O_NONBLOCK;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc < 1)
{
usage();
}
nirqs = 0;
while (argc > 0 && nirqs < MAXIRQS)
{
irq[nirqs] = atoi(argv[0]);
argc--;
argv++;
nirqs++;
}
if (argc > 0 || nirqs > MAXIRQS)
{
printf("too many pins at once, try increase MAXIRQS.\n");
exit(1);
}
if (nirqs > 1 && (flags & O_NONBLOCK) == 0)
{
printf("multiple pins implies the use of nonblock mode.\n");
flags |= O_NONBLOCK;
}
if (flags & O_NONBLOCK)
{
printf("using nonblock mode.\n");
}
if (ctlfile == NULL)
{
handle = gpio_open(0);
}
else
{
handle = gpio_open_device(ctlfile);
}
if (handle == GPIO_INVALID_HANDLE)
{
perror("gpio_open");
exit(1);
}
for (i = 0; i < MAXIRQS; i++)
{
fds[i].fd = -1;
}
signal(SIGINT, sig_die);
signal(SIGHUP, sig_die);
signal(SIGTERM, sig_die);
for (i = 0; i < nirqs; i++)
{
if (gpio_irq_register(handle, irq[i], &unit) < 0)
{
perror("gpio_irq_register");
goto out;
}
/* Open irq event device. */
fds[i].fd = gpio_irq_open(unit, flags);
if (fds[i].fd == GPIO_INVALID_HANDLE)
{
perror("gpio_irq_open");
goto out;
}
fds[i].events = POLLIN;
}
fprintf(stderr, "READY\n");
if (flags & O_NONBLOCK)
{
poll_irq_ev(fds, nirqs);
}
else
{
read_irq_ev(fds[0].fd);
}
out:
for (i = 0; i < nirqs; i++)
{
if (fds[i].fd != -1)
{
gpio_close(fds[i].fd);
}
if (gpio_irq_unregister(handle, irq[i]) < 0)
{
perror("gpio_irq_unregister");
}
}
gpio_close(handle);
exit(0);
}
the chatgpt rev 4 of this
/*
* gpio-watch.c
*
* FreeBSD userland GPIO interrupt watcher with trailing-edge debounce.
*
* Real GPIO mode:
*
* gpio-watch <controller> <edge> <debounce-us> <pin> [pin ...]
*
* Test mode:
*
* gpio-watch --test
*
* Build:
*
* cc -Wall -Wextra -O2 -o gpio-watch gpio-watch.c -lgpio
*/
#include <sys/types.h>
#include <sys/gpio.h>
#include <libgpio.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define MAX_PINS 128
#define EVENT_FIFO 256
struct watched_pin {
int pin;
/* Last state that was actually reported. */
uint32_t last_state;
/* Most recent event during the debounce period. */
uint32_t pending_state;
sbintime_t pending_time;
/*
* Monotonic time at which the pending event
* becomes reportable.
*/
struct timespec deadline;
int pending;
};
static volatile sig_atomic_t running = 1;
static void
sig_handler(int sig __unused)
{
running = 0;
}
static int
parse_int(const char *s, const char *what)
{
char *end;
long v;
errno = 0;
v = strtol(s, &end, 0);
if (errno != 0 || *end != '\0' || v < 0)
errx(1, "invalid %s: %s", what, s);
return ((int)v);
}
static uint64_t
parse_uint64(const char *s, const char *what)
{
char *end;
unsigned long long v;
errno = 0;
v = strtoull(s, &end, 10);
if (errno != 0 || *end != '\0')
errx(1, "invalid %s: %s", what, s);
return ((uint64_t)v);
}
static uint32_t
parse_edge(const char *s)
{
if (strcmp(s, "rising") == 0)
return (GPIO_INTR_EDGE_RISING);
if (strcmp(s, "falling") == 0)
return (GPIO_INTR_EDGE_FALLING);
if (strcmp(s, "both") == 0)
return (GPIO_INTR_EDGE_BOTH);
errx(1, "edge must be rising, falling or both");
}
static struct watched_pin *
find_pin(struct watched_pin *pins, int npins, int pin)
{
int i;
for (i = 0; i < npins; i++) {
if (pins[i].pin == pin)
return (&pins[i]);
}
return (NULL);
}
static const char *
edge_string(uint32_t oldstate, uint32_t newstate)
{
if (oldstate == 0 && newstate != 0)
return ("rising");
if (oldstate != 0 && newstate == 0)
return ("falling");
return ("stable");
}
/*
* Convert sbintime_t to seconds/nanoseconds for display.
*/
static void
print_timestamp(sbintime_t t)
{
uint64_t sec;
uint64_t frac;
uint64_t nsec;
sec = (uint64_t)t >> 32;
frac = (uint32_t)t;
nsec = (frac * 1000000000ULL) >> 32;
printf("%ju.%09ju",
(uintmax_t)sec,
(uintmax_t)nsec);
}
/*
* Convert sbintime_t to microseconds.
*
* Round instead of truncating so synthetic timestamps such as
* 104000 us don't turn into 103999 us.
*/
static uint64_t
sbt_to_us(sbintime_t t)
{
uint64_t sec;
uint64_t frac;
sec = (uint64_t)t >> 32;
frac = (uint32_t)t;
return ((sec * 1000000ULL) +
((frac * 1000000ULL + (1ULL << 31)) >> 32));
}
/*
* Add microseconds to a timespec.
*/
static struct timespec
timespec_add_us(struct timespec t, uint64_t us)
{
uint64_t nsec;
if (us > UINT64_MAX / 1000ULL)
errx(1, "debounce interval too large");
nsec = us * 1000ULL;
t.tv_sec += (time_t)(nsec / 1000000000ULL);
t.tv_nsec += (long)(nsec % 1000000000ULL);
if (t.tv_nsec >= 1000000000L) {
t.tv_sec++;
t.tv_nsec -= 1000000000L;
}
return (t);
}
static int
timespec_cmp(const struct timespec *a,
const struct timespec *b)
{
if (a->tv_sec < b->tv_sec)
return (-1);
if (a->tv_sec > b->tv_sec)
return (1);
if (a->tv_nsec < b->tv_nsec)
return (-1);
if (a->tv_nsec > b->tv_nsec)
return (1);
return (0);
}
/*
* Return milliseconds until deadline.
*
* poll(2) takes milliseconds, so round upward.
*/
static int
deadline_timeout(const struct timespec *now,
const struct timespec *deadline)
{
int64_t sec;
int64_t nsec;
int64_t ms;
if (timespec_cmp(deadline, now) <= 0)
return (0);
sec = (int64_t)deadline->tv_sec -
(int64_t)now->tv_sec;
nsec = (int64_t)deadline->tv_nsec -
(int64_t)now->tv_nsec;
if (nsec < 0) {
sec--;
nsec += 1000000000LL;
}
ms = sec * 1000LL;
ms += (nsec + 999999LL) / 1000000LL;
if (ms > INT_MAX)
return (INT_MAX);
return ((int)ms);
}
/*
* Find the earliest pending debounce deadline.
*/
static int
next_timeout(struct watched_pin *pins, int npins)
{
struct timespec now;
struct timespec earliest;
int have_earliest;
int i;
if (clock_gettime(CLOCK_MONOTONIC, &now) != 0)
err(1, "clock_gettime");
have_earliest = 0;
for (i = 0; i < npins; i++) {
if (!pins[i].pending)
continue;
if (!have_earliest ||
timespec_cmp(&pins[i].deadline,
&earliest) < 0) {
earliest = pins[i].deadline;
have_earliest = 1;
}
}
if (!have_earliest)
return (-1);
return (deadline_timeout(&now, &earliest));
}
/*
* Report expired debounce timers.
*/
static void
flush_expired(struct watched_pin *pins, int npins,
const struct timespec *now)
{
int i;
for (i = 0; i < npins; i++) {
struct watched_pin *wp;
wp = &pins[i];
if (!wp->pending)
continue;
if (timespec_cmp(now, &wp->deadline) < 0)
continue;
print_timestamp(wp->pending_time);
printf(" GPIO%d %-7s state=%u\n",
wp->pin,
edge_string(wp->last_state,
wp->pending_state),
wp->pending_state);
fflush(stdout);
wp->last_state = wp->pending_state;
wp->pending = 0;
}
}
/*
* Common debounce engine.
*
* Every new event:
*
* - replaces pending_state
* - replaces pending_time
* - restarts this pin's debounce timer
*/
static void
debounce_event(struct watched_pin *wp,
uint32_t state,
sbintime_t event_time,
const struct timespec *now,
uint64_t debounce_us)
{
wp->pending_state = state;
wp->pending_time = event_time;
wp->pending = 1;
wp->deadline =
timespec_add_us(*now, debounce_us);
}
/*
* Convert simulated microseconds to sbintime_t.
*/
static sbintime_t
us_to_sbt(uint64_t us)
{
uint64_t sec;
uint64_t rem;
uint64_t frac;
sec = us / 1000000ULL;
rem = us % 1000000ULL;
frac = (rem * (1ULL << 32)) / 1000000ULL;
return ((sbintime_t)((sec << 32) | frac));
}
static void
print_test_time(uint64_t us)
{
printf("+%06ju us", (uintmax_t)us);
}
/*
* Reset test pins to known initial states.
*/
static void
test_reset(struct watched_pin *pins, int npins)
{
int i;
for (i = 0; i < npins; i++) {
pins[i].pending = 0;
pins[i].pending_state = 0;
pins[i].pending_time = 0;
memset(&pins[i].deadline, 0,
sizeof(pins[i].deadline));
}
pins[0].last_state = 1;
pins[1].last_state = 0;
pins[2].last_state = 1;
}
static void
test_event(struct watched_pin *pins,
int npins,
int pin,
uint32_t state,
uint64_t event_us,
uint64_t debounce_us)
{
struct watched_pin *wp;
struct timespec now;
wp = find_pin(pins, npins, pin);
if (wp == NULL)
errx(1, "test pin %d not found", pin);
now.tv_sec = (time_t)(event_us / 1000000ULL);
now.tv_nsec =
(long)((event_us % 1000000ULL) * 1000ULL);
printf("RAW ");
print_test_time(event_us);
printf(" GPIO%d %-7s state=%u\n",
pin,
edge_string(
wp->pending ?
wp->pending_state :
wp->last_state,
state),
state);
debounce_event(wp,
state,
us_to_sbt(event_us),
&now,
debounce_us);
}
static void
test_flush(struct watched_pin *pins,
int npins,
uint64_t now_us)
{
struct timespec now;
int i;
now.tv_sec = (time_t)(now_us / 1000000ULL);
now.tv_nsec =
(long)((now_us % 1000000ULL) * 1000ULL);
for (i = 0; i < npins; i++) {
struct watched_pin *wp;
wp = &pins[i];
if (!wp->pending)
continue;
if (timespec_cmp(&now, &wp->deadline) < 0)
continue;
printf("DEBOUNCED ");
print_test_time(now_us);
printf(" GPIO%d %-7s state=%u",
wp->pin,
edge_string(wp->last_state,
wp->pending_state),
wp->pending_state);
printf(" (last event ");
print_test_time(sbt_to_us(wp->pending_time));
printf(")\n");
wp->last_state = wp->pending_state;
wp->pending = 0;
}
}
static int
run_test(void)
{
struct watched_pin pins[3];
const uint64_t debounce_us = 5000;
int npins = 3;
int i;
memset(pins, 0, sizeof(pins));
pins[0].pin = 20;
pins[1].pin = 35;
pins[2].pin = 71;
printf("GPIO WATCH TEST\n");
printf("debounce: %ju us\n\n",
(uintmax_t)debounce_us);
/*
* Test 1
*/
printf("TEST 1: single pin bounce\n\n");
test_reset(pins, npins);
test_event(pins, npins, 20, 0,
100000, debounce_us);
test_event(pins, npins, 20, 1,
101000, debounce_us);
test_event(pins, npins, 20, 0,
102000, debounce_us);
test_event(pins, npins, 20, 1,
104000, debounce_us);
test_flush(pins, npins, 109000);
printf("\n");
/*
* Test 2
*/
printf("TEST 2: two pins simultaneously\n\n");
test_reset(pins, npins);
test_event(pins, npins, 20, 0,
200000, debounce_us);
test_event(pins, npins, 35, 1,
201000, debounce_us);
test_event(pins, npins, 20, 1,
202000, debounce_us);
test_event(pins, npins, 35, 0,
203000, debounce_us);
test_event(pins, npins, 20, 0,
204000, debounce_us);
test_event(pins, npins, 35, 1,
205000, debounce_us);
test_flush(pins, npins, 209000);
test_flush(pins, npins, 210000);
printf("\n");
/*
* Test 3
*/
printf("TEST 3: independent timers\n\n");
test_reset(pins, npins);
test_event(pins, npins, 20, 0,
300000, debounce_us);
test_event(pins, npins, 20, 1,
302000, debounce_us);
test_event(pins, npins, 71, 0,
303000, debounce_us);
/*
* GPIO20 deadline = 307000
* GPIO71 deadline = 308000
*/
test_flush(pins, npins, 307000);
test_flush(pins, npins, 308000);
printf("\n");
/*
* Test 4
*/
printf("TEST 4: last event wins\n\n");
test_reset(pins, npins);
test_event(pins, npins, 35, 0,
400000, debounce_us);
test_event(pins, npins, 35, 1,
401000, debounce_us);
test_event(pins, npins, 35, 0,
402000, debounce_us);
test_flush(pins, npins, 407000);
printf("\n");
printf("FINAL STATES\n");
for (i = 0; i < npins; i++)
printf("GPIO%d = %u\n",
pins[i].pin,
pins[i].last_state);
printf("\nTEST COMPLETE\n");
return (0);
}
static int
run_gpio(int argc, char **argv)
{
gpio_handle_t gpio;
struct watched_pin pins[MAX_PINS];
struct gpio_event_detail events[EVENT_FIFO];
struct pollfd pfd;
uint32_t intr;
uint64_t debounce_us;
int controller;
int npins;
int i;
int flags;
if (argc < 5) {
fprintf(stderr,
"usage: %s <controller> "
"<rising|falling|both> "
"<debounce-us> <pin> [pin ...]\n",
argv[0]);
return (1);
}
controller = parse_int(argv[1], "controller");
intr = parse_edge(argv[2]);
debounce_us =
parse_uint64(argv[3], "debounce interval");
npins = argc - 4;
if (npins > MAX_PINS)
errx(1, "too many GPIO pins");
for (i = 0; i < npins; i++) {
pins[i].pin =
parse_int(argv[i + 4], "GPIO pin");
pins[i].last_state = 0;
pins[i].pending_state = 0;
pins[i].pending_time = 0;
pins[i].pending = 0;
if (find_pin(pins, i, pins[i].pin) != NULL)
errx(1, "GPIO pin specified twice: %d",
pins[i].pin);
}
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
gpio = gpio_open((unsigned)controller);
if (gpio == GPIO_INVALID_HANDLE)
errx(1, "gpio_open(%d)", controller);
if (gpio_configure_events(gpio,
GPIO_EVENT_REPORT_DETAIL,
EVENT_FIFO) != 0)
err(1, "gpio_configure_events");
for (i = 0; i < npins; i++) {
gpio_config_t cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.g_pin = pins[i].pin;
cfg.g_flags = GPIO_PIN_INPUT | intr;
if (gpio_pin_set_flags(gpio, &cfg) != 0)
err(1, "cannot configure GPIO %d",
pins[i].pin);
pins[i].last_state =
gpio_pin_get(gpio, pins[i].pin);
}
pfd.fd = gpio_fileno(gpio);
pfd.events = POLLIN;
pfd.revents = 0;
flags = fcntl(pfd.fd, F_GETFL, 0);
if (flags == -1)
err(1, "fcntl(F_GETFL)");
if (fcntl(pfd.fd, F_SETFL, flags | O_NONBLOCK) == -1)
err(1, "fcntl(F_SETFL)");
printf("watching %d GPIO pins, %s edge, "
"trailing debounce %ju us\n",
npins,
argv[2],
(uintmax_t)debounce_us);
for (i = 0; i < npins; i++) {
printf(" GPIO%d initial=%u\n",
pins[i].pin,
pins[i].last_state);
}
fflush(stdout);
while (running) {
int timeout;
int ret;
timeout = next_timeout(pins, npins);
ret = poll(&pfd, 1, timeout);
if (ret < 0) {
if (errno == EINTR)
continue;
err(1, "poll");
}
if (pfd.revents & POLLIN) {
for (;;) {
ssize_t bytes;
size_t nevents;
size_t j;
bytes = read(pfd.fd,
events, sizeof(events));
if (bytes < 0) {
if (errno == EAGAIN ||
errno == EWOULDBLOCK)
break;
if (errno == EINTR)
continue;
err(1, "read");
}
if (bytes == 0)
break;
if ((size_t)bytes %
sizeof(struct gpio_event_detail) != 0)
errx(1,
"partial GPIO event");
nevents = (size_t)bytes /
sizeof(struct gpio_event_detail);
for (j = 0; j < nevents; j++) {
struct gpio_event_detail *ev;
struct watched_pin *wp;
struct timespec now;
ev = &events[j];
wp = find_pin(
pins,
npins,
ev->gp_pin);
if (wp == NULL)
continue;
if (clock_gettime(
CLOCK_MONOTONIC,
&now) != 0)
err(1,
"clock_gettime");
debounce_event(
wp,
ev->gp_pinstate,
ev->gp_time,
&now,
debounce_us);
}
}
}
{
struct timespec now;
if (clock_gettime(
CLOCK_MONOTONIC, &now) != 0)
err(1, "clock_gettime");
flush_expired(
pins,
npins,
&now);
}
}
gpio_close(gpio);
return (0);
}
int
main(int argc, char **argv)
{
if (argc == 2 &&
strcmp(argv[1], "--test") == 0)
return (run_test());
return (run_gpio(argc, argv));
}
also
https://wiki.freebsd.org/SummerOfCode2018Projects/UserSpaceGPIOinterrupts
anyway this is hell
if i make it working again i'll put that somewhere so nobody else would need to pull their hair out since they don't know c. and it's only available from your custom c code!!!