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