[RFC PATCH v1 10/20] tools: gpio: Add GPIO output generation user application

From: lakshmi . sowjanya . d
Date: Tue Aug 24 2021 - 12:48:48 EST


From: Christopher Hall <christopher.s.hall@xxxxxxxxx>

Add GPIO user application - gpio_event_gen - to generate output using
output methods added to GPIO lib. The output produced is 1 Hz clock
aligned to the system clock using singly scheduled edges.

gpio_event_gen accepts similar arguments to gpio-event-mon.

Example output:
$ gpio-event-gen -n gpiochip0 -o 0 -c 3
Generating events on line 0 on gpiochip1
clock realtime : 1612453529996832765
GPIO EVENT TRIGGER: 1612453531000000000
clock realtime 2 2 : 1612453531500000000
GPIO EVENT TRIGGER: 1612453531500000000
clock realtime 2 2 : 1612453532000000000
GPIO EVENT TRIGGER: 1612453532000000000
clock realtime 2 2 : 1612453532500000000

Produces 3 events of 1 Hz output on line 0 of chip/device 0.

Signed-off-by: Christopher Hall <christopher.s.hall@xxxxxxxxx>
Signed-off-by: Tamal Saha <tamal.saha@xxxxxxxxx>
Co-developed-by: Lakshmi Sowjanya D <lakshmi.sowjanya.d@xxxxxxxxx>
Signed-off-by: Lakshmi Sowjanya D <lakshmi.sowjanya.d@xxxxxxxxx>
Reviewed-by: Mark Gross <mgross@xxxxxxxxxxxxxxx>
---
tools/gpio/.gitignore | 1 +
tools/gpio/Build | 1 +
tools/gpio/Makefile | 11 ++-
tools/gpio/gpio-event-gen.c | 191 ++++++++++++++++++++++++++++++++++++
4 files changed, 203 insertions(+), 1 deletion(-)
create mode 100644 tools/gpio/gpio-event-gen.c

diff --git a/tools/gpio/.gitignore b/tools/gpio/.gitignore
index a00d604027a2..d5685cd0eb51 100644
--- a/tools/gpio/.gitignore
+++ b/tools/gpio/.gitignore
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
gpio-event-mon
+gpio-event-gen
gpio-hammer
gpio-watch
lsgpio
diff --git a/tools/gpio/Build b/tools/gpio/Build
index 67c7b7f6a717..dc6a178c195a 100644
--- a/tools/gpio/Build
+++ b/tools/gpio/Build
@@ -2,4 +2,5 @@ gpio-utils-y += gpio-utils.o
lsgpio-y += lsgpio.o gpio-utils.o
gpio-hammer-y += gpio-hammer.o gpio-utils.o
gpio-event-mon-y += gpio-event-mon.o gpio-utils.o
+gpio-event-gen-y += gpio-event-gen.o gpio-utils.o
gpio-watch-y += gpio-watch.o
diff --git a/tools/gpio/Makefile b/tools/gpio/Makefile
index 440434027557..c9efaee76f28 100644
--- a/tools/gpio/Makefile
+++ b/tools/gpio/Makefile
@@ -18,7 +18,7 @@ MAKEFLAGS += -r

override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include

-ALL_TARGETS := lsgpio gpio-hammer gpio-event-mon gpio-watch
+ALL_TARGETS := lsgpio gpio-hammer gpio-event-mon gpio-event-gen gpio-watch
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))

all: $(ALL_PROGRAMS)
@@ -66,6 +66,15 @@ $(GPIO_EVENT_MON_IN): prepare FORCE $(OUTPUT)gpio-utils-in.o
$(OUTPUT)gpio-event-mon: $(GPIO_EVENT_MON_IN)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@

+#
+# gpio-event-gen
+#
+GPIO_EVENT_GEN_IN := $(OUTPUT)gpio-event-gen-in.o
+$(GPIO_EVENT_GEN_IN): prepare FORCE $(OUTPUT)gpio-utils-in.o
+ $(Q)$(MAKE) $(build)=gpio-event-gen
+$(OUTPUT)gpio-event-gen: $(GPIO_EVENT_GEN_IN)
+ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
+
#
# gpio-watch
#
diff --git a/tools/gpio/gpio-event-gen.c b/tools/gpio/gpio-event-gen.c
new file mode 100644
index 000000000000..3d5ef47d79d0
--- /dev/null
+++ b/tools/gpio/gpio-event-gen.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * gpio-event-gen - generate GPIO line events from userspace
+ *
+ * Copyright (C) 2020 Intel Corporation
+ * Author: Christopher S Hall <christopher.s.hall@xxxxxxxxx>
+ *
+ * Adapted from gpio-event-mon.c
+ * Copyright (C) 2016 Linus Walleij
+ *
+ * Usage:
+ * gpio-event-gen -n <device-name> -o <offset>
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <linux/gpio.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#define NSEC_PER_SEC (1000000000ULL)
+#define TIMESPEC_TO_U64(x) (((uint64_t)(x).tv_sec) * NSEC_PER_SEC + (x).tv_nsec)
+#define U64_TO_TIMESPEC(x) \
+ ((struct timespec){ .tv_sec = (x) / NSEC_PER_SEC, \
+ .tv_nsec = (x) % NSEC_PER_SEC})
+
+int sleep_until(uint64_t systime)
+{
+ struct timespec wait_until;
+
+ wait_until = U64_TO_TIMESPEC(systime);
+ return clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wait_until, NULL);
+}
+
+int generate_events(const char *device_name,
+ unsigned int line[],
+ unsigned int num_lines,
+ uint32_t flags,
+ unsigned int loops)
+{
+ struct gpio_v2_line_request req;
+ struct gpio_v2_line_config config;
+ uint64_t trigger_time;
+ struct timespec now;
+ char *chrdev_name;
+ int ret, fd;
+ int i = 0;
+
+ ret = asprintf(&chrdev_name, "/dev/%s", device_name);
+ if (ret < 0)
+ return -ENOMEM;
+
+ fd = open(chrdev_name, 0);
+ if (fd == -1) {
+ ret = -errno;
+ fprintf(stderr, "Failed to open %s\n", chrdev_name);
+ goto exit_close_error;
+ }
+
+ memset(&config, 0, sizeof(config));
+ config.flags = flags;
+
+ memset(&req, 0, sizeof(req));
+
+ for (i = 0; i < num_lines; i++)
+ req.offsets[i] = line[i];
+ req.num_lines = num_lines;
+
+ req.config = config;
+ strcpy(req.consumer, "gpio-event-gen");
+
+ ret = ioctl(fd, GPIO_V2_GET_LINE_IOCTL, &req);
+ if (ret == -1) {
+ ret = -errno;
+ fprintf(stderr, "Failed to issue GET EVENT IOCTL (%d)\n", ret);
+ goto exit_close_error;
+ }
+
+ if (req.num_lines == 1) {
+ fprintf(stdout, "Generating events on line %u on %s\n",
+ line[0], device_name);
+ } else {
+ fprintf(stdout, "Generating events on %s for line %u",
+ device_name, line[0]);
+ for (i = 1; i < num_lines; i++)
+ fprintf(stdout, " line %u", line[i]);
+ }
+
+ clock_gettime(CLOCK_REALTIME, &now);
+ trigger_time = TIMESPEC_TO_U64(now);
+ trigger_time -= trigger_time % NSEC_PER_SEC;
+ trigger_time += 2 * NSEC_PER_SEC;
+ i = 0;
+ while (1) {
+ struct gpio_output_event out_event;
+
+ out_event.timestamp = trigger_time;
+ printf("GPIO EVENT TRIGGER: %llu\n", trigger_time);
+ ret = write(req.fd, &out_event, sizeof(out_event));
+ if (ret == -1) {
+ ret = -errno;
+ fprintf(stderr, "Failed to write event spec(%s)\n",
+ strerror(-ret));
+ break;
+ }
+
+ if (ret != sizeof(out_event)) {
+ fprintf(stderr, "Writing event spec failed\n");
+ ret = -EIO;
+ break;
+ }
+
+ sleep_until(trigger_time + NSEC_PER_SEC / 10);
+ trigger_time += NSEC_PER_SEC / 2;
+
+ i++;
+ if (i == loops)
+ break;
+ }
+
+exit_close_error:
+ if (close(fd) == -1)
+ perror("Failed to close GPIO character device file");
+ free(chrdev_name);
+ return ret;
+}
+
+void print_usage(void)
+{
+ fprintf(stderr, "Usage: gpio-event-gen [options]...;"
+ "Listen to events on GPIO lines, 0->1 1->0;"
+ " -n <name> Listen on GPIOs on a named device;"
+ "(must be stated);"
+ " -o <n> Offset to monitor;"
+ " [-c <n>] Do <n> loops;"
+ "(optional, infinite loop if not stated);"
+ " -? This helptext;"
+ "Example: gpio-event-gen -n gpiochip0 -o 0"
+ );
+}
+
+int main(int argc, char **argv)
+{
+ uint32_t flags = GPIO_V2_LINE_FLAG_OUTPUT;
+ const char *device_name = NULL;
+ unsigned int lines[GPIO_V2_LINES_MAX];
+ unsigned int loops = 0;
+ int num_lines = 0;
+ int c;
+
+ while ((c = getopt(argc, argv, "c:n:o:dsrf?")) != -1) {
+ switch (c) {
+ case 'c':
+ loops = strtoul(optarg, NULL, 10);
+ break;
+ case 'n':
+ device_name = optarg;
+ break;
+ case 'o':
+ if (num_lines >= GPIO_V2_LINES_MAX) {
+ print_usage();
+ return -1;
+ }
+ lines[num_lines] = strtoul(optarg, NULL, 10);
+ num_lines++;
+ break;
+ case '?':
+ print_usage();
+ return -1;
+ }
+ }
+
+ if (!device_name || num_lines == -1) {
+ print_usage();
+ return -1;
+ }
+
+ return generate_events(device_name, lines, num_lines,
+ flags, loops);
+}
--
2.17.1