Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / tools / testing / selftests / gpio / gpio-mockup-chardev.c
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
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
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
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
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
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  */
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 }