18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * gpio-watch - monitor unrequested lines for property changes using the
48c2ecf20Sopenharmony_ci *              character device
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2019 BayLibre SAS
78c2ecf20Sopenharmony_ci * Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <ctype.h>
118c2ecf20Sopenharmony_ci#include <errno.h>
128c2ecf20Sopenharmony_ci#include <fcntl.h>
138c2ecf20Sopenharmony_ci#include <inttypes.h>
148c2ecf20Sopenharmony_ci#include <linux/gpio.h>
158c2ecf20Sopenharmony_ci#include <poll.h>
168c2ecf20Sopenharmony_ci#include <stdbool.h>
178c2ecf20Sopenharmony_ci#include <stdio.h>
188c2ecf20Sopenharmony_ci#include <stdlib.h>
198c2ecf20Sopenharmony_ci#include <string.h>
208c2ecf20Sopenharmony_ci#include <sys/ioctl.h>
218c2ecf20Sopenharmony_ci#include <unistd.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ciint main(int argc, char **argv)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	struct gpio_v2_line_info_changed chg;
268c2ecf20Sopenharmony_ci	struct gpio_v2_line_info req;
278c2ecf20Sopenharmony_ci	struct pollfd pfd;
288c2ecf20Sopenharmony_ci	int fd, i, j, ret;
298c2ecf20Sopenharmony_ci	char *event, *end;
308c2ecf20Sopenharmony_ci	ssize_t rd;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	if (argc < 3)
338c2ecf20Sopenharmony_ci		goto err_usage;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	fd = open(argv[1], O_RDWR | O_CLOEXEC);
368c2ecf20Sopenharmony_ci	if (fd < 0) {
378c2ecf20Sopenharmony_ci		perror("unable to open gpiochip");
388c2ecf20Sopenharmony_ci		return EXIT_FAILURE;
398c2ecf20Sopenharmony_ci	}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	for (i = 0, j = 2; i < argc - 2; i++, j++) {
428c2ecf20Sopenharmony_ci		memset(&req, 0, sizeof(req));
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci		req.offset = strtoul(argv[j], &end, 0);
458c2ecf20Sopenharmony_ci		if (*end != '\0')
468c2ecf20Sopenharmony_ci			goto err_usage;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci		ret = ioctl(fd, GPIO_V2_GET_LINEINFO_WATCH_IOCTL, &req);
498c2ecf20Sopenharmony_ci		if (ret) {
508c2ecf20Sopenharmony_ci			perror("unable to set up line watch");
518c2ecf20Sopenharmony_ci			return EXIT_FAILURE;
528c2ecf20Sopenharmony_ci		}
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	pfd.fd = fd;
568c2ecf20Sopenharmony_ci	pfd.events = POLLIN | POLLPRI;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	for (;;) {
598c2ecf20Sopenharmony_ci		ret = poll(&pfd, 1, 5000);
608c2ecf20Sopenharmony_ci		if (ret < 0) {
618c2ecf20Sopenharmony_ci			perror("error polling the linechanged fd");
628c2ecf20Sopenharmony_ci			return EXIT_FAILURE;
638c2ecf20Sopenharmony_ci		} else if (ret > 0) {
648c2ecf20Sopenharmony_ci			memset(&chg, 0, sizeof(chg));
658c2ecf20Sopenharmony_ci			rd = read(pfd.fd, &chg, sizeof(chg));
668c2ecf20Sopenharmony_ci			if (rd < 0 || rd != sizeof(chg)) {
678c2ecf20Sopenharmony_ci				if (rd != sizeof(chg))
688c2ecf20Sopenharmony_ci					errno = EIO;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci				perror("error reading line change event");
718c2ecf20Sopenharmony_ci				return EXIT_FAILURE;
728c2ecf20Sopenharmony_ci			}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci			switch (chg.event_type) {
758c2ecf20Sopenharmony_ci			case GPIO_V2_LINE_CHANGED_REQUESTED:
768c2ecf20Sopenharmony_ci				event = "requested";
778c2ecf20Sopenharmony_ci				break;
788c2ecf20Sopenharmony_ci			case GPIO_V2_LINE_CHANGED_RELEASED:
798c2ecf20Sopenharmony_ci				event = "released";
808c2ecf20Sopenharmony_ci				break;
818c2ecf20Sopenharmony_ci			case GPIO_V2_LINE_CHANGED_CONFIG:
828c2ecf20Sopenharmony_ci				event = "config changed";
838c2ecf20Sopenharmony_ci				break;
848c2ecf20Sopenharmony_ci			default:
858c2ecf20Sopenharmony_ci				fprintf(stderr,
868c2ecf20Sopenharmony_ci					"invalid event type received from the kernel\n");
878c2ecf20Sopenharmony_ci				return EXIT_FAILURE;
888c2ecf20Sopenharmony_ci			}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci			printf("line %u: %s at %" PRIu64 "\n",
918c2ecf20Sopenharmony_ci			       chg.info.offset, event, (uint64_t)chg.timestamp_ns);
928c2ecf20Sopenharmony_ci		}
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return 0;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cierr_usage:
988c2ecf20Sopenharmony_ci	printf("%s: <gpiochip> <line0> <line1> ...\n", argv[0]);
998c2ecf20Sopenharmony_ci	return EXIT_FAILURE;
1008c2ecf20Sopenharmony_ci}
101