1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * lsgpio - example on how to list the GPIO lines on a system 4 * 5 * Copyright (C) 2015 Linus Walleij 6 * 7 * Usage: 8 * lsgpio <-n device-name> 9 */ 10 11 #include <unistd.h> 12 #include <stdlib.h> 13 #include <stdbool.h> 14 #include <stdio.h> 15 #include <dirent.h> 16 #include <errno.h> 17 #include <string.h> 18 #include <poll.h> 19 #include <fcntl.h> 20 #include <getopt.h> 21 #include <sys/ioctl.h> 22 #include <linux/gpio.h> 23 24 #include "gpio-utils.h" 25 26 struct gpio_flag { 27 char *name; 28 unsigned long long mask; 29 }; 30 31 struct gpio_flag flagnames[] = { 32 { 33 .name = "used", 34 .mask = GPIO_V2_LINE_FLAG_USED, 35 }, 36 { 37 .name = "input", 38 .mask = GPIO_V2_LINE_FLAG_INPUT, 39 }, 40 { 41 .name = "output", 42 .mask = GPIO_V2_LINE_FLAG_OUTPUT, 43 }, 44 { 45 .name = "active-low", 46 .mask = GPIO_V2_LINE_FLAG_ACTIVE_LOW, 47 }, 48 { 49 .name = "open-drain", 50 .mask = GPIO_V2_LINE_FLAG_OPEN_DRAIN, 51 }, 52 { 53 .name = "open-source", 54 .mask = GPIO_V2_LINE_FLAG_OPEN_SOURCE, 55 }, 56 { 57 .name = "pull-up", 58 .mask = GPIO_V2_LINE_FLAG_BIAS_PULL_UP, 59 }, 60 { 61 .name = "pull-down", 62 .mask = GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN, 63 }, 64 { 65 .name = "bias-disabled", 66 .mask = GPIO_V2_LINE_FLAG_BIAS_DISABLED, 67 }, 68 }; 69 70 static void print_attributes(struct gpio_v2_line_info *info) 71 { 72 int i; 73 const char *field_format = "%s"; 74 75 for (i = 0; i < ARRAY_SIZE(flagnames); i++) { 76 if (info->flags & flagnames[i].mask) { 77 fprintf(stdout, field_format, flagnames[i].name); 78 field_format = ", %s"; 79 } 80 } 81 82 if ((info->flags & GPIO_V2_LINE_FLAG_EDGE_RISING) && 83 (info->flags & GPIO_V2_LINE_FLAG_EDGE_FALLING)) 84 fprintf(stdout, field_format, "both-edges"); 85 else if (info->flags & GPIO_V2_LINE_FLAG_EDGE_RISING) 86 fprintf(stdout, field_format, "rising-edge"); 87 else if (info->flags & GPIO_V2_LINE_FLAG_EDGE_FALLING) 88 fprintf(stdout, field_format, "falling-edge"); 89 90 for (i = 0; i < info->num_attrs; i++) { 91 if (info->attrs[i].id == GPIO_V2_LINE_ATTR_ID_DEBOUNCE) 92 fprintf(stdout, ", debounce_period=%dusec", 93 info->attrs[0].debounce_period_us); 94 } 95 } 96 97 int list_device(const char *device_name) 98 { 99 struct gpiochip_info cinfo; 100 char *chrdev_name; 101 int fd; 102 int ret; 103 int i; 104 105 ret = asprintf(&chrdev_name, "/dev/%s", device_name); 106 if (ret < 0) 107 return -ENOMEM; 108 109 fd = open(chrdev_name, 0); 110 if (fd == -1) { 111 ret = -errno; 112 fprintf(stderr, "Failed to open %s\n", chrdev_name); 113 goto exit_free_name; 114 } 115 116 /* Inspect this GPIO chip */ 117 ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &cinfo); 118 if (ret == -1) { 119 ret = -errno; 120 perror("Failed to issue CHIPINFO IOCTL\n"); 121 goto exit_close_error; 122 } 123 fprintf(stdout, "GPIO chip: %s, \"%s\", %u GPIO lines\n", 124 cinfo.name, cinfo.label, cinfo.lines); 125 126 /* Loop over the lines and print info */ 127 for (i = 0; i < cinfo.lines; i++) { 128 struct gpio_v2_line_info linfo; 129 130 memset(&linfo, 0, sizeof(linfo)); 131 linfo.offset = i; 132 133 ret = ioctl(fd, GPIO_V2_GET_LINEINFO_IOCTL, &linfo); 134 if (ret == -1) { 135 ret = -errno; 136 perror("Failed to issue LINEINFO IOCTL\n"); 137 goto exit_close_error; 138 } 139 fprintf(stdout, "\tline %2d:", linfo.offset); 140 if (linfo.name[0]) 141 fprintf(stdout, " \"%s\"", linfo.name); 142 else 143 fprintf(stdout, " unnamed"); 144 if (linfo.consumer[0]) 145 fprintf(stdout, " \"%s\"", linfo.consumer); 146 else 147 fprintf(stdout, " unused"); 148 if (linfo.flags) { 149 fprintf(stdout, " ["); 150 print_attributes(&linfo); 151 fprintf(stdout, "]"); 152 } 153 fprintf(stdout, "\n"); 154 155 } 156 157 exit_close_error: 158 if (close(fd) == -1) 159 perror("Failed to close GPIO character device file"); 160 exit_free_name: 161 free(chrdev_name); 162 return ret; 163 } 164 165 void print_usage(void) 166 { 167 fprintf(stderr, "Usage: lsgpio [options]...\n" 168 "List GPIO chips, lines and states\n" 169 " -n <name> List GPIOs on a named device\n" 170 " -? This helptext\n" 171 ); 172 } 173 174 int main(int argc, char **argv) 175 { 176 const char *device_name = NULL; 177 int ret; 178 int c; 179 180 while ((c = getopt(argc, argv, "n:")) != -1) { 181 switch (c) { 182 case 'n': 183 device_name = optarg; 184 break; 185 case '?': 186 print_usage(); 187 return -1; 188 } 189 } 190 191 if (device_name) 192 ret = list_device(device_name); 193 else { 194 const struct dirent *ent; 195 DIR *dp; 196 197 /* List all GPIO devices one at a time */ 198 dp = opendir("/dev"); 199 if (!dp) { 200 ret = -errno; 201 goto error_out; 202 } 203 204 ret = -ENOENT; 205 while (ent = readdir(dp), ent) { 206 if (check_prefix(ent->d_name, "gpiochip")) { 207 ret = list_device(ent->d_name); 208 if (ret) 209 break; 210 } 211 } 212 213 ret = 0; 214 if (closedir(dp) == -1) { 215 perror("scanning devices: Failed to close directory"); 216 ret = -errno; 217 } 218 } 219 error_out: 220 return ret; 221 } 222