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