1
2
3
4
5
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
25enum direction {
26 OUT,
27 IN
28};
29
30static 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
72static 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
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
127static 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
187error_close_dir:
188 closedir(dp);
189error_out:
190 if (*ret < 0)
191 err(EXIT_FAILURE, "list gpiochip failed: %s", strerror(*ret));
192
193 return cinfo;
194}
195
196int 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
234fail_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
242void 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
263
264
265
266
267
268
269
270int 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
312out:
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