From 2a144dd09118d9ba8cb57ef65eb573af222c455c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 26 Apr 2016 10:47:09 +0200 Subject: tools/gpio: add the gpio-hammer tool The gpio-hammer is used from userspace as an example of how to retrieve a GPIO handle for one or several GPIO lines and hammer the outputs from low to high and back again. It will pulse the selected lines once per second for a specified number of times or indefinitely if no loop count is supplied. Example output: $ gpio-hammer -n gpiochip0 -o5 -o6 -o7 Hammer lines [5, 6, 7] on gpiochip0, initial states: [1, 1, 1] [-] [5: 0, 6: 0, 7: 0] Tested-by: Michael Welling Signed-off-by: Linus Walleij --- tools/gpio/Makefile | 5 +- tools/gpio/gpio-hammer.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 tools/gpio/gpio-hammer.c (limited to 'tools') diff --git a/tools/gpio/Makefile b/tools/gpio/Makefile index c155d6bc47a7..aea23949054e 100644 --- a/tools/gpio/Makefile +++ b/tools/gpio/Makefile @@ -1,12 +1,13 @@ CC = $(CROSS_COMPILE)gcc CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -all: lsgpio +all: lsgpio gpio-hammer lsgpio: lsgpio.o gpio-utils.o +gpio-hammer: gpio-hammer.o gpio-utils.o %.o: %.c gpio-utils.h .PHONY: clean clean: - rm -f *.o lsgpio + rm -f *.o lsgpio gpio-hammer diff --git a/tools/gpio/gpio-hammer.c b/tools/gpio/gpio-hammer.c new file mode 100644 index 000000000000..37b3f141053d --- /dev/null +++ b/tools/gpio/gpio-hammer.c @@ -0,0 +1,189 @@ +/* + * gpio-hammer - example swiss army knife to shake GPIO lines on a system + * + * Copyright (C) 2016 Linus Walleij + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * Usage: + * gpio-hammer -n -o -o + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int hammer_device(const char *device_name, unsigned int *lines, int nlines, + unsigned int loops) +{ + struct gpiohandle_request req; + struct gpiohandle_data data; + char *chrdev_name; + char swirr[] = "-\\|/"; + int fd; + int ret; + int i, j; + unsigned int iteration = 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; + } + + /* Request lines as output */ + for (i = 0; i < nlines; i++) + req.lineoffsets[i] = lines[i]; + req.flags = GPIOHANDLE_REQUEST_OUTPUT; /* Request as output */ + strcpy(req.consumer_label, "gpio-hammer"); + req.lines = nlines; + ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue GET LINEHANDLE " + "IOCTL (%d)\n", + ret); + goto exit_close_error; + } + + /* Read initial states */ + ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE " + "VALUES IOCTL (%d)\n", + ret); + goto exit_close_error; + } + fprintf(stdout, "Hammer lines ["); + for (i = 0; i < nlines; i++) { + fprintf(stdout, "%d", lines[i]); + if (i != (nlines - 1)) + fprintf(stdout, ", "); + } + fprintf(stdout, "] on %s, initial states: [", device_name); + for (i = 0; i < nlines; i++) { + fprintf(stdout, "%d", data.values[i]); + if (i != (nlines - 1)) + fprintf(stdout, ", "); + } + fprintf(stdout, "]\n"); + + /* Hammertime! */ + j = 0; + while (1) { + /* Invert all lines so we blink */ + for (i = 0; i < nlines; i++) + data.values[i] = !data.values[i]; + + ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue GPIOHANDLE SET LINE " + "VALUES IOCTL (%d)\n", + ret); + goto exit_close_error; + } + /* Re-read values to get status */ + ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE " + "VALUES IOCTL (%d)\n", + ret); + goto exit_close_error; + } + + fprintf(stdout, "[%c] ", swirr[j]); + j++; + if (j == sizeof(swirr)-1) + j = 0; + + fprintf(stdout, "["); + for (i = 0; i < nlines; i++) { + fprintf(stdout, "%d: %d", lines[i], data.values[i]); + if (i != (nlines - 1)) + fprintf(stdout, ", "); + } + fprintf(stdout, "]\r"); + fflush(stdout); + sleep(1); + iteration++; + if (loops && iteration == loops) + break; + } + fprintf(stdout, "\n"); + ret = 0; + +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-hammer [options]...\n" + "Hammer GPIO lines, 0->1->0->1...\n" + " -n Hammer GPIOs on a named device (must be stated)\n" + " -o Offset[s] to hammer, at least one, several can be stated\n" + " [-c ] Do loops (optional, infinite loop if not stated)\n" + " -? This helptext\n" + "\n" + "Example:\n" + "gpio-hammer -n gpiochip0 -o 4\n" + ); +} + +int main(int argc, char **argv) +{ + const char *device_name = NULL; + unsigned int lines[GPIOHANDLES_MAX]; + unsigned int loops = 0; + int nlines; + int c; + int i; + + i = 0; + while ((c = getopt(argc, argv, "c:n:o:?")) != -1) { + switch (c) { + case 'c': + loops = strtoul(optarg, NULL, 10); + break; + case 'n': + device_name = optarg; + break; + case 'o': + lines[i] = strtoul(optarg, NULL, 10); + i++; + break; + case '?': + print_usage(); + return -1; + } + } + nlines = i; + + if (!device_name || !nlines) { + print_usage(); + return -1; + } + return hammer_device(device_name, lines, nlines, loops); +} -- cgit From 97f69747d8b11c76df321894344beeae6bafc301 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 2 Jun 2016 11:38:33 +0200 Subject: tools/gpio: add the gpio-event-mon tool The gpio-event-mon is used from userspace as an example of how to monitor GPIO line events. It will latch on to a certain GPIO line on a certain gpiochip and print timestamped events as they arrive. Example output: $ gpio-event-mon -n gpiochip2 -o 0 -r -f Monitoring line 0 on gpiochip2 Initial line value: 1 GPIO EVENT 946685798487609863: falling edge GPIO EVENT 946685798732482910: rising edge GPIO EVENT 946685799115997314: falling edge GPIO EVENT 946685799381469726: rising edge Signed-off-by: Linus Walleij --- tools/gpio/Makefile | 5 +- tools/gpio/gpio-event-mon.c | 192 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 tools/gpio/gpio-event-mon.c (limited to 'tools') diff --git a/tools/gpio/Makefile b/tools/gpio/Makefile index aea23949054e..619314ff9bd6 100644 --- a/tools/gpio/Makefile +++ b/tools/gpio/Makefile @@ -1,13 +1,14 @@ CC = $(CROSS_COMPILE)gcc CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -all: lsgpio gpio-hammer +all: lsgpio gpio-hammer gpio-event-mon lsgpio: lsgpio.o gpio-utils.o gpio-hammer: gpio-hammer.o gpio-utils.o +gpio-event-mon: gpio-event-mon.o gpio-utils.o %.o: %.c gpio-utils.h .PHONY: clean clean: - rm -f *.o lsgpio gpio-hammer + rm -f *.o lsgpio gpio-hammer gpio-event-mon diff --git a/tools/gpio/gpio-event-mon.c b/tools/gpio/gpio-event-mon.c new file mode 100644 index 000000000000..448ed96b3b4f --- /dev/null +++ b/tools/gpio/gpio-event-mon.c @@ -0,0 +1,192 @@ +/* + * gpio-hammer - example swiss army knife to shake GPIO lines on a system + * + * Copyright (C) 2016 Linus Walleij + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * Usage: + * gpio-event-mon -n -o + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int monitor_device(const char *device_name, + unsigned int line, + u_int32_t handleflags, + u_int32_t eventflags, + unsigned int loops) +{ + struct gpioevent_request req; + struct gpiohandle_data data; + char *chrdev_name; + int fd; + int ret; + 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; + } + + req.lineoffset = line; + req.handleflags = handleflags; + req.eventflags = eventflags; + strcpy(req.consumer_label, "gpio-event-mon"); + + ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &req); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue GET EVENT " + "IOCTL (%d)\n", + ret); + goto exit_close_error; + } + + /* Read initial states */ + ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE " + "VALUES IOCTL (%d)\n", + ret); + goto exit_close_error; + } + + fprintf(stdout, "Monitoring line %d on %s\n", line, device_name); + fprintf(stdout, "Initial line value: %d\n", data.values[0]); + + while (1) { + struct gpioevent_data event; + + ret = read(req.fd, &event, sizeof(event)); + if (ret == -1) { + if (errno == -EAGAIN) { + fprintf(stderr, "nothing available\n"); + continue; + } else { + ret = -errno; + fprintf(stderr, "Failed to read event (%d)\n", + ret); + break; + } + } + + if (ret != sizeof(event)) { + fprintf(stderr, "Reading event failed\n"); + ret = -EIO; + break; + } + fprintf(stdout, "GPIO EVENT %" PRIu64 ": ", event.timestamp); + switch (event.id) { + case GPIOEVENT_EVENT_RISING_EDGE: + fprintf(stdout, "rising edge"); + break; + case GPIOEVENT_EVENT_FALLING_EDGE: + fprintf(stdout, "falling edge"); + break; + default: + fprintf(stdout, "unknown event"); + } + fprintf(stdout, "\n"); + + 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-mon [options]...\n" + "Listen to events on GPIO lines, 0->1 1->0\n" + " -n Listen on GPIOs on a named device (must be stated)\n" + " -o Offset to monitor\n" + " -d Set line as open drain\n" + " -s Set line as open source\n" + " -r Listen for rising edges\n" + " -f Listen for falling edges\n" + " [-c ] Do loops (optional, infinite loop if not stated)\n" + " -? This helptext\n" + "\n" + "Example:\n" + "gpio-event-mon -n gpiochip0 -o 4 -r -f\n" + ); +} + +int main(int argc, char **argv) +{ + const char *device_name = NULL; + unsigned int line = -1; + unsigned int loops = 0; + u_int32_t handleflags = GPIOHANDLE_REQUEST_INPUT; + u_int32_t eventflags = 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': + line = strtoul(optarg, NULL, 10); + break; + case 'd': + handleflags |= GPIOHANDLE_REQUEST_OPEN_DRAIN; + break; + case 's': + handleflags |= GPIOHANDLE_REQUEST_OPEN_SOURCE; + break; + case 'r': + eventflags |= GPIOEVENT_REQUEST_RISING_EDGE; + break; + case 'f': + eventflags |= GPIOEVENT_REQUEST_FALLING_EDGE; + break; + case '?': + print_usage(); + return -1; + } + } + + if (!device_name || line == -1) { + print_usage(); + return -1; + } + if (!eventflags) { + printf("No flags specified, listening on both rising and " + "falling edges\n"); + eventflags = GPIOEVENT_REQUEST_BOTH_EDGES; + } + return monitor_device(device_name, line, handleflags, + eventflags, loops); +} -- cgit From 8674cea84dc6c0b98d8f45d6f2cf348864aabf1b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 19 Jun 2016 23:41:11 +0300 Subject: tools/gpio: move to tools buildsystem There is a nice buildsystem dedicated for userspace tools in Linux kernel tree. Switch gpio target to be built by it. Signed-off-by: Andy Shevchenko Acked-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Linus Walleij --- tools/gpio/Build | 3 +++ tools/gpio/Makefile | 69 ++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 tools/gpio/Build (limited to 'tools') diff --git a/tools/gpio/Build b/tools/gpio/Build new file mode 100644 index 000000000000..620c1937d957 --- /dev/null +++ b/tools/gpio/Build @@ -0,0 +1,3 @@ +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 diff --git a/tools/gpio/Makefile b/tools/gpio/Makefile index 619314ff9bd6..5a82f15d0071 100644 --- a/tools/gpio/Makefile +++ b/tools/gpio/Makefile @@ -1,14 +1,67 @@ +include ../scripts/Makefile.include + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(shell pwd))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +endif + +# Do not use make's built-in rules +# (this improves performance and avoids hard-to-debug behaviour); +MAKEFLAGS += -r + CC = $(CROSS_COMPILE)gcc -CFLAGS += -O2 -Wall -g -D_GNU_SOURCE +LD = $(CROSS_COMPILE)ld +CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include + +ALL_TARGETS := lsgpio gpio-hammer gpio-event-mon +ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS)) + +all: $(ALL_PROGRAMS) -all: lsgpio gpio-hammer gpio-event-mon +export srctree OUTPUT CC LD CFLAGS +include $(srctree)/tools/build/Makefile.include -lsgpio: lsgpio.o gpio-utils.o -gpio-hammer: gpio-hammer.o gpio-utils.o -gpio-event-mon: gpio-event-mon.o gpio-utils.o +# +# We need the following to be outside of kernel tree +# +$(OUTPUT)include/linux/gpio.h: ../../include/uapi/linux/gpio.h + mkdir -p $(OUTPUT)include/linux 2>&1 || true + ln -sf $(CURDIR)/../../include/uapi/linux/gpio.h $@ -%.o: %.c gpio-utils.h +prepare: $(OUTPUT)include/linux/gpio.h + +# +# lsgpio +# +LSGPIO_IN := $(OUTPUT)lsgpio-in.o +$(LSGPIO_IN): prepare FORCE + $(Q)$(MAKE) $(build)=lsgpio +$(OUTPUT)lsgpio: $(LSGPIO_IN) + $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +# +# gpio-hammer +# +GPIO_HAMMER_IN := $(OUTPUT)gpio-hammer-in.o +$(GPIO_HAMMER_IN): prepare FORCE + $(Q)$(MAKE) $(build)=gpio-hammer +$(OUTPUT)gpio-hammer: $(GPIO_HAMMER_IN) + $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +# +# gpio-event-mon +# +GPIO_EVENT_MON_IN := $(OUTPUT)gpio-event-mon-in.o +$(GPIO_EVENT_MON_IN): prepare FORCE + $(Q)$(MAKE) $(build)=gpio-event-mon +$(OUTPUT)gpio-event-mon: $(GPIO_EVENT_MON_IN) + $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ -.PHONY: clean clean: - rm -f *.o lsgpio gpio-hammer gpio-event-mon + rm -f $(ALL_PROGRAMS) + rm -f $(OUTPUT)include/linux/gpio.h + find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete + +FORCE: + +.PHONY: all clean FORCE prepare -- cgit From 53499109289846ac115ff2cfc78f0637df5b473d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 19 Jun 2016 23:41:12 +0300 Subject: tools/gpio: add install section Allow user to call install target. Signed-off-by: Andy Shevchenko Acked-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Linus Walleij --- tools/Makefile | 7 ++++--- tools/gpio/Makefile | 10 +++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/Makefile b/tools/Makefile index f10b64d8c674..daa8fb3e4363 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -85,7 +85,7 @@ tmon: FORCE freefall: FORCE $(call descend,laptop/$@) -all: acpi cgroup cpupower hv firewire lguest \ +all: acpi cgroup cpupower gpio hv firewire lguest \ perf selftests turbostat usb \ virtio vm net x86_energy_perf_policy \ tmon freefall objtool @@ -96,7 +96,7 @@ acpi_install: cpupower_install: $(call descend,power/$(@:_install=),install) -cgroup_install firewire_install hv_install lguest_install perf_install usb_install virtio_install vm_install net_install objtool_install: +cgroup_install firewire_install gpio_install hv_install lguest_install perf_install usb_install virtio_install vm_install net_install objtool_install: $(call descend,$(@:_install=),install) selftests_install: @@ -114,7 +114,8 @@ freefall_install: kvm_stat_install: $(call descend,kvm/$(@:_install=),install) -install: acpi_install cgroup_install cpupower_install hv_install firewire_install lguest_install \ +install: acpi_install cgroup_install cpupower_install gpio_install \ + hv_install firewire_install lguest_install \ perf_install selftests_install turbostat_install usb_install \ virtio_install vm_install net_install x86_energy_perf_policy_install \ tmon_install freefall_install objtool_install kvm_stat_install diff --git a/tools/gpio/Makefile b/tools/gpio/Makefile index 5a82f15d0071..250a891e6ef0 100644 --- a/tools/gpio/Makefile +++ b/tools/gpio/Makefile @@ -1,5 +1,7 @@ include ../scripts/Makefile.include +bindir ?= /usr/bin + ifeq ($(srctree),) srctree := $(patsubst %/,%,$(dir $(shell pwd))) srctree := $(patsubst %/,%,$(dir $(srctree))) @@ -62,6 +64,12 @@ clean: rm -f $(OUTPUT)include/linux/gpio.h find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete +install: $(ALL_PROGRAMS) + install -d -m 755 $(DESTDIR)$(bindir); \ + for program in $(ALL_PROGRAMS); do \ + install $$program $(DESTDIR)$(bindir); \ + done + FORCE: -.PHONY: all clean FORCE prepare +.PHONY: all install clean FORCE prepare -- cgit