gpio-mockup-chardev.c 6.7 KB

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