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 .name = "clock-realtime", 70 .mask = GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME, 71 }, 72 }; 73 74 static void print_attributes(struct gpio_v2_line_info *info) 75 { 76 int i; 77 const char *field_format = "%s"; 78 79 for (i = 0; i < ARRAY_SIZE(flagnames); i++) { 80 if (info->flags & flagnames[i].mask) { 81 fprintf(stdout, field_format, flagnames[i].name); 82 field_format = ", %s"; 83 } 84 } 85 86 if ((info->flags & GPIO_V2_LINE_FLAG_EDGE_RISING) && 87 (info->flags & GPIO_V2_LINE_FLAG_EDGE_FALLING)) 88 fprintf(stdout, field_format, "both-edges"); 89 else if (info->flags & GPIO_V2_LINE_FLAG_EDGE_RISING) 90 fprintf(stdout, field_format, "rising-edge"); 91 else if (info->flags & GPIO_V2_LINE_FLAG_EDGE_FALLING) 92 fprintf(stdout, field_format, "falling-edge"); 93 94 for (i = 0; i < info->num_attrs; i++) { 95 if (info->attrs[i].id == GPIO_V2_LINE_ATTR_ID_DEBOUNCE) 96 fprintf(stdout, ", debounce_period=%dusec", 97 info->attrs[i].debounce_period_us); 98 } 99 } 100 101 int list_device(const char *device_name) 102 { 103 struct gpiochip_info cinfo; 104 char *chrdev_name; 105 int fd; 106 int ret; 107 int i; 108 109 ret = asprintf(&chrdev_name, "/dev/%s", device_name); 110 if (ret < 0) 111 return -ENOMEM; 112 113 fd = open(chrdev_name, 0); 114 if (fd == -1) { 115 ret = -errno; 116 fprintf(stderr, "Failed to open %s\n", chrdev_name); 117 goto exit_free_name; 118 } 119 120 /* Inspect this GPIO chip */ 121 ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &cinfo); 122 if (ret == -1) { 123 ret = -errno; 124 perror("Failed to issue CHIPINFO IOCTL\n"); 125 goto exit_close_error; 126 } 127 fprintf(stdout, "GPIO chip: %s, \"%s\", %u GPIO lines\n", 128 cinfo.name, cinfo.label, cinfo.lines); 129 130 /* Loop over the lines and print info */ 131 for (i = 0; i < cinfo.lines; i++) { 132 struct gpio_v2_line_info linfo; 133 134 memset(&linfo, 0, sizeof(linfo)); 135 linfo.offset = i; 136 137 ret = ioctl(fd, GPIO_V2_GET_LINEINFO_IOCTL, &linfo); 138 if (ret == -1) { 139 ret = -errno; 140 perror("Failed to issue LINEINFO IOCTL\n"); 141 goto exit_close_error; 142 } 143 fprintf(stdout, "\tline %2d:", linfo.offset); 144 if (linfo.name[0]) 145 fprintf(stdout, " \"%s\"", linfo.name); 146 else 147 fprintf(stdout, " unnamed"); 148 if (linfo.consumer[0]) 149 fprintf(stdout, " \"%s\"", linfo.consumer); 150 else 151 fprintf(stdout, " unused"); 152 if (linfo.flags) { 153 fprintf(stdout, " ["); 154 print_attributes(&linfo); 155 fprintf(stdout, "]"); 156 } 157 fprintf(stdout, "\n"); 158 159 } 160 161 exit_close_error: 162 if (close(fd) == -1) 163 perror("Failed to close GPIO character device file"); 164 exit_free_name: 165 free(chrdev_name); 166 return ret; 167 } 168 169 void print_usage(void) 170 { 171 fprintf(stderr, "Usage: lsgpio [options]...\n" 172 "List GPIO chips, lines and states\n" 173 " -n <name> List GPIOs on a named device\n" 174 " -? This helptext\n" 175 ); 176 } 177 178 int main(int argc, char **argv) 179 { 180 const char *device_name = NULL; 181 int ret; 182 int c; 183 184 while ((c = getopt(argc, argv, "n:")) != -1) { 185 switch (c) { 186 case 'n': 187 device_name = optarg; 188 break; 189 case '?': 190 print_usage(); 191 return -1; 192 } 193 } 194 195 if (device_name) 196 ret = list_device(device_name); 197 else { 198 const struct dirent *ent; 199 DIR *dp; 200 201 /* List all GPIO devices one at a time */ 202 dp = opendir("/dev"); 203 if (!dp) { 204 ret = -errno; 205 goto error_out; 206 } 207 208 ret = -ENOENT; 209 while (ent = readdir(dp), ent) { 210 if (check_prefix(ent->d_name, "gpiochip")) { 211 ret = list_device(ent->d_name); 212 if (ret) 213 break; 214 } 215 } 216 217 ret = 0; 218 if (closedir(dp) == -1) { 219 perror("scanning devices: Failed to close directory"); 220 ret = -errno; 221 } 222 } 223 error_out: 224 return ret; 225 } 226