162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * gpio-watch - monitor unrequested lines for property changes using the
462306a36Sopenharmony_ci *              character device
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2019 BayLibre SAS
762306a36Sopenharmony_ci * Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <ctype.h>
1162306a36Sopenharmony_ci#include <errno.h>
1262306a36Sopenharmony_ci#include <fcntl.h>
1362306a36Sopenharmony_ci#include <inttypes.h>
1462306a36Sopenharmony_ci#include <linux/gpio.h>
1562306a36Sopenharmony_ci#include <poll.h>
1662306a36Sopenharmony_ci#include <stdbool.h>
1762306a36Sopenharmony_ci#include <stdio.h>
1862306a36Sopenharmony_ci#include <stdlib.h>
1962306a36Sopenharmony_ci#include <string.h>
2062306a36Sopenharmony_ci#include <sys/ioctl.h>
2162306a36Sopenharmony_ci#include <unistd.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ciint main(int argc, char **argv)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	struct gpio_v2_line_info_changed chg;
2662306a36Sopenharmony_ci	struct gpio_v2_line_info req;
2762306a36Sopenharmony_ci	struct pollfd pfd;
2862306a36Sopenharmony_ci	int fd, i, j, ret;
2962306a36Sopenharmony_ci	char *event, *end;
3062306a36Sopenharmony_ci	ssize_t rd;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (argc < 3)
3362306a36Sopenharmony_ci		goto err_usage;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	fd = open(argv[1], O_RDWR | O_CLOEXEC);
3662306a36Sopenharmony_ci	if (fd < 0) {
3762306a36Sopenharmony_ci		perror("unable to open gpiochip");
3862306a36Sopenharmony_ci		return EXIT_FAILURE;
3962306a36Sopenharmony_ci	}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	for (i = 0, j = 2; i < argc - 2; i++, j++) {
4262306a36Sopenharmony_ci		memset(&req, 0, sizeof(req));
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci		req.offset = strtoul(argv[j], &end, 0);
4562306a36Sopenharmony_ci		if (*end != '\0')
4662306a36Sopenharmony_ci			goto err_usage;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci		ret = ioctl(fd, GPIO_V2_GET_LINEINFO_WATCH_IOCTL, &req);
4962306a36Sopenharmony_ci		if (ret) {
5062306a36Sopenharmony_ci			perror("unable to set up line watch");
5162306a36Sopenharmony_ci			return EXIT_FAILURE;
5262306a36Sopenharmony_ci		}
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	pfd.fd = fd;
5662306a36Sopenharmony_ci	pfd.events = POLLIN | POLLPRI;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	for (;;) {
5962306a36Sopenharmony_ci		ret = poll(&pfd, 1, 5000);
6062306a36Sopenharmony_ci		if (ret < 0) {
6162306a36Sopenharmony_ci			perror("error polling the linechanged fd");
6262306a36Sopenharmony_ci			return EXIT_FAILURE;
6362306a36Sopenharmony_ci		} else if (ret > 0) {
6462306a36Sopenharmony_ci			memset(&chg, 0, sizeof(chg));
6562306a36Sopenharmony_ci			rd = read(pfd.fd, &chg, sizeof(chg));
6662306a36Sopenharmony_ci			if (rd < 0 || rd != sizeof(chg)) {
6762306a36Sopenharmony_ci				if (rd != sizeof(chg))
6862306a36Sopenharmony_ci					errno = EIO;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci				perror("error reading line change event");
7162306a36Sopenharmony_ci				return EXIT_FAILURE;
7262306a36Sopenharmony_ci			}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci			switch (chg.event_type) {
7562306a36Sopenharmony_ci			case GPIO_V2_LINE_CHANGED_REQUESTED:
7662306a36Sopenharmony_ci				event = "requested";
7762306a36Sopenharmony_ci				break;
7862306a36Sopenharmony_ci			case GPIO_V2_LINE_CHANGED_RELEASED:
7962306a36Sopenharmony_ci				event = "released";
8062306a36Sopenharmony_ci				break;
8162306a36Sopenharmony_ci			case GPIO_V2_LINE_CHANGED_CONFIG:
8262306a36Sopenharmony_ci				event = "config changed";
8362306a36Sopenharmony_ci				break;
8462306a36Sopenharmony_ci			default:
8562306a36Sopenharmony_ci				fprintf(stderr,
8662306a36Sopenharmony_ci					"invalid event type received from the kernel\n");
8762306a36Sopenharmony_ci				return EXIT_FAILURE;
8862306a36Sopenharmony_ci			}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci			printf("line %u: %s at %" PRIu64 "\n",
9162306a36Sopenharmony_ci			       chg.info.offset, event, (uint64_t)chg.timestamp_ns);
9262306a36Sopenharmony_ci		}
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cierr_usage:
9862306a36Sopenharmony_ci	printf("%s: <gpiochip> <line0> <line1> ...\n", argv[0]);
9962306a36Sopenharmony_ci	return EXIT_FAILURE;
10062306a36Sopenharmony_ci}
101