133f0c47bSBartosz Golaszewski // SPDX-License-Identifier: GPL-2.0-only
233f0c47bSBartosz Golaszewski /*
333f0c47bSBartosz Golaszewski * gpio-watch - monitor unrequested lines for property changes using the
433f0c47bSBartosz Golaszewski * character device
533f0c47bSBartosz Golaszewski *
633f0c47bSBartosz Golaszewski * Copyright (C) 2019 BayLibre SAS
733f0c47bSBartosz Golaszewski * Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
833f0c47bSBartosz Golaszewski */
933f0c47bSBartosz Golaszewski
1033f0c47bSBartosz Golaszewski #include <ctype.h>
1133f0c47bSBartosz Golaszewski #include <errno.h>
1233f0c47bSBartosz Golaszewski #include <fcntl.h>
13*1fc7c1efSKent Gibson #include <inttypes.h>
1433f0c47bSBartosz Golaszewski #include <linux/gpio.h>
1533f0c47bSBartosz Golaszewski #include <poll.h>
1633f0c47bSBartosz Golaszewski #include <stdbool.h>
1733f0c47bSBartosz Golaszewski #include <stdio.h>
1833f0c47bSBartosz Golaszewski #include <stdlib.h>
1933f0c47bSBartosz Golaszewski #include <string.h>
2033f0c47bSBartosz Golaszewski #include <sys/ioctl.h>
2133f0c47bSBartosz Golaszewski #include <unistd.h>
2233f0c47bSBartosz Golaszewski
main(int argc,char ** argv)2333f0c47bSBartosz Golaszewski int main(int argc, char **argv)
2433f0c47bSBartosz Golaszewski {
25e86a863bSKent Gibson struct gpio_v2_line_info_changed chg;
26e86a863bSKent Gibson struct gpio_v2_line_info req;
2733f0c47bSBartosz Golaszewski struct pollfd pfd;
2833f0c47bSBartosz Golaszewski int fd, i, j, ret;
2933f0c47bSBartosz Golaszewski char *event, *end;
3033f0c47bSBartosz Golaszewski ssize_t rd;
3133f0c47bSBartosz Golaszewski
3233f0c47bSBartosz Golaszewski if (argc < 3)
3333f0c47bSBartosz Golaszewski goto err_usage;
3433f0c47bSBartosz Golaszewski
3533f0c47bSBartosz Golaszewski fd = open(argv[1], O_RDWR | O_CLOEXEC);
3633f0c47bSBartosz Golaszewski if (fd < 0) {
3733f0c47bSBartosz Golaszewski perror("unable to open gpiochip");
3833f0c47bSBartosz Golaszewski return EXIT_FAILURE;
3933f0c47bSBartosz Golaszewski }
4033f0c47bSBartosz Golaszewski
4133f0c47bSBartosz Golaszewski for (i = 0, j = 2; i < argc - 2; i++, j++) {
4233f0c47bSBartosz Golaszewski memset(&req, 0, sizeof(req));
4333f0c47bSBartosz Golaszewski
44e86a863bSKent Gibson req.offset = strtoul(argv[j], &end, 0);
4533f0c47bSBartosz Golaszewski if (*end != '\0')
4633f0c47bSBartosz Golaszewski goto err_usage;
4733f0c47bSBartosz Golaszewski
48e86a863bSKent Gibson ret = ioctl(fd, GPIO_V2_GET_LINEINFO_WATCH_IOCTL, &req);
4933f0c47bSBartosz Golaszewski if (ret) {
5033f0c47bSBartosz Golaszewski perror("unable to set up line watch");
5133f0c47bSBartosz Golaszewski return EXIT_FAILURE;
5233f0c47bSBartosz Golaszewski }
5333f0c47bSBartosz Golaszewski }
5433f0c47bSBartosz Golaszewski
5533f0c47bSBartosz Golaszewski pfd.fd = fd;
5633f0c47bSBartosz Golaszewski pfd.events = POLLIN | POLLPRI;
5733f0c47bSBartosz Golaszewski
5833f0c47bSBartosz Golaszewski for (;;) {
5933f0c47bSBartosz Golaszewski ret = poll(&pfd, 1, 5000);
6033f0c47bSBartosz Golaszewski if (ret < 0) {
6133f0c47bSBartosz Golaszewski perror("error polling the linechanged fd");
6233f0c47bSBartosz Golaszewski return EXIT_FAILURE;
6333f0c47bSBartosz Golaszewski } else if (ret > 0) {
6433f0c47bSBartosz Golaszewski memset(&chg, 0, sizeof(chg));
6533f0c47bSBartosz Golaszewski rd = read(pfd.fd, &chg, sizeof(chg));
6633f0c47bSBartosz Golaszewski if (rd < 0 || rd != sizeof(chg)) {
6733f0c47bSBartosz Golaszewski if (rd != sizeof(chg))
6833f0c47bSBartosz Golaszewski errno = EIO;
6933f0c47bSBartosz Golaszewski
7033f0c47bSBartosz Golaszewski perror("error reading line change event");
7133f0c47bSBartosz Golaszewski return EXIT_FAILURE;
7233f0c47bSBartosz Golaszewski }
7333f0c47bSBartosz Golaszewski
7433f0c47bSBartosz Golaszewski switch (chg.event_type) {
75e86a863bSKent Gibson case GPIO_V2_LINE_CHANGED_REQUESTED:
7633f0c47bSBartosz Golaszewski event = "requested";
7733f0c47bSBartosz Golaszewski break;
78e86a863bSKent Gibson case GPIO_V2_LINE_CHANGED_RELEASED:
7933f0c47bSBartosz Golaszewski event = "released";
8033f0c47bSBartosz Golaszewski break;
81e86a863bSKent Gibson case GPIO_V2_LINE_CHANGED_CONFIG:
8233f0c47bSBartosz Golaszewski event = "config changed";
8333f0c47bSBartosz Golaszewski break;
8433f0c47bSBartosz Golaszewski default:
8533f0c47bSBartosz Golaszewski fprintf(stderr,
8633f0c47bSBartosz Golaszewski "invalid event type received from the kernel\n");
8733f0c47bSBartosz Golaszewski return EXIT_FAILURE;
8833f0c47bSBartosz Golaszewski }
8933f0c47bSBartosz Golaszewski
90*1fc7c1efSKent Gibson printf("line %u: %s at %" PRIu64 "\n",
91*1fc7c1efSKent Gibson chg.info.offset, event, (uint64_t)chg.timestamp_ns);
9233f0c47bSBartosz Golaszewski }
9333f0c47bSBartosz Golaszewski }
9433f0c47bSBartosz Golaszewski
9533f0c47bSBartosz Golaszewski return 0;
9633f0c47bSBartosz Golaszewski
9733f0c47bSBartosz Golaszewski err_usage:
9833f0c47bSBartosz Golaszewski printf("%s: <gpiochip> <line0> <line1> ...\n", argv[0]);
9933f0c47bSBartosz Golaszewski return EXIT_FAILURE;
10033f0c47bSBartosz Golaszewski }
101