test_gpio_sysfs.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*
  2. * c-periphery
  3. * https://github.com/vsergeev/c-periphery
  4. * License: MIT
  5. */
  6. #include "test.h"
  7. #include <stdlib.h>
  8. #include <errno.h>
  9. #include <sys/wait.h>
  10. #include <pthread.h>
  11. #include <semaphore.h>
  12. #include <unistd.h>
  13. #include "../src/gpio.h"
  14. unsigned int pin_input, pin_output;
  15. void test_arguments(void) {
  16. gpio_t *gpio;
  17. ptest();
  18. /* Allocate GPIO */
  19. gpio = gpio_new();
  20. passert(gpio != NULL);
  21. /* Invalid direction */
  22. passert(gpio_open_sysfs(gpio, pin_input, 5) == GPIO_ERROR_ARG);
  23. /* Free GPIO */
  24. gpio_free(gpio);
  25. }
  26. void test_open_config_close(void) {
  27. gpio_t *gpio;
  28. bool value;
  29. gpio_direction_t direction;
  30. gpio_edge_t edge;
  31. gpio_bias_t bias;
  32. gpio_drive_t drive;
  33. bool inverted;
  34. ptest();
  35. /* Allocate GPIO */
  36. gpio = gpio_new();
  37. passert(gpio != NULL);
  38. /* Open non-existent GPIO -- export should fail with EINVAL */
  39. passert(gpio_open_sysfs(gpio, 9999, GPIO_DIR_IN) == GPIO_ERROR_OPEN);
  40. passert(gpio_errno(gpio) == EINVAL);
  41. /* Open legitimate GPIO */
  42. passert(gpio_open_sysfs(gpio, pin_output, GPIO_DIR_IN) == 0);
  43. /* Check properties */
  44. passert(gpio_line(gpio) == pin_output);
  45. passert(gpio_fd(gpio) >= 0);
  46. /* Invalid direction */
  47. passert(gpio_set_direction(gpio, 5) == GPIO_ERROR_ARG);
  48. /* Invalid interrupt edge */
  49. passert(gpio_set_edge(gpio, 5) == GPIO_ERROR_ARG);
  50. /* Unsupported setting bias */
  51. passert(gpio_set_bias(gpio, GPIO_BIAS_PULL_UP) == GPIO_ERROR_UNSUPPORTED);
  52. passert(gpio_get_bias(gpio, &bias) == GPIO_ERROR_UNSUPPORTED);
  53. /* Unsupported setting drive */
  54. passert(gpio_set_drive(gpio, GPIO_DRIVE_OPEN_DRAIN) == GPIO_ERROR_UNSUPPORTED);
  55. passert(gpio_get_drive(gpio, &drive) == GPIO_ERROR_UNSUPPORTED);
  56. /* Unsupported property */
  57. passert(gpio_chip_fd(gpio) == GPIO_ERROR_UNSUPPORTED);
  58. /* Unsupported method */
  59. passert(gpio_read_event(gpio, &edge, NULL) == GPIO_ERROR_UNSUPPORTED);
  60. /* Set direction out, check direction out, check value low */
  61. passert(gpio_set_direction(gpio, GPIO_DIR_OUT) == 0);
  62. passert(gpio_get_direction(gpio, &direction) == 0);
  63. passert(direction == GPIO_DIR_OUT);
  64. passert(gpio_read(gpio, &value) == 0);
  65. passert(value == false);
  66. /* Set direction out low, check direction out, check value low */
  67. passert(gpio_set_direction(gpio, GPIO_DIR_OUT_LOW) == 0);
  68. passert(gpio_get_direction(gpio, &direction) == 0);
  69. passert(direction == GPIO_DIR_OUT);
  70. passert(gpio_read(gpio, &value) == 0);
  71. passert(value == false);
  72. /* Set direction out high, check direction out, check value high */
  73. passert(gpio_set_direction(gpio, GPIO_DIR_OUT_HIGH) == 0);
  74. passert(gpio_get_direction(gpio, &direction) == 0);
  75. passert(direction == GPIO_DIR_OUT);
  76. passert(gpio_read(gpio, &value) == 0);
  77. passert(value == true);
  78. /* Set inverted true, check inverted */
  79. passert(gpio_set_inverted(gpio, true) == 0);
  80. passert(gpio_get_inverted(gpio, &inverted) == 0);
  81. passert(inverted == true);
  82. /* Set inverted false, check inverted */
  83. passert(gpio_set_inverted(gpio, false) == 0);
  84. passert(gpio_get_inverted(gpio, &inverted) == 0);
  85. passert(inverted == false);
  86. /* Set direction in, check direction in */
  87. passert(gpio_set_direction(gpio, GPIO_DIR_IN) == 0);
  88. passert(gpio_get_direction(gpio, &direction) == 0);
  89. passert(direction == GPIO_DIR_IN);
  90. passert(gpio_read(gpio, &value) == 0);
  91. /* Set edge none, check edge none */
  92. passert(gpio_set_edge(gpio, GPIO_EDGE_NONE) == 0);
  93. passert(gpio_get_edge(gpio, &edge) == 0);
  94. passert(edge == GPIO_EDGE_NONE);
  95. /* Set edge rising, check edge rising */
  96. passert(gpio_set_edge(gpio, GPIO_EDGE_RISING) == 0);
  97. passert(gpio_get_edge(gpio, &edge) == 0);
  98. passert(edge == GPIO_EDGE_RISING);
  99. /* Set edge falling, check edge falling */
  100. passert(gpio_set_edge(gpio, GPIO_EDGE_FALLING) == 0);
  101. passert(gpio_get_edge(gpio, &edge) == 0);
  102. passert(edge == GPIO_EDGE_FALLING);
  103. /* Set edge both, check edge both */
  104. passert(gpio_set_edge(gpio, GPIO_EDGE_BOTH) == 0);
  105. passert(gpio_get_edge(gpio, &edge) == 0);
  106. passert(edge == GPIO_EDGE_BOTH);
  107. /* Set edge none, check edge none */
  108. passert(gpio_set_edge(gpio, GPIO_EDGE_NONE) == 0);
  109. passert(gpio_get_edge(gpio, &edge) == 0);
  110. passert(edge == GPIO_EDGE_NONE);
  111. /* Close GPIO */
  112. passert(gpio_close(gpio) == 0);
  113. /* Free GPIO */
  114. gpio_free(gpio);
  115. }
  116. /* Threaded poll helper functions */
  117. typedef struct {
  118. sem_t *sem;
  119. gpio_t *gpio;
  120. int timeout_ms;
  121. } gpio_poll_args_t;
  122. void *gpio_poll_thread(void *arg) {
  123. gpio_poll_args_t *args = (gpio_poll_args_t *)arg;
  124. gpio_t *gpio = args->gpio;
  125. int timeout_ms = args->timeout_ms;
  126. assert(sem_post(args->sem) == 0);
  127. intptr_t ret = gpio_poll(gpio, timeout_ms);
  128. return (void *)ret;
  129. }
  130. void gpio_poll_start(pthread_t *thread, gpio_t *gpio, int timeout_ms) {
  131. sem_t sem;
  132. gpio_poll_args_t args = {.sem = &sem, .gpio = gpio, .timeout_ms = timeout_ms};
  133. assert(sem_init(&sem, 0, 0) == 0);
  134. assert(pthread_create(thread, NULL, &gpio_poll_thread, &args) == 0);
  135. assert(sem_wait(&sem) == 0);
  136. }
  137. int gpio_poll_join(pthread_t thread) {
  138. void *ret;
  139. assert(pthread_join(thread, &ret) == 0);
  140. return (intptr_t)ret;
  141. }
  142. void test_loopback(void) {
  143. gpio_t *gpio_in, *gpio_out;
  144. pthread_t poll_thread;
  145. bool value;
  146. ptest();
  147. /* Allocate GPIO */
  148. gpio_in = gpio_new();
  149. passert(gpio_in != NULL);
  150. gpio_out = gpio_new();
  151. passert(gpio_out != NULL);
  152. /* Open in and out pins */
  153. passert(gpio_open_sysfs(gpio_in, pin_input, GPIO_DIR_IN) == 0);
  154. passert(gpio_open_sysfs(gpio_out, pin_output, GPIO_DIR_OUT) == 0);
  155. /* Drive out low, check in low */
  156. passert(gpio_write(gpio_out, false) == 0);
  157. passert(gpio_read(gpio_in, &value) == 0);
  158. passert(value == false);
  159. /* Drive out high, check in high */
  160. passert(gpio_write(gpio_out, true) == 0);
  161. passert(gpio_read(gpio_in, &value) == 0);
  162. passert(value == true);
  163. /* Check poll falling 1 -> 0 interrupt */
  164. passert(gpio_set_edge(gpio_in, GPIO_EDGE_FALLING) == 0);
  165. gpio_poll_start(&poll_thread, gpio_in, 1000);
  166. passert(gpio_write(gpio_out, false) == 0);
  167. passert(gpio_poll_join(poll_thread) == 1);
  168. passert(gpio_read(gpio_in, &value) == 0);
  169. passert(value == false);
  170. /* Check poll rising 0 -> 1 interrupt */
  171. passert(gpio_set_edge(gpio_in, GPIO_EDGE_RISING) == 0);
  172. gpio_poll_start(&poll_thread, gpio_in, 1000);
  173. passert(gpio_write(gpio_out, true) == 0);
  174. passert(gpio_poll_join(poll_thread) == 1);
  175. passert(gpio_read(gpio_in, &value) == 0);
  176. passert(value == true);
  177. /* Check poll timeout */
  178. passert(gpio_poll(gpio_in, 1000) == 0);
  179. /* Test gpio_poll_multiple() API with one GPIO */
  180. gpio_t *gpios[1] = {gpio_in};
  181. bool gpios_ready[1] = {false};
  182. passert(gpio_set_edge(gpio_in, GPIO_EDGE_BOTH) == 0);
  183. /* Check poll falling 1 -> 0 interrupt */
  184. passert(gpio_write(gpio_out, false) == 0);
  185. passert(gpio_poll_multiple(gpios, 1, 1000, gpios_ready) == 1);
  186. passert(gpios_ready[0] == true);
  187. passert(gpio_read(gpio_in, &value) == 0);
  188. passert(value == false);
  189. /* Check poll rising 0 -> 1 interrupt */
  190. passert(gpio_write(gpio_out, true) == 0);
  191. passert(gpio_poll_multiple(gpios, 1, 1000, gpios_ready) == 1);
  192. passert(gpios_ready[0] == true);
  193. passert(gpio_read(gpio_in, &value) == 0);
  194. passert(value == true);
  195. /* Check poll timeout */
  196. passert(gpio_poll_multiple(gpios, 1, 1000, gpios_ready) == 0);
  197. passert(gpios_ready[0] == false);
  198. passert(gpio_close(gpio_in) == 0);
  199. passert(gpio_close(gpio_out) == 0);
  200. /* Free GPIO */
  201. gpio_free(gpio_in);
  202. gpio_free(gpio_out);
  203. }
  204. bool getc_yes(void) {
  205. char buf[4];
  206. fgets(buf, sizeof(buf), stdin);
  207. return (buf[0] == 'y' || buf[0] == 'Y');
  208. }
  209. void test_interactive(void) {
  210. char str[256];
  211. gpio_t *gpio;
  212. ptest();
  213. /* Allocate GPIO */
  214. gpio = gpio_new();
  215. passert(gpio != NULL);
  216. passert(gpio_open_sysfs(gpio, pin_output, GPIO_DIR_OUT) == 0);
  217. printf("Starting interactive test. Get out your multimeter, buddy!\n");
  218. printf("Press enter to continue...\n");
  219. getc(stdin);
  220. /* Check tostring */
  221. passert(gpio_tostring(gpio, str, sizeof(str)) > 0);
  222. printf("GPIO description: %s\n", str);
  223. printf("GPIO description looks OK? y/n\n");
  224. passert(getc_yes());
  225. /* Drive GPIO out low */
  226. passert(gpio_write(gpio, false) == 0);
  227. printf("GPIO out is low? y/n\n");
  228. passert(getc_yes());
  229. /* Drive GPIO out high */
  230. passert(gpio_write(gpio, true) == 0);
  231. printf("GPIO out is high? y/n\n");
  232. passert(getc_yes());
  233. /* Drive GPIO out low */
  234. passert(gpio_write(gpio, false) == 0);
  235. printf("GPIO out is low? y/n\n");
  236. passert(getc_yes());
  237. passert(gpio_close(gpio) == 0);
  238. /* Free GPIO */
  239. gpio_free(gpio);
  240. }
  241. int main(int argc, char *argv[]) {
  242. if (argc < 3) {
  243. fprintf(stderr, "Usage: %s <GPIO #1> <GPIO #2>\n\n", argv[0]);
  244. fprintf(stderr, "[1/4] Argument test: No requirements.\n");
  245. fprintf(stderr, "[2/4] Open/close test: GPIO #2 should be real.\n");
  246. fprintf(stderr, "[3/4] Loopback test: GPIOs #1 and #2 should be connected with a wire.\n");
  247. fprintf(stderr, "[4/4] Interactive test: GPIO #2 should be observed with a multimeter.\n\n");
  248. fprintf(stderr, "Hint: for Raspberry Pi 3,\n");
  249. fprintf(stderr, "Use GPIO 17 (header pin 11) and GPIO 27 (header pin 13),\n");
  250. fprintf(stderr, "connect a loopback between them, and run this test with:\n");
  251. fprintf(stderr, " %s 17 27\n\n", argv[0]);
  252. exit(1);
  253. }
  254. pin_input = strtoul(argv[1], NULL, 10);
  255. pin_output = strtoul(argv[2], NULL, 10);
  256. test_arguments();
  257. printf(" " STR_OK " Arguments test passed.\n\n");
  258. test_open_config_close();
  259. printf(" " STR_OK " Open/close test passed.\n\n");
  260. test_loopback();
  261. printf(" " STR_OK " Loopback test passed.\n\n");
  262. test_interactive();
  263. printf(" " STR_OK " Interactive test passed.\n\n");
  264. printf("All tests passed!\n");
  265. return 0;
  266. }