From 6d591c46bce037696d173bd5c8461b2b4bb680ad Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 21 Oct 2015 15:45:54 +0200 Subject: tools/gpio: create GPIO tools This creates GPIO tools under tools/gpio/* and adds a single example program to list the GPIOs on a system. When proper devices are created it provides this minimal output: Cc: Johan Hovold Cc: Michael Welling Cc: Markus Pargmann Signed-off-by: Linus Walleij --- tools/Makefile | 8 +-- tools/gpio/Makefile | 12 +++++ tools/gpio/gpio-utils.c | 11 +++++ tools/gpio/gpio-utils.h | 25 ++++++++++ tools/gpio/lsgpio.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 tools/gpio/Makefile create mode 100644 tools/gpio/gpio-utils.c create mode 100644 tools/gpio/gpio-utils.h create mode 100644 tools/gpio/lsgpio.c (limited to 'tools') diff --git a/tools/Makefile b/tools/Makefile index 6339f6ac3ccb..f41e7c6ea23e 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -13,6 +13,7 @@ help: @echo ' cpupower - a tool for all things x86 CPU power' @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer' @echo ' freefall - laptop accelerometer program for disk protection' + @echo ' gpio - GPIO tools' @echo ' hv - tools used when in Hyper-V clients' @echo ' iio - IIO tools' @echo ' lguest - a minimal 32-bit x86 hypervisor' @@ -53,7 +54,7 @@ acpi: FORCE cpupower: FORCE $(call descend,power/$@) -cgroup firewire hv guest spi usb virtio vm net iio: FORCE +cgroup firewire hv guest spi usb virtio vm net iio gpio: FORCE $(call descend,$@) liblockdep: FORCE @@ -119,7 +120,7 @@ acpi_clean: cpupower_clean: $(call descend,power/cpupower,clean) -cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean: +cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean: $(call descend,$(@:_clean=),clean) liblockdep_clean: @@ -155,6 +156,7 @@ build_clean: clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \ perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ - freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean + freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \ + gpio_clean .PHONY: FORCE diff --git a/tools/gpio/Makefile b/tools/gpio/Makefile new file mode 100644 index 000000000000..4d198d5c4203 --- /dev/null +++ b/tools/gpio/Makefile @@ -0,0 +1,12 @@ +CC = $(CROSS_COMPILE)gcc +CFLAGS += -Wall -g -D_GNU_SOURCE + +all: lsgpio + +lsgpio: lsgpio.o gpio-utils.o + +%.o: %.c gpio-utils.h + +.PHONY: clean +clean: + rm -f *.o lsgpio diff --git a/tools/gpio/gpio-utils.c b/tools/gpio/gpio-utils.c new file mode 100644 index 000000000000..8208718f2c99 --- /dev/null +++ b/tools/gpio/gpio-utils.c @@ -0,0 +1,11 @@ +/* + * GPIO tools - helpers library for the GPIO tools + * + * Copyright (C) 2015 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. + */ + +#include "gpio-utils.h" diff --git a/tools/gpio/gpio-utils.h b/tools/gpio/gpio-utils.h new file mode 100644 index 000000000000..b18209a45ad3 --- /dev/null +++ b/tools/gpio/gpio-utils.h @@ -0,0 +1,25 @@ +/* + * GPIO tools - utility helpers library for the GPIO tools + * + * Copyright (C) 2015 Linus Walleij + * + * Portions copied from iio_utils and lssio: + * Copyright (c) 2010 Manuel Stahl + * Copyright (c) 2008 Jonathan Cameron + * * + * 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. + */ +#ifndef _GPIO_UTILS_H_ +#define _GPIO_UTILS_H_ + +#include + +static inline int check_prefix(const char *str, const char *prefix) +{ + return strlen(str) > strlen(prefix) && + strncmp(str, prefix, strlen(prefix)) == 0; +} + +#endif /* _GPIO_UTILS_H_ */ diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c new file mode 100644 index 000000000000..4cfe29da279b --- /dev/null +++ b/tools/gpio/lsgpio.c @@ -0,0 +1,128 @@ +/* + * lsgpio - example on how to list the GPIO lines on a system + * + * Copyright (C) 2015 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: + * lsgpio <-n device-name> + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpio-utils.h" + +int list_device(const char *device_name) +{ + struct gpiochip_info cinfo; + char *chrdev_name; + int fd; + int ret; + + 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 free_chrdev_name; + } + + /* Inspect this GPIO chip */ + ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &cinfo); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to retrieve GPIO fd\n"); + if (close(fd) == -1) + perror("Failed to close GPIO character device file"); + + goto free_chrdev_name; + } + fprintf(stdout, "GPIO chip: %s, %u GPIO lines\n", + cinfo.name, cinfo.lines); + + if (close(fd) == -1) { + ret = -errno; + goto free_chrdev_name; + } + +free_chrdev_name: + free(chrdev_name); + + return ret; + +} + +void print_usage(void) +{ + fprintf(stderr, "Usage: lsgpio [options]...\n" + "List GPIO chips, lines and states\n" + " -n List GPIOs on a named device\n" + " -? This helptext\n" + ); +} + +int main(int argc, char **argv) +{ + const char *device_name; + int ret; + int c; + + while ((c = getopt(argc, argv, "n:")) != -1) { + switch (c) { + case 'n': + device_name = optarg; + break; + case '?': + print_usage(); + return -1; + } + } + + if (device_name) + ret = list_device(device_name); + else { + const struct dirent *ent; + DIR *dp; + + /* List all GPIO devices one at a time */ + dp = opendir("/dev"); + if (!dp) { + ret = -errno; + goto error_out; + } + + ret = -ENOENT; + while (ent = readdir(dp), ent) { + if (check_prefix(ent->d_name, "gpiochip")) { + ret = list_device(ent->d_name); + if (ret) + break; + } + } + + ret = 0; + if (closedir(dp) == -1) { + perror("scanning devices: Failed to close directory"); + ret = -errno; + } + } +error_out: + return ret; +} -- cgit v1.2.3 From df4878e969ccc047da45d2cd3af5d08031da1593 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 12 Feb 2016 14:48:23 +0100 Subject: gpio: store reflect the label to userspace The gpio_chip label is useful for userspace to understand what kind of GPIO chip it is dealing with. Let's store a copy of this label in the gpio_device, add it to the struct passed to userspace for GPIO_GET_CHIPINFO_IOCTL and modify lsgpio to show it. Signed-off-by: Linus Walleij --- tools/gpio/lsgpio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c index 4cfe29da279b..692233f561fb 100644 --- a/tools/gpio/lsgpio.c +++ b/tools/gpio/lsgpio.c @@ -54,8 +54,8 @@ int list_device(const char *device_name) goto free_chrdev_name; } - fprintf(stdout, "GPIO chip: %s, %u GPIO lines\n", - cinfo.name, cinfo.lines); + fprintf(stdout, "GPIO chip: %s, \"%s\", %u GPIO lines\n", + cinfo.name, cinfo.label, cinfo.lines); if (close(fd) == -1) { ret = -errno; -- cgit v1.2.3 From 521a2ad6f862a28e2e43cb3e254a26bf0f9452e9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 12 Feb 2016 22:25:22 +0100 Subject: gpio: add userspace ABI for GPIO line information This adds a GPIO line ABI for getting name, label and a few select flags from the kernel. This hides the kernel internals and only tells userspace what it may need to know: the different in-kernel consumers are masked behind the flag "kernel" and that is all userspace needs to know. However electric characteristics like active low, open drain etc are reflected to userspace, as this is important information. We provide information on all lines on all chips, later on we will likely add a flag for the chardev consumer so we can filter and display only the lines userspace actually uses in e.g. lsgpio, but then we first need an ABI for userspace to grab and use (get/set/select direction) a GPIO line. Sample output from "lsgpio" on ux500: GPIO chip: gpiochip7, "8011e000.gpio", 32 GPIO lines line 0: unnamed unlabeled line 1: unnamed unlabeled (...) line 25: unnamed "SFH7741 Proximity Sensor" [kernel output open-drain] line 26: unnamed unlabeled (...) Tested-by: Michael Welling Signed-off-by: Linus Walleij --- tools/gpio/gpio-utils.h | 2 ++ tools/gpio/lsgpio.c | 91 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 81 insertions(+), 12 deletions(-) (limited to 'tools') diff --git a/tools/gpio/gpio-utils.h b/tools/gpio/gpio-utils.h index b18209a45ad3..5f57133b8c04 100644 --- a/tools/gpio/gpio-utils.h +++ b/tools/gpio/gpio-utils.h @@ -16,6 +16,8 @@ #include +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + static inline int check_prefix(const char *str, const char *prefix) { return strlen(str) > strlen(prefix) && diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c index 692233f561fb..5535ce81f8f7 100644 --- a/tools/gpio/lsgpio.c +++ b/tools/gpio/lsgpio.c @@ -26,12 +26,56 @@ #include "gpio-utils.h" +struct gpio_flag { + char *name; + unsigned long mask; +}; + +struct gpio_flag flagnames[] = { + { + .name = "kernel", + .mask = GPIOLINE_FLAG_KERNEL, + }, + { + .name = "output", + .mask = GPIOLINE_FLAG_IS_OUT, + }, + { + .name = "active-low", + .mask = GPIOLINE_FLAG_ACTIVE_LOW, + }, + { + .name = "open-drain", + .mask = GPIOLINE_FLAG_OPEN_DRAIN, + }, + { + .name = "open-source", + .mask = GPIOLINE_FLAG_OPEN_SOURCE, + }, +}; + +void print_flags(unsigned long flags) +{ + int i; + int printed = 0; + + for (i = 0; i < ARRAY_SIZE(flagnames); i++) { + if (flags & flagnames[i].mask) { + if (printed) + fprintf(stdout, " "); + fprintf(stdout, "%s", flagnames[i].name); + printed++; + } + } +} + int list_device(const char *device_name) { struct gpiochip_info cinfo; char *chrdev_name; int fd; int ret; + int i; ret = asprintf(&chrdev_name, "/dev/%s", device_name); if (ret < 0) @@ -41,32 +85,55 @@ int list_device(const char *device_name) if (fd == -1) { ret = -errno; fprintf(stderr, "Failed to open %s\n", chrdev_name); - goto free_chrdev_name; + goto exit_close_error; } /* Inspect this GPIO chip */ ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &cinfo); if (ret == -1) { ret = -errno; - fprintf(stderr, "Failed to retrieve GPIO fd\n"); - if (close(fd) == -1) - perror("Failed to close GPIO character device file"); - - goto free_chrdev_name; + perror("Failed to issue CHIPINFO IOCTL\n"); + goto exit_close_error; } fprintf(stdout, "GPIO chip: %s, \"%s\", %u GPIO lines\n", cinfo.name, cinfo.label, cinfo.lines); - if (close(fd) == -1) { - ret = -errno; - goto free_chrdev_name; + /* Loop over the lines and print info */ + for (i = 0; i < cinfo.lines; i++) { + struct gpioline_info linfo; + + memset(&linfo, 0, sizeof(linfo)); + linfo.line_offset = i; + + ret = ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &linfo); + if (ret == -1) { + ret = -errno; + perror("Failed to issue LINEINFO IOCTL\n"); + goto exit_close_error; + } + fprintf(stdout, "\tline %d:", linfo.line_offset); + if (linfo.name[0]) + fprintf(stdout, " %s", linfo.name); + else + fprintf(stdout, " unnamed"); + if (linfo.label[0]) + fprintf(stdout, " \"%s\"", linfo.label); + else + fprintf(stdout, " unlabeled"); + if (linfo.flags) { + fprintf(stdout, " ["); + print_flags(linfo.flags); + fprintf(stdout, "]"); + } + fprintf(stdout, "\n"); + } -free_chrdev_name: +exit_close_error: + if (close(fd) == -1) + perror("Failed to close GPIO character device file"); free(chrdev_name); - return ret; - } void print_usage(void) -- cgit v1.2.3 From bb91d345b49f9ca8ccf3ed9cc3346decdd9c4c3f Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Tue, 23 Feb 2016 08:54:46 +0100 Subject: tools: gpio: Small updates for output format Use %2d for the GPIO line number. This should align the results horziontally for most gpio chips. The GPIO label uses quotes for real values. For GPIO names this is currently missing. The patch adds the missing quote. Signed-off-by: Markus Pargmann Signed-off-by: Linus Walleij --- tools/gpio/lsgpio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c index 5535ce81f8f7..6af118cc7efb 100644 --- a/tools/gpio/lsgpio.c +++ b/tools/gpio/lsgpio.c @@ -111,9 +111,9 @@ int list_device(const char *device_name) perror("Failed to issue LINEINFO IOCTL\n"); goto exit_close_error; } - fprintf(stdout, "\tline %d:", linfo.line_offset); + fprintf(stdout, "\tline %2d:", linfo.line_offset); if (linfo.name[0]) - fprintf(stdout, " %s", linfo.name); + fprintf(stdout, " \"%s\"", linfo.name); else fprintf(stdout, " unnamed"); if (linfo.label[0]) -- cgit v1.2.3 From 214338e372af2b856af07978daa771dbe087f990 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 25 Feb 2016 21:01:48 +0100 Subject: gpio: present the consumer of a line to userspace I named the field representing the current user of GPIO line as "label" but this is too vague and ambiguous. Before anyone gets confused, rename it to "consumer" and indicate clearly in the documentation that this is a string set by the user of the line. Also clean up leftovers in the documentation. Signed-off-by: Linus Walleij --- tools/gpio/lsgpio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c index 6af118cc7efb..1124da375942 100644 --- a/tools/gpio/lsgpio.c +++ b/tools/gpio/lsgpio.c @@ -116,10 +116,10 @@ int list_device(const char *device_name) fprintf(stdout, " \"%s\"", linfo.name); else fprintf(stdout, " unnamed"); - if (linfo.label[0]) - fprintf(stdout, " \"%s\"", linfo.label); + if (linfo.consumer[0]) + fprintf(stdout, " \"%s\"", linfo.consumer); else - fprintf(stdout, " unlabeled"); + fprintf(stdout, " unused"); if (linfo.flags) { fprintf(stdout, " ["); print_flags(linfo.flags); -- cgit v1.2.3