1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 26d591c46SLinus Walleij /* 36d591c46SLinus Walleij * lsgpio - example on how to list the GPIO lines on a system 46d591c46SLinus Walleij * 56d591c46SLinus Walleij * Copyright (C) 2015 Linus Walleij 66d591c46SLinus Walleij * 76d591c46SLinus Walleij * Usage: 86d591c46SLinus Walleij * lsgpio <-n device-name> 96d591c46SLinus Walleij */ 106d591c46SLinus Walleij 116d591c46SLinus Walleij #include <unistd.h> 126d591c46SLinus Walleij #include <stdlib.h> 136d591c46SLinus Walleij #include <stdbool.h> 146d591c46SLinus Walleij #include <stdio.h> 156d591c46SLinus Walleij #include <dirent.h> 166d591c46SLinus Walleij #include <errno.h> 176d591c46SLinus Walleij #include <string.h> 186d591c46SLinus Walleij #include <poll.h> 196d591c46SLinus Walleij #include <fcntl.h> 206d591c46SLinus Walleij #include <getopt.h> 216d591c46SLinus Walleij #include <sys/ioctl.h> 226d591c46SLinus Walleij #include <linux/gpio.h> 236d591c46SLinus Walleij 246d591c46SLinus Walleij #include "gpio-utils.h" 256d591c46SLinus Walleij 26521a2ad6SLinus Walleij struct gpio_flag { 27521a2ad6SLinus Walleij char *name; 283c333c47SKent Gibson unsigned long long mask; 29521a2ad6SLinus Walleij }; 30521a2ad6SLinus Walleij 31521a2ad6SLinus Walleij struct gpio_flag flagnames[] = { 32521a2ad6SLinus Walleij { 333c333c47SKent Gibson .name = "used", 343c333c47SKent Gibson .mask = GPIO_V2_LINE_FLAG_USED, 353c333c47SKent Gibson }, 363c333c47SKent Gibson { 373c333c47SKent Gibson .name = "input", 383c333c47SKent Gibson .mask = GPIO_V2_LINE_FLAG_INPUT, 39521a2ad6SLinus Walleij }, 40521a2ad6SLinus Walleij { 41521a2ad6SLinus Walleij .name = "output", 423c333c47SKent Gibson .mask = GPIO_V2_LINE_FLAG_OUTPUT, 43521a2ad6SLinus Walleij }, 44521a2ad6SLinus Walleij { 45521a2ad6SLinus Walleij .name = "active-low", 463c333c47SKent Gibson .mask = GPIO_V2_LINE_FLAG_ACTIVE_LOW, 47521a2ad6SLinus Walleij }, 48521a2ad6SLinus Walleij { 49521a2ad6SLinus Walleij .name = "open-drain", 503c333c47SKent Gibson .mask = GPIO_V2_LINE_FLAG_OPEN_DRAIN, 51521a2ad6SLinus Walleij }, 52521a2ad6SLinus Walleij { 53521a2ad6SLinus Walleij .name = "open-source", 543c333c47SKent Gibson .mask = GPIO_V2_LINE_FLAG_OPEN_SOURCE, 55521a2ad6SLinus Walleij }, 563831c051SKent Gibson { 573831c051SKent Gibson .name = "pull-up", 583c333c47SKent Gibson .mask = GPIO_V2_LINE_FLAG_BIAS_PULL_UP, 593831c051SKent Gibson }, 603831c051SKent Gibson { 613831c051SKent Gibson .name = "pull-down", 623c333c47SKent Gibson .mask = GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN, 633831c051SKent Gibson }, 643831c051SKent Gibson { 653831c051SKent Gibson .name = "bias-disabled", 663c333c47SKent Gibson .mask = GPIO_V2_LINE_FLAG_BIAS_DISABLED, 673831c051SKent Gibson }, 68521a2ad6SLinus Walleij }; 69521a2ad6SLinus Walleij 703c333c47SKent Gibson static void print_attributes(struct gpio_v2_line_info *info) 71521a2ad6SLinus Walleij { 72521a2ad6SLinus Walleij int i; 733c333c47SKent Gibson const char *field_format = "%s"; 74521a2ad6SLinus Walleij 75521a2ad6SLinus Walleij for (i = 0; i < ARRAY_SIZE(flagnames); i++) { 763c333c47SKent Gibson if (info->flags & flagnames[i].mask) { 773c333c47SKent Gibson fprintf(stdout, field_format, flagnames[i].name); 783c333c47SKent Gibson field_format = ", %s"; 79521a2ad6SLinus Walleij } 80521a2ad6SLinus Walleij } 813c333c47SKent Gibson 823c333c47SKent Gibson if ((info->flags & GPIO_V2_LINE_FLAG_EDGE_RISING) && 833c333c47SKent Gibson (info->flags & GPIO_V2_LINE_FLAG_EDGE_FALLING)) 843c333c47SKent Gibson fprintf(stdout, field_format, "both-edges"); 853c333c47SKent Gibson else if (info->flags & GPIO_V2_LINE_FLAG_EDGE_RISING) 863c333c47SKent Gibson fprintf(stdout, field_format, "rising-edge"); 873c333c47SKent Gibson else if (info->flags & GPIO_V2_LINE_FLAG_EDGE_FALLING) 883c333c47SKent Gibson fprintf(stdout, field_format, "falling-edge"); 893c333c47SKent Gibson 903c333c47SKent Gibson for (i = 0; i < info->num_attrs; i++) { 913c333c47SKent Gibson if (info->attrs[i].id == GPIO_V2_LINE_ATTR_ID_DEBOUNCE) 923c333c47SKent Gibson fprintf(stdout, ", debounce_period=%dusec", 933c333c47SKent Gibson info->attrs[0].debounce_period_us); 943c333c47SKent Gibson } 95521a2ad6SLinus Walleij } 96521a2ad6SLinus Walleij 976d591c46SLinus Walleij int list_device(const char *device_name) 986d591c46SLinus Walleij { 996d591c46SLinus Walleij struct gpiochip_info cinfo; 1006d591c46SLinus Walleij char *chrdev_name; 1016d591c46SLinus Walleij int fd; 1026d591c46SLinus Walleij int ret; 103521a2ad6SLinus Walleij int i; 1046d591c46SLinus Walleij 1056d591c46SLinus Walleij ret = asprintf(&chrdev_name, "/dev/%s", device_name); 1066d591c46SLinus Walleij if (ret < 0) 1076d591c46SLinus Walleij return -ENOMEM; 1086d591c46SLinus Walleij 1096d591c46SLinus Walleij fd = open(chrdev_name, 0); 1106d591c46SLinus Walleij if (fd == -1) { 1116d591c46SLinus Walleij ret = -errno; 1126d591c46SLinus Walleij fprintf(stderr, "Failed to open %s\n", chrdev_name); 113ef3c61a0SKent Gibson goto exit_free_name; 1146d591c46SLinus Walleij } 1156d591c46SLinus Walleij 1166d591c46SLinus Walleij /* Inspect this GPIO chip */ 1176d591c46SLinus Walleij ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &cinfo); 1186d591c46SLinus Walleij if (ret == -1) { 1196d591c46SLinus Walleij ret = -errno; 120521a2ad6SLinus Walleij perror("Failed to issue CHIPINFO IOCTL\n"); 121521a2ad6SLinus Walleij goto exit_close_error; 1226d591c46SLinus Walleij } 123df4878e9SLinus Walleij fprintf(stdout, "GPIO chip: %s, \"%s\", %u GPIO lines\n", 124df4878e9SLinus Walleij cinfo.name, cinfo.label, cinfo.lines); 1256d591c46SLinus Walleij 126521a2ad6SLinus Walleij /* Loop over the lines and print info */ 127521a2ad6SLinus Walleij for (i = 0; i < cinfo.lines; i++) { 1283c333c47SKent Gibson struct gpio_v2_line_info linfo; 129521a2ad6SLinus Walleij 130521a2ad6SLinus Walleij memset(&linfo, 0, sizeof(linfo)); 1313c333c47SKent Gibson linfo.offset = i; 132521a2ad6SLinus Walleij 1333c333c47SKent Gibson ret = ioctl(fd, GPIO_V2_GET_LINEINFO_IOCTL, &linfo); 134521a2ad6SLinus Walleij if (ret == -1) { 1356d591c46SLinus Walleij ret = -errno; 136521a2ad6SLinus Walleij perror("Failed to issue LINEINFO IOCTL\n"); 137521a2ad6SLinus Walleij goto exit_close_error; 138521a2ad6SLinus Walleij } 1393c333c47SKent Gibson fprintf(stdout, "\tline %2d:", linfo.offset); 140521a2ad6SLinus Walleij if (linfo.name[0]) 141bb91d345SMarkus Pargmann fprintf(stdout, " \"%s\"", linfo.name); 142521a2ad6SLinus Walleij else 143521a2ad6SLinus Walleij fprintf(stdout, " unnamed"); 144214338e3SLinus Walleij if (linfo.consumer[0]) 145214338e3SLinus Walleij fprintf(stdout, " \"%s\"", linfo.consumer); 146521a2ad6SLinus Walleij else 147214338e3SLinus Walleij fprintf(stdout, " unused"); 148521a2ad6SLinus Walleij if (linfo.flags) { 149521a2ad6SLinus Walleij fprintf(stdout, " ["); 1503c333c47SKent Gibson print_attributes(&linfo); 151521a2ad6SLinus Walleij fprintf(stdout, "]"); 152521a2ad6SLinus Walleij } 153521a2ad6SLinus Walleij fprintf(stdout, "\n"); 154521a2ad6SLinus Walleij 1556d591c46SLinus Walleij } 1566d591c46SLinus Walleij 157521a2ad6SLinus Walleij exit_close_error: 158521a2ad6SLinus Walleij if (close(fd) == -1) 159521a2ad6SLinus Walleij perror("Failed to close GPIO character device file"); 160ef3c61a0SKent Gibson exit_free_name: 1616d591c46SLinus Walleij free(chrdev_name); 1626d591c46SLinus Walleij return ret; 1636d591c46SLinus Walleij } 1646d591c46SLinus Walleij 1656d591c46SLinus Walleij void print_usage(void) 1666d591c46SLinus Walleij { 1676d591c46SLinus Walleij fprintf(stderr, "Usage: lsgpio [options]...\n" 1686d591c46SLinus Walleij "List GPIO chips, lines and states\n" 1696d591c46SLinus Walleij " -n <name> List GPIOs on a named device\n" 1706d591c46SLinus Walleij " -? This helptext\n" 1716d591c46SLinus Walleij ); 1726d591c46SLinus Walleij } 1736d591c46SLinus Walleij 1746d591c46SLinus Walleij int main(int argc, char **argv) 1756d591c46SLinus Walleij { 176691998faSGeert Uytterhoeven const char *device_name = NULL; 1776d591c46SLinus Walleij int ret; 1786d591c46SLinus Walleij int c; 1796d591c46SLinus Walleij 1806d591c46SLinus Walleij while ((c = getopt(argc, argv, "n:")) != -1) { 1816d591c46SLinus Walleij switch (c) { 1826d591c46SLinus Walleij case 'n': 1836d591c46SLinus Walleij device_name = optarg; 1846d591c46SLinus Walleij break; 1856d591c46SLinus Walleij case '?': 1866d591c46SLinus Walleij print_usage(); 1876d591c46SLinus Walleij return -1; 1886d591c46SLinus Walleij } 1896d591c46SLinus Walleij } 1906d591c46SLinus Walleij 1916d591c46SLinus Walleij if (device_name) 1926d591c46SLinus Walleij ret = list_device(device_name); 1936d591c46SLinus Walleij else { 1946d591c46SLinus Walleij const struct dirent *ent; 1956d591c46SLinus Walleij DIR *dp; 1966d591c46SLinus Walleij 1976d591c46SLinus Walleij /* List all GPIO devices one at a time */ 1986d591c46SLinus Walleij dp = opendir("/dev"); 1996d591c46SLinus Walleij if (!dp) { 2006d591c46SLinus Walleij ret = -errno; 2016d591c46SLinus Walleij goto error_out; 2026d591c46SLinus Walleij } 2036d591c46SLinus Walleij 2046d591c46SLinus Walleij ret = -ENOENT; 2056d591c46SLinus Walleij while (ent = readdir(dp), ent) { 2066d591c46SLinus Walleij if (check_prefix(ent->d_name, "gpiochip")) { 2076d591c46SLinus Walleij ret = list_device(ent->d_name); 2086d591c46SLinus Walleij if (ret) 2096d591c46SLinus Walleij break; 2106d591c46SLinus Walleij } 2116d591c46SLinus Walleij } 2126d591c46SLinus Walleij 2136d591c46SLinus Walleij ret = 0; 2146d591c46SLinus Walleij if (closedir(dp) == -1) { 2156d591c46SLinus Walleij perror("scanning devices: Failed to close directory"); 2166d591c46SLinus Walleij ret = -errno; 2176d591c46SLinus Walleij } 2186d591c46SLinus Walleij } 2196d591c46SLinus Walleij error_out: 2206d591c46SLinus Walleij return ret; 2216d591c46SLinus Walleij } 222