From: Kent Gibson <warthog618@gmail.com>
To: linux-gpio@vger.kernel.org, brgl@bgdev.pl
Cc: Kent Gibson <warthog618@gmail.com>
Subject: [libgpiod v2][PATCH 3/4] tools: add gpiowatch
Date: Mon, 27 Jun 2022 21:44:46 +0800 [thread overview]
Message-ID: <20220627134447.81927-4-warthog618@gmail.com> (raw)
In-Reply-To: <20220627134447.81927-1-warthog618@gmail.com>
Add a gpiowatch tool, based on gpiomon, but reporting line info change
events similar to the gpio-watch tool in the linux kernel.
Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
man/Makefile.am | 2 +-
tools/.gitignore | 1 +
tools/Makefile.am | 4 +-
tools/gpiowatch.c | 214 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 219 insertions(+), 2 deletions(-)
create mode 100644 tools/gpiowatch.c
diff --git a/man/Makefile.am b/man/Makefile.am
index 4d2c29b..3badd3b 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -3,7 +3,7 @@
if WITH_MANPAGES
-dist_man1_MANS = gpiodetect.man gpioinfo.man gpioget.man gpioset.man gpiofind.man gpiomon.man
+dist_man1_MANS = gpiodetect.man gpioinfo.man gpioget.man gpioset.man gpiofind.man gpiomon.man gpiowatch.man
%.man: $(top_builddir)/tools/$(*F)
help2man $(top_builddir)/tools/$(*F) --include=$(srcdir)/template --output=$(builddir)/$@ --no-info
diff --git a/tools/.gitignore b/tools/.gitignore
index 0d53de9..6175e26 100644
--- a/tools/.gitignore
+++ b/tools/.gitignore
@@ -7,3 +7,4 @@ gpioget
gpioset
gpiomon
gpiofind
+gpiowatch
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 4a13266..8bb2cac 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -9,7 +9,7 @@ libtools_common_la_SOURCES = tools-common.c tools-common.h
LDADD = libtools-common.la $(top_builddir)/lib/libgpiod.la
-bin_PROGRAMS = gpiodetect gpioinfo gpioget gpioset gpiomon gpiofind
+bin_PROGRAMS = gpiodetect gpioinfo gpioget gpioset gpiomon gpiofind gpiowatch
gpiodetect_SOURCES = gpiodetect.c
@@ -23,6 +23,8 @@ gpiomon_SOURCES = gpiomon.c
gpiofind_SOURCES = gpiofind.c
+gpiowatch_SOURCES = gpiowatch.c
+
EXTRA_DIST = gpio-tools-test gpio-tools-test.bats
if WITH_TESTS
diff --git a/tools/gpiowatch.c b/tools/gpiowatch.c
new file mode 100644
index 0000000..e6bfeb6
--- /dev/null
+++ b/tools/gpiowatch.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2017-2022 Bartosz Golaszewski <bartekgola@gmail.com>
+
+#include <getopt.h>
+#include <gpiod.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "tools-common.h"
+
+static int by_name;
+static int event_clock_mode;
+static int banner;
+
+static const struct option longopts[] = {
+ { "banner", no_argument, &banner, 1 },
+ { "by-name", no_argument, &by_name, 1 },
+ { "chip", required_argument, NULL, 'c' },
+ { "help", no_argument, NULL, 'h' },
+ { "localtime", no_argument, &event_clock_mode, 2 },
+ { "strict", no_argument, NULL, 's' },
+ { "utc", no_argument, &event_clock_mode, 1 },
+ { "version", no_argument, NULL, 'v' },
+ { GETOPT_NULL_LONGOPT },
+};
+
+static const char *const shortopts = "+c:shv";
+
+static void print_help(void)
+{
+ printf("Usage: %s [OPTIONS] <line> ...\n", get_progname());
+ printf("\n");
+ printf("Wait for changes to info on GPIO lines and print them to standard output.\n");
+ printf("\n");
+ printf("Lines are specified by name, or optionally by offset if the chip option\n");
+ printf("is provided.\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" --banner\t\tdisplay a banner on successful startup\n");
+ printf(" --by-name\t\ttreat lines as names even if they would parse as an offset\n");
+ printf(" -c, --chip <chip>\trestrict scope to a particular chip\n");
+ printf(" -h, --help\t\tdisplay this help and exit\n");
+ printf(" --localtime\treport event time as a local time (default is monotonic)\n");
+ printf(" -s, --strict\t\tabort if requested line names are not unique\n");
+ printf(" --utc\t\treport event time as UTC (default is monotonic)\n");
+ printf(" -v, --version\t\toutput version information and exit\n");
+ print_chip_help();
+}
+
+struct config {
+ bool strict;
+ const char *chip_id;
+};
+
+int parse_config(int argc, char **argv, struct config *cfg)
+{
+ int opti, optc;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ for (;;) {
+ optc = getopt_long(argc, argv, shortopts, longopts, &opti);
+ if (optc < 0)
+ break;
+
+ switch (optc) {
+ case 'c':
+ cfg->chip_id = optarg;
+ break;
+ case 's':
+ cfg->strict = true;
+ break;
+ case 'h':
+ print_help();
+ exit(EXIT_SUCCESS);
+ case 'v':
+ print_version();
+ exit(EXIT_SUCCESS);
+ case '?':
+ die("try %s --help", get_progname());
+ case 0:
+ break;
+ default:
+ abort();
+ }
+ }
+
+ return optind;
+}
+
+static void print_banner(int num_lines, char **lines)
+{
+ int i;
+
+ if (num_lines > 1) {
+ printf("Watching lines ");
+ for (i = 0; i < num_lines - 1; i++)
+ printf("%s, ", lines[i]);
+ printf("and %s...\n", lines[i]);
+ } else {
+ printf("Watching line %s ...\n", lines[0]);
+ }
+}
+
+static void event_print(struct gpiod_info_event *event, const char *chip_id)
+{
+ struct gpiod_line_info *info;
+ uint64_t evtime, before, after, mono;
+ char *evname;
+ int evtype;
+ struct timespec ts;
+
+ info = gpiod_info_event_get_line_info(event);
+ evtime = gpiod_info_event_get_timestamp_ns(event);
+ evtype = gpiod_info_event_get_event_type(event);
+
+ switch (evtype) {
+ case GPIOD_INFO_EVENT_LINE_REQUESTED:
+ evname = "REQUESTED";
+ break;
+ case GPIOD_INFO_EVENT_LINE_RELEASED:
+ evname = "RELEASED ";
+ break;
+ case GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED:
+ evname = "RECONFIG ";
+ break;
+ default:
+ evname = "UNKNOWN ";
+ }
+ if (event_clock_mode) {
+ // map clock monotonic to realtime, as uAPI only supports CLOCK_MONOTONIC
+ clock_gettime(CLOCK_REALTIME, &ts);
+ before = ts.tv_nsec + ts.tv_sec * 1000000000;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ mono = ts.tv_nsec + ts.tv_sec * 1000000000;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ after = ts.tv_nsec + ts.tv_sec * 1000000000;
+ evtime += (after/2 - mono + before/2);
+ }
+ print_event_time(evtime, event_clock_mode);
+ printf(" %s", evname);
+ if (chip_id)
+ printf(" %s %d", chip_id, gpiod_line_info_get_offset(info));
+ print_line_info(info);
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ int i, j;
+ struct gpiod_chip **chips;
+ struct pollfd *pollfds;
+ struct gpiod_chip *chip;
+ struct line_resolver *resolver;
+ struct gpiod_info_event *event;
+ struct config cfg;
+
+ i = parse_config(argc, argv, &cfg);
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ die("at least one GPIO line must be specified");
+
+ if (argc > 64)
+ die("too many lines given");
+
+ resolver = resolve_lines(argc, argv, cfg.chip_id, cfg.strict, by_name);
+ chips = calloc(resolver->num_chips, sizeof(*chips));
+ pollfds = calloc(resolver->num_chips, sizeof(*pollfds));
+ if (!pollfds)
+ die("out of memory");
+ for (i = 0; i < resolver->num_chips; i++) {
+ chip = gpiod_chip_open(resolver->chip_paths[i]);
+ if (!chip)
+ die_perror("unable to open chip %s", resolver->chip_paths[i]);
+
+ for (j = 0; j < resolver->num_lines; j++)
+ if (resolver->lines[j].chip_path == resolver->chip_paths[i])
+ if (!gpiod_chip_watch_line_info(chip, resolver->lines[j].offset))
+ die_perror("unable to watch line on chip %s",
+ resolver->chip_paths[i]);
+
+ chips[i] = chip;
+ pollfds[i].fd = gpiod_chip_get_fd(chip);
+ pollfds[i].events = POLLIN;
+ }
+
+ if (banner)
+ print_banner(argc, argv);
+
+ for (;;) {
+ if (poll(pollfds, resolver->num_chips, -1) < 0)
+ die_perror("error polling for events");
+
+ for (i = 0; i < resolver->num_chips; i++) {
+ if (pollfds[i].revents == 0)
+ continue;
+
+ event = gpiod_chip_read_info_event(chips[i]);
+ event_print(event, cfg.chip_id);
+ }
+ }
+ for (i = 0; i < resolver->num_chips; i++)
+ gpiod_chip_close(chips[i]);
+ free(chips);
+ free_line_resolver(resolver);
+
+ return EXIT_SUCCESS;
+}
--
2.36.1
next prev parent reply other threads:[~2022-06-27 13:46 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-06-27 13:44 [libgpiod v2][PATCH 0/4] tools: line name focussed rework Kent Gibson
2022-06-27 13:44 ` [libgpiod v2][PATCH 1/4] " Kent Gibson
2022-07-06 20:20 ` Bartosz Golaszewski
2022-07-07 2:24 ` Kent Gibson
2022-07-07 9:01 ` Bartosz Golaszewski
2022-07-07 9:18 ` Kent Gibson
2022-06-27 13:44 ` [libgpiod v2][PATCH 2/4] tools: tests for " Kent Gibson
2022-07-01 0:42 ` Kent Gibson
2022-06-27 13:44 ` Kent Gibson [this message]
2022-07-06 20:46 ` [libgpiod v2][PATCH 3/4] tools: add gpiowatch Bartosz Golaszewski
2022-07-07 2:27 ` Kent Gibson
2022-07-07 8:41 ` Bartosz Golaszewski
2022-07-07 8:55 ` Kent Gibson
2022-06-27 13:44 ` [libgpiod v2][PATCH 4/4] tools: gpiowatch tests Kent Gibson
2022-06-28 5:26 ` [libgpiod v2][PATCH 0/4] tools: line name focussed rework Kent Gibson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220627134447.81927-4-warthog618@gmail.com \
--to=warthog618@gmail.com \
--cc=brgl@bgdev.pl \
--cc=linux-gpio@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.