• 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;
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 		asprintf(path, "%s/gpio", mnt_fs_get_target(fs));
63 
64 	mnt_free_iter(itr);
65 	mnt_free_context(cxt);
66 
67 	if (!found)
68 		return -1;
69 
70 	return 0;
71 }
72 
gpio_debugfs_get(const char * consumer,int * dir,int * value)73 static int gpio_debugfs_get(const char *consumer, int *dir, int *value)
74 {
75 	char *debugfs;
76 	FILE *f;
77 	char *line = NULL;
78 	size_t len = 0;
79 	char *cur;
80 	int found = 0;
81 
82 	if (get_debugfs(&debugfs) != 0)
83 		err(EXIT_FAILURE, "debugfs is not mounted");
84 
85 	f = fopen(debugfs, "r");
86 	if (!f)
87 		err(EXIT_FAILURE, "read from gpio debugfs failed");
88 
89 	/*
90 	 * gpio-2   (                    |gpio-selftest               ) in  lo
91 	 */
92 	while (getline(&line, &len, f) != -1) {
93 		cur = strstr(line, consumer);
94 		if (cur == NULL)
95 			continue;
96 
97 		cur = strchr(line, ')');
98 		if (!cur)
99 			continue;
100 
101 		cur += 2;
102 		if (!strncmp(cur, "out", 3)) {
103 			*dir = OUT;
104 			cur += 4;
105 		} else if (!strncmp(cur, "in", 2)) {
106 			*dir = IN;
107 			cur += 4;
108 		}
109 
110 		if (!strncmp(cur, "hi", 2))
111 			*value = 1;
112 		else if (!strncmp(cur, "lo", 2))
113 			*value = 0;
114 
115 		found = 1;
116 		break;
117 	}
118 	free(debugfs);
119 	fclose(f);
120 	free(line);
121 
122 	if (!found)
123 		return -1;
124 
125 	return 0;
126 }
127 
list_gpiochip(const char * gpiochip_name,int * ret)128 static struct gpiochip_info *list_gpiochip(const char *gpiochip_name, int *ret)
129 {
130 	struct gpiochip_info *cinfo;
131 	struct gpiochip_info *current;
132 	const struct dirent *ent;
133 	DIR *dp;
134 	char *chrdev_name;
135 	int fd;
136 	int i = 0;
137 
138 	cinfo = calloc(sizeof(struct gpiochip_info) * 4, GC_NUM + 1);
139 	if (!cinfo)
140 		err(EXIT_FAILURE, "gpiochip_info allocation failed");
141 
142 	current = cinfo;
143 	dp = opendir("/dev");
144 	if (!dp) {
145 		*ret = -errno;
146 		goto error_out;
147 	} else {
148 		*ret = 0;
149 	}
150 
151 	while (ent = readdir(dp), ent) {
152 		if (check_prefix(ent->d_name, "gpiochip")) {
153 			*ret = asprintf(&chrdev_name, "/dev/%s", ent->d_name);
154 			if (*ret < 0)
155 				goto error_out;
156 
157 			fd = open(chrdev_name, 0);
158 			if (fd == -1) {
159 				*ret = -errno;
160 				fprintf(stderr, "Failed to open %s\n",
161 					chrdev_name);
162 				goto error_close_dir;
163 			}
164 			*ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, current);
165 			if (*ret == -1) {
166 				perror("Failed to issue CHIPINFO IOCTL\n");
167 				goto error_close_dir;
168 			}
169 			close(fd);
170 			if (strcmp(current->label, gpiochip_name) == 0
171 			    || check_prefix(current->label, gpiochip_name)) {
172 				*ret = 0;
173 				current++;
174 				i++;
175 			}
176 		}
177 	}
178 
179 	if ((!*ret && i == 0) || *ret < 0) {
180 		free(cinfo);
181 		cinfo = NULL;
182 	}
183 	if (!*ret && i > 0) {
184 		cinfo = realloc(cinfo, sizeof(struct gpiochip_info) * 4 * i);
185 		*ret = i;
186 	}
187 
188 error_close_dir:
189 	closedir(dp);
190 error_out:
191 	if (*ret < 0)
192 		err(EXIT_FAILURE, "list gpiochip failed: %s", strerror(*ret));
193 
194 	return cinfo;
195 }
196 
gpio_pin_test(struct gpiochip_info * cinfo,int line,int flag,int value)197 int gpio_pin_test(struct gpiochip_info *cinfo, int line, int flag, int value)
198 {
199 	struct gpiohandle_data data;
200 	unsigned int lines[] = {line};
201 	int fd;
202 	int debugfs_dir = IN;
203 	int debugfs_value = 0;
204 	int ret;
205 
206 	data.values[0] = value;
207 	ret = gpiotools_request_linehandle(cinfo->name, lines, 1, flag, &data,
208 					   CONSUMER);
209 	if (ret < 0)
210 		goto fail_out;
211 	else
212 		fd = ret;
213 
214 	ret = gpio_debugfs_get(CONSUMER, &debugfs_dir, &debugfs_value);
215 	if (ret) {
216 		ret = -EINVAL;
217 		goto fail_out;
218 	}
219 	if (flag & GPIOHANDLE_REQUEST_INPUT) {
220 		if (debugfs_dir != IN) {
221 			errno = -EINVAL;
222 			ret = -errno;
223 		}
224 	} else if (flag & GPIOHANDLE_REQUEST_OUTPUT) {
225 		if (flag & GPIOHANDLE_REQUEST_ACTIVE_LOW)
226 			debugfs_value = !debugfs_value;
227 
228 		if (!(debugfs_dir == OUT && value == debugfs_value))
229 			errno = -EINVAL;
230 		ret = -errno;
231 
232 	}
233 	gpiotools_release_linehandle(fd);
234 
235 fail_out:
236 	if (ret)
237 		err(EXIT_FAILURE, "gpio<%s> line<%d> test flag<0x%x> value<%d>",
238 		    cinfo->name, line, flag, value);
239 
240 	return ret;
241 }
242 
gpio_pin_tests(struct gpiochip_info * cinfo,unsigned int line)243 void gpio_pin_tests(struct gpiochip_info *cinfo, unsigned int line)
244 {
245 	printf("line<%d>", line);
246 	gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 0);
247 	printf(".");
248 	gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 1);
249 	printf(".");
250 	gpio_pin_test(cinfo, line,
251 		      GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW,
252 		      0);
253 	printf(".");
254 	gpio_pin_test(cinfo, line,
255 		      GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW,
256 		      1);
257 	printf(".");
258 	gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_INPUT, 0);
259 	printf(".");
260 }
261 
262 /*
263  * ./gpio-mockup-chardev gpio_chip_name_prefix is_valid_gpio_chip
264  * Return 0 if successful or exit with EXIT_FAILURE if test failed.
265  * gpio_chip_name_prefix: The prefix of gpiochip you want to test. E.g.
266  *			  gpio-mockup
267  * is_valid_gpio_chip:	  Whether the gpio_chip is valid. 1 means valid,
268  *			  0 means invalid which could not be found by
269  *			  list_gpiochip.
270  */
main(int argc,char * argv[])271 int main(int argc, char *argv[])
272 {
273 	char *prefix;
274 	int valid;
275 	struct gpiochip_info *cinfo;
276 	struct gpiochip_info *current;
277 	int i;
278 	int ret;
279 
280 	if (argc < 3) {
281 		printf("Usage: %s prefix is_valid", argv[0]);
282 		exit(EXIT_FAILURE);
283 	}
284 
285 	prefix = argv[1];
286 	valid = strcmp(argv[2], "true") == 0 ? 1 : 0;
287 
288 	printf("Test gpiochip %s: ", prefix);
289 	cinfo = list_gpiochip(prefix, &ret);
290 	if (!cinfo) {
291 		if (!valid && ret == 0) {
292 			printf("Invalid test successful\n");
293 			ret = 0;
294 			goto out;
295 		} else {
296 			ret = -EINVAL;
297 			goto out;
298 		}
299 	} else if (cinfo && !valid) {
300 		ret = -EINVAL;
301 		goto out;
302 	}
303 	current = cinfo;
304 	for (i = 0; i < ret; i++) {
305 		gpio_pin_tests(current, 0);
306 		gpio_pin_tests(current, current->lines - 1);
307 		gpio_pin_tests(current, random() % current->lines);
308 		current++;
309 	}
310 	ret = 0;
311 	printf("successful\n");
312 
313 out:
314 	if (ret)
315 		fprintf(stderr, "gpio<%s> test failed\n", prefix);
316 
317 	if (cinfo)
318 		free(cinfo);
319 
320 	if (ret)
321 		exit(EXIT_FAILURE);
322 
323 	return ret;
324 }
325