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