summaryrefslogtreecommitdiffstats
path: root/tools/gpio/lsgpio.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/gpio/lsgpio.c')
-rw-r--r--tools/gpio/lsgpio.c225
1 files changed, 225 insertions, 0 deletions
diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c
new file mode 100644
index 000000000..52a0be454
--- /dev/null
+++ b/tools/gpio/lsgpio.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * lsgpio - example on how to list the GPIO lines on a system
+ *
+ * Copyright (C) 2015 Linus Walleij
+ *
+ * Usage:
+ * lsgpio <-n device-name>
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <linux/gpio.h>
+
+#include "gpio-utils.h"
+
+struct gpio_flag {
+ char *name;
+ unsigned long long mask;
+};
+
+struct gpio_flag flagnames[] = {
+ {
+ .name = "used",
+ .mask = GPIO_V2_LINE_FLAG_USED,
+ },
+ {
+ .name = "input",
+ .mask = GPIO_V2_LINE_FLAG_INPUT,
+ },
+ {
+ .name = "output",
+ .mask = GPIO_V2_LINE_FLAG_OUTPUT,
+ },
+ {
+ .name = "active-low",
+ .mask = GPIO_V2_LINE_FLAG_ACTIVE_LOW,
+ },
+ {
+ .name = "open-drain",
+ .mask = GPIO_V2_LINE_FLAG_OPEN_DRAIN,
+ },
+ {
+ .name = "open-source",
+ .mask = GPIO_V2_LINE_FLAG_OPEN_SOURCE,
+ },
+ {
+ .name = "pull-up",
+ .mask = GPIO_V2_LINE_FLAG_BIAS_PULL_UP,
+ },
+ {
+ .name = "pull-down",
+ .mask = GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN,
+ },
+ {
+ .name = "bias-disabled",
+ .mask = GPIO_V2_LINE_FLAG_BIAS_DISABLED,
+ },
+ {
+ .name = "clock-realtime",
+ .mask = GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME,
+ },
+};
+
+static void print_attributes(struct gpio_v2_line_info *info)
+{
+ int i;
+ const char *field_format = "%s";
+
+ for (i = 0; i < ARRAY_SIZE(flagnames); i++) {
+ if (info->flags & flagnames[i].mask) {
+ fprintf(stdout, field_format, flagnames[i].name);
+ field_format = ", %s";
+ }
+ }
+
+ if ((info->flags & GPIO_V2_LINE_FLAG_EDGE_RISING) &&
+ (info->flags & GPIO_V2_LINE_FLAG_EDGE_FALLING))
+ fprintf(stdout, field_format, "both-edges");
+ else if (info->flags & GPIO_V2_LINE_FLAG_EDGE_RISING)
+ fprintf(stdout, field_format, "rising-edge");
+ else if (info->flags & GPIO_V2_LINE_FLAG_EDGE_FALLING)
+ fprintf(stdout, field_format, "falling-edge");
+
+ for (i = 0; i < info->num_attrs; i++) {
+ if (info->attrs[i].id == GPIO_V2_LINE_ATTR_ID_DEBOUNCE)
+ fprintf(stdout, ", debounce_period=%dusec",
+ info->attrs[i].debounce_period_us);
+ }
+}
+
+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)
+ return -ENOMEM;
+
+ fd = open(chrdev_name, 0);
+ if (fd == -1) {
+ ret = -errno;
+ fprintf(stderr, "Failed to open %s\n", chrdev_name);
+ goto exit_free_name;
+ }
+
+ /* Inspect this GPIO chip */
+ ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &cinfo);
+ if (ret == -1) {
+ ret = -errno;
+ 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);
+
+ /* Loop over the lines and print info */
+ for (i = 0; i < cinfo.lines; i++) {
+ struct gpio_v2_line_info linfo;
+
+ memset(&linfo, 0, sizeof(linfo));
+ linfo.offset = i;
+
+ ret = ioctl(fd, GPIO_V2_GET_LINEINFO_IOCTL, &linfo);
+ if (ret == -1) {
+ ret = -errno;
+ perror("Failed to issue LINEINFO IOCTL\n");
+ goto exit_close_error;
+ }
+ fprintf(stdout, "\tline %2d:", linfo.offset);
+ if (linfo.name[0])
+ fprintf(stdout, " \"%s\"", linfo.name);
+ else
+ fprintf(stdout, " unnamed");
+ if (linfo.consumer[0])
+ fprintf(stdout, " \"%s\"", linfo.consumer);
+ else
+ fprintf(stdout, " unused");
+ if (linfo.flags) {
+ fprintf(stdout, " [");
+ print_attributes(&linfo);
+ fprintf(stdout, "]");
+ }
+ fprintf(stdout, "\n");
+
+ }
+
+exit_close_error:
+ if (close(fd) == -1)
+ perror("Failed to close GPIO character device file");
+exit_free_name:
+ free(chrdev_name);
+ return ret;
+}
+
+void print_usage(void)
+{
+ fprintf(stderr, "Usage: lsgpio [options]...\n"
+ "List GPIO chips, lines and states\n"
+ " -n <name> List GPIOs on a named device\n"
+ " -? This helptext\n"
+ );
+}
+
+int main(int argc, char **argv)
+{
+ const char *device_name = NULL;
+ 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;
+}