226 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * lsgpio - example on how to list the GPIO lines on a system4 *5 * Copyright (C) 2015 Linus Walleij6 *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 26struct gpio_flag {27 char *name;28 unsigned long long mask;29};30 31struct 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 74static 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 101int 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 else147 fprintf(stdout, " unnamed");148 if (linfo.consumer[0])149 fprintf(stdout, " \"%s\"", linfo.consumer);150 else151 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 161exit_close_error:162 if (close(fd) == -1)163 perror("Failed to close GPIO character device file");164exit_free_name:165 free(chrdev_name);166 return ret;167}168 169void 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 178int 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 }223error_out:224 return ret;225}226