• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * GPIO chardev test helper
4  *
5  * Copyright (C) 2016 Bamvor Jian Zhang
6  */
7 
8 #define _GNU_SOURCE
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <fcntl.h>
15 #include <getopt.h>
16 #include <sys/ioctl.h>
17 #include <libmount.h>
18 #include <err.h>
19 #include <dirent.h>
20 #include <linux/gpio.h>
21 #include "../../../gpio/gpio-utils.h"
22 
23 #define CONSUMER	"gpio-selftest"
24 #define	GC_NUM		10
25 enum direction {
26 	OUT,
27 	IN
28 };
29 
get_debugfs(char ** path)30 static int get_debugfs(char **path)
31 {
32 	struct libmnt_context *cxt;
33 	struct libmnt_table *tb;
34 	struct libmnt_iter *itr = NULL;
35 	struct libmnt_fs *fs;
36 	int found = 0, ret;
37 
38 	cxt = mnt_new_context();
39 	if (!cxt)
40 		err(EXIT_FAILURE, "libmount context allocation failed");
41 
42 	itr = mnt_new_iter(MNT_ITER_FORWARD);
43 	if (!itr)
44 		err(EXIT_FAILURE, "failed to initialize libmount iterator");
45 
46 	if (mnt_context_get_mtab(cxt, &tb))
47 		err(EXIT_FAILURE, "failed to read mtab");
48 
49 	while (mnt_table_next_fs(tb, itr, &fs) == 0) {
50 		const char *type = mnt_fs_get_fstype(fs);
51 
52 		if (!strcmp(type, "debugfs")) {
53 			found = 1;
54 			break;
55 		}
56 	}
57 	if (found) {
58 		ret = asprintf(path, "%s/gpio", mnt_fs_get_target(fs));
59 		if (ret < 0)
60 			err(EXIT_FAILURE, "failed to format string");
61 	}
62 
63 	mnt_free_iter(itr);
64 	mnt_free_context(cxt);
65 
66 	if (!found)
67 		return -1;
68 
69 	return 0;
70 }
71 
gpio_debugfs_get(const char * consumer,int * dir,int * value)72 static int gpio_debugfs_get(const char *consumer, int *dir, int *value)
73 {
74 	char *debugfs;
75 	FILE *f;
76 	char *line = NULL;
77 	size_t len = 0;
78 	char *cur;
79 	int found = 0;
80 
81 	if (get_debugfs(&debugfs) != 0)
82 		err(EXIT_FAILURE, "debugfs is not mounted");
83 
84 	f = fopen(debugfs, "r");
85 	if (!f)
86 		err(EXIT_FAILURE, "read from gpio debugfs failed");
87 
88 	/*
89 	 * gpio-2   (                    |gpio-selftest               ) in  lo
90 	 */
91 	while (getline(&line, &len, f) != -1) {
92 		cur = strstr(line, consumer);
93 		if (cur == NULL)
94 			continue;
95 
96 		cur = strchr(line, ')');
97 		if (!cur)
98 			continue;
99 
100 		cur += 2;
101 		if (!strncmp(cur, "out", 3)) {
102 			*dir = OUT;
103 			cur += 4;
104 		} else if (!strncmp(cur, "in", 2)) {
105 			*dir = IN;
106 			cur += 4;
107 		}
108 
109 		if (!strncmp(cur, "hi", 2))
110 			*value = 1;
111 		else if (!strncmp(cur, "lo", 2))
112 			*value = 0;
113 
114 		found = 1;
115 		break;
116 	}
117 	free(debugfs);
118 	fclose(f);
119 	free(line);
120 
121 	if (!found)
122 		return -1;
123 
124 	return 0;
125 }
126 
list_gpiochip(const char * gpiochip_name,int * ret)127 static struct gpiochip_info *list_gpiochip(const char *gpiochip_name, int *ret)
128 {
129 	struct gpiochip_info *cinfo;
130 	struct gpiochip_info *current;
131 	const struct dirent *ent;
132 	DIR *dp;
133 	char *chrdev_name;
134 	int fd;
135 	int i = 0;
136 
137 	cinfo = calloc(sizeof(struct gpiochip_info) * 4, GC_NUM + 1);
138 	if (!cinfo)
139 		err(EXIT_FAILURE, "gpiochip_info allocation failed");
140 
141 	current = cinfo;
142 	dp = opendir("/dev");
143 	if (!dp) {
144 		*ret = -errno;
145 		goto error_out;
146 	} else {
147 		*ret = 0;
148 	}
149 
150 	while (ent = readdir(dp), ent) {
151 		if (check_prefix(ent->d_name, "gpiochip")) {
152 			*ret = asprintf(&chrdev_name, "/dev/%s", ent->d_name);
153 			if (*ret < 0)
154 				goto error_out;
155 
156 			fd = open(chrdev_name, 0);
157 			if (fd == -1) {
158 				*ret = -errno;
159 				fprintf(stderr, "Failed to open %s\n",
160 					chrdev_name);
161 				goto error_close_dir;
162 			}
163 			*ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, current);
164 			if (*ret == -1) {
165 				perror("Failed to issue CHIPINFO IOCTL\n");
166 				goto error_close_dir;
167 			}
168 			close(fd);
169 			if (strcmp(current->label, gpiochip_name) == 0
170 			    || check_prefix(current->label, gpiochip_name)) {
171 				*ret = 0;
172 				current++;
173 				i++;
174 			}
175 		}
176 	}
177 
178 	if ((!*ret && i == 0) || *ret < 0) {
179 		free(cinfo);
180 		cinfo = NULL;
181 	}
182 	if (!*ret && i > 0) {
183 		cinfo = realloc(cinfo, sizeof(struct gpiochip_info) * 4 * i);
184 		*ret = i;
185 	}
186 
187 error_close_dir:
188 	closedir(dp);
189 error_out:
190 	if (*ret < 0)
191 		err(EXIT_FAILURE, "list gpiochip failed: %s", strerror(*ret));
192 
193 	return cinfo;
194 }
195 
gpio_pin_test(struct gpiochip_info * cinfo,int line,int flag,int value)196 int gpio_pin_test(struct gpiochip_info *cinfo, int line, int flag, int value)
197 {
198 	struct gpiohandle_data data;
199 	unsigned int lines[] = {line};
200 	int fd;
201 	int debugfs_dir = IN;
202 	int debugfs_value = 0;
203 	int ret;
204 
205 	data.values[0] = value;
206 	ret = gpiotools_request_linehandle(cinfo->name, lines, 1, flag, &data,
207 					   CONSUMER);
208 	if (ret < 0)
209 		goto fail_out;
210 	else
211 		fd = ret;
212 
213 	ret = gpio_debugfs_get(CONSUMER, &debugfs_dir, &debugfs_value);
214 	if (ret) {
215 		ret = -EINVAL;
216 		goto fail_out;
217 	}
218 	if (flag & GPIOHANDLE_REQUEST_INPUT) {
219 		if (debugfs_dir != IN) {
220 			errno = -EINVAL;
221 			ret = -errno;
222 		}
223 	} else if (flag & GPIOHANDLE_REQUEST_OUTPUT) {
224 		if (flag & GPIOHANDLE_REQUEST_ACTIVE_LOW)
225 			debugfs_value = !debugfs_value;
226 
227 		if (!(debugfs_dir == OUT && value == debugfs_value)) {
228 			errno = -EINVAL;
229 			ret = -errno;
230 		}
231 	}
232 	gpiotools_release_linehandle(fd);
233 
234 fail_out:
235 	if (ret)
236 		err(EXIT_FAILURE, "gpio<%s> line<%d> test flag<0x%x> value<%d>",
237 		    cinfo->name, line, flag, value);
238 
239 	return ret;
240 }
241 
gpio_pin_tests(struct gpiochip_info * cinfo,unsigned int line)242 void gpio_pin_tests(struct gpiochip_info *cinfo, unsigned int line)
243 {
244 	printf("line<%d>", line);
245 	gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 0);
246 	printf(".");
247 	gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 1);
248 	printf(".");
249 	gpio_pin_test(cinfo, line,
250 		      GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW,
251 		      0);
252 	printf(".");
253 	gpio_pin_test(cinfo, line,
254 		      GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW,
255 		      1);
256 	printf(".");
257 	gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_INPUT, 0);
258 	printf(".");
259 }
260 
261 /*
262  * ./gpio-mockup-chardev gpio_chip_name_prefix is_valid_gpio_chip
263  * Return 0 if successful or exit with EXIT_FAILURE if test failed.
264  * gpio_chip_name_prefix: The prefix of gpiochip you want to test. E.g.
265  *			  gpio-mockup
266  * is_valid_gpio_chip:	  Whether the gpio_chip is valid. 1 means valid,
267  *			  0 means invalid which could not be found by
268  *			  list_gpiochip.
269  */
main(int argc,char * argv[])270 int main(int argc, char *argv[])
271 {
272 	char *prefix;
273 	int valid;
274 	struct gpiochip_info *cinfo;
275 	struct gpiochip_info *current;
276 	int i;
277 	int ret;
278 
279 	if (argc < 3) {
280 		printf("Usage: %s prefix is_valid", argv[0]);
281 		exit(EXIT_FAILURE);
282 	}
283 
284 	prefix = argv[1];
285 	valid = strcmp(argv[2], "true") == 0 ? 1 : 0;
286 
287 	printf("Test gpiochip %s: ", prefix);
288 	cinfo = list_gpiochip(prefix, &ret);
289 	if (!cinfo) {
290 		if (!valid && ret == 0) {
291 			printf("Invalid test successful\n");
292 			ret = 0;
293 			goto out;
294 		} else {
295 			ret = -EINVAL;
296 			goto out;
297 		}
298 	} else if (cinfo && !valid) {
299 		ret = -EINVAL;
300 		goto out;
301 	}
302 	current = cinfo;
303 	for (i = 0; i < ret; i++) {
304 		gpio_pin_tests(current, 0);
305 		gpio_pin_tests(current, current->lines - 1);
306 		gpio_pin_tests(current, random() % current->lines);
307 		current++;
308 	}
309 	ret = 0;
310 	printf("successful\n");
311 
312 out:
313 	if (ret)
314 		fprintf(stderr, "gpio<%s> test failed\n", prefix);
315 
316 	if (cinfo)
317 		free(cinfo);
318 
319 	if (ret)
320 		exit(EXIT_FAILURE);
321 
322 	return ret;
323 }
324