1 /* 2 * lsgpio - example on how to list the GPIO lines on a system 3 * 4 * Copyright (C) 2015 Linus Walleij 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 as published by 8 * the Free Software Foundation. 9 * 10 * Usage: 11 * lsgpio <-n device-name> 12 */ 13 14 #include <unistd.h> 15 #include <stdlib.h> 16 #include <stdbool.h> 17 #include <stdio.h> 18 #include <dirent.h> 19 #include <errno.h> 20 #include <string.h> 21 #include <poll.h> 22 #include <fcntl.h> 23 #include <getopt.h> 24 #include <sys/ioctl.h> 25 #include <linux/gpio.h> 26 27 #include "gpio-utils.h" 28 29 struct gpio_flag { 30 char *name; 31 unsigned long mask; 32 }; 33 34 struct gpio_flag flagnames[] = { 35 { 36 .name = "kernel", 37 .mask = GPIOLINE_FLAG_KERNEL, 38 }, 39 { 40 .name = "output", 41 .mask = GPIOLINE_FLAG_IS_OUT, 42 }, 43 { 44 .name = "active-low", 45 .mask = GPIOLINE_FLAG_ACTIVE_LOW, 46 }, 47 { 48 .name = "open-drain", 49 .mask = GPIOLINE_FLAG_OPEN_DRAIN, 50 }, 51 { 52 .name = "open-source", 53 .mask = GPIOLINE_FLAG_OPEN_SOURCE, 54 }, 55 }; 56 57 void print_flags(unsigned long flags) 58 { 59 int i; 60 int printed = 0; 61 62 for (i = 0; i < ARRAY_SIZE(flagnames); i++) { 63 if (flags & flagnames[i].mask) { 64 if (printed) 65 fprintf(stdout, " "); 66 fprintf(stdout, "%s", flagnames[i].name); 67 printed++; 68 } 69 } 70 } 71 72 int list_device(const char *device_name) 73 { 74 struct gpiochip_info cinfo; 75 char *chrdev_name; 76 int fd; 77 int ret; 78 int i; 79 80 ret = asprintf(&chrdev_name, "/dev/%s", device_name); 81 if (ret < 0) 82 return -ENOMEM; 83 84 fd = open(chrdev_name, 0); 85 if (fd == -1) { 86 ret = -errno; 87 fprintf(stderr, "Failed to open %s\n", chrdev_name); 88 goto exit_close_error; 89 } 90 91 /* Inspect this GPIO chip */ 92 ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &cinfo); 93 if (ret == -1) { 94 ret = -errno; 95 perror("Failed to issue CHIPINFO IOCTL\n"); 96 goto exit_close_error; 97 } 98 fprintf(stdout, "GPIO chip: %s, \"%s\", %u GPIO lines\n", 99 cinfo.name, cinfo.label, cinfo.lines); 100 101 /* Loop over the lines and print info */ 102 for (i = 0; i < cinfo.lines; i++) { 103 struct gpioline_info linfo; 104 105 memset(&linfo, 0, sizeof(linfo)); 106 linfo.line_offset = i; 107 108 ret = ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &linfo); 109 if (ret == -1) { 110 ret = -errno; 111 perror("Failed to issue LINEINFO IOCTL\n"); 112 goto exit_close_error; 113 } 114 fprintf(stdout, "\tline %2d:", linfo.line_offset); 115 if (linfo.name[0]) 116 fprintf(stdout, " \"%s\"", linfo.name); 117 else 118 fprintf(stdout, " unnamed"); 119 if (linfo.consumer[0]) 120 fprintf(stdout, " \"%s\"", linfo.consumer); 121 else 122 fprintf(stdout, " unused"); 123 if (linfo.flags) { 124 fprintf(stdout, " ["); 125 print_flags(linfo.flags); 126 fprintf(stdout, "]"); 127 } 128 fprintf(stdout, "\n"); 129 130 } 131 132 exit_close_error: 133 if (close(fd) == -1) 134 perror("Failed to close GPIO character device file"); 135 free(chrdev_name); 136 return ret; 137 } 138 139 void print_usage(void) 140 { 141 fprintf(stderr, "Usage: lsgpio [options]...\n" 142 "List GPIO chips, lines and states\n" 143 " -n <name> List GPIOs on a named device\n" 144 " -? This helptext\n" 145 ); 146 } 147 148 int main(int argc, char **argv) 149 { 150 const char *device_name = NULL; 151 int ret; 152 int c; 153 154 while ((c = getopt(argc, argv, "n:")) != -1) { 155 switch (c) { 156 case 'n': 157 device_name = optarg; 158 break; 159 case '?': 160 print_usage(); 161 return -1; 162 } 163 } 164 165 if (device_name) 166 ret = list_device(device_name); 167 else { 168 const struct dirent *ent; 169 DIR *dp; 170 171 /* List all GPIO devices one at a time */ 172 dp = opendir("/dev"); 173 if (!dp) { 174 ret = -errno; 175 goto error_out; 176 } 177 178 ret = -ENOENT; 179 while (ent = readdir(dp), ent) { 180 if (check_prefix(ent->d_name, "gpiochip")) { 181 ret = list_device(ent->d_name); 182 if (ret) 183 break; 184 } 185 } 186 187 ret = 0; 188 if (closedir(dp) == -1) { 189 perror("scanning devices: Failed to close directory"); 190 ret = -errno; 191 } 192 } 193 error_out: 194 return ret; 195 } 196