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