test_gpio.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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 <string.h>
  9. #include <errno.h>
  10. #include <sys/wait.h>
  11. #include <pthread.h>
  12. #include <semaphore.h>
  13. #include <unistd.h>
  14. #include "../src/gpio.h"
  15. const char *device;
  16. unsigned int pin_input, pin_output;
  17. void test_arguments(void) {
  18. gpio_t *gpio;
  19. ptest();
  20. /* Allocate GPIO */
  21. gpio = gpio_new();
  22. passert(gpio != NULL);
  23. /* Invalid direction */
  24. passert(gpio_open(gpio, device, pin_input, 5) == GPIO_ERROR_ARG);
  25. /* Free GPIO */
  26. gpio_free(gpio);
  27. }
  28. void test_open_config_close(void) {
  29. gpio_t *gpio;
  30. bool value;
  31. gpio_direction_t direction;
  32. gpio_edge_t edge;
  33. char label[32];
  34. gpio_bias_t bias;
  35. gpio_drive_t drive;
  36. bool inverted;
  37. ptest();
  38. /* Allocate GPIO */
  39. gpio = gpio_new();
  40. passert(gpio != NULL);
  41. /* Open non-existent GPIO */
  42. passert(gpio_open(gpio, device, -1, GPIO_DIR_IN) == GPIO_ERROR_OPEN);
  43. passert(gpio_errno(gpio) == EINVAL);
  44. /* Open legitimate GPIO */
  45. passert(gpio_open(gpio, device, pin_output, GPIO_DIR_IN) == 0);
  46. /* Check properties */
  47. passert(gpio_line(gpio) == pin_output);
  48. passert(gpio_fd(gpio) >= 0);
  49. passert(gpio_chip_fd(gpio) >= 0);
  50. /* Check default label */
  51. passert(gpio_label(gpio, label, sizeof(label)) == 0);
  52. passert(strncmp(label, "periphery", sizeof(label)) == 0);
  53. /* Invalid direction */
  54. passert(gpio_set_direction(gpio, 5) == GPIO_ERROR_ARG);
  55. /* Invalid interrupt edge */
  56. passert(gpio_set_edge(gpio, 5) == GPIO_ERROR_ARG);
  57. /* Invalid bias */
  58. passert(gpio_set_bias(gpio, 5) == GPIO_ERROR_ARG);
  59. /* Invalid drive */
  60. passert(gpio_set_drive(gpio, 5) == GPIO_ERROR_ARG);
  61. /* Set direction out, check direction out, check value low */
  62. passert(gpio_set_direction(gpio, GPIO_DIR_OUT) == 0);
  63. passert(gpio_get_direction(gpio, &direction) == 0);
  64. passert(direction == GPIO_DIR_OUT);
  65. passert(gpio_read(gpio, &value) == 0);
  66. passert(value == false);
  67. /* Set direction out low, check direction out, check value low */
  68. passert(gpio_set_direction(gpio, GPIO_DIR_OUT_LOW) == 0);
  69. passert(gpio_get_direction(gpio, &direction) == 0);
  70. passert(direction == GPIO_DIR_OUT);
  71. passert(gpio_read(gpio, &value) == 0);
  72. passert(value == false);
  73. /* Set direction out high, check direction out, check value high */
  74. passert(gpio_set_direction(gpio, GPIO_DIR_OUT_HIGH) == 0);
  75. passert(gpio_get_direction(gpio, &direction) == 0);
  76. passert(direction == GPIO_DIR_OUT);
  77. passert(gpio_read(gpio, &value) == 0);
  78. passert(value == true);
  79. /* Set drive open drain, check drive open drain */
  80. passert(gpio_set_drive(gpio, GPIO_DRIVE_OPEN_DRAIN) == 0);
  81. passert(gpio_get_drive(gpio, &drive) == 0);
  82. passert(drive == GPIO_DRIVE_OPEN_DRAIN);
  83. /* Set drive open source, check drive open source */
  84. passert(gpio_set_drive(gpio, GPIO_DRIVE_OPEN_SOURCE) == 0);
  85. passert(gpio_get_drive(gpio, &drive) == 0);
  86. passert(drive == GPIO_DRIVE_OPEN_SOURCE);
  87. /* Set drive default, check drive default */
  88. passert(gpio_set_drive(gpio, GPIO_DRIVE_DEFAULT) == 0);
  89. passert(gpio_get_drive(gpio, &drive) == 0);
  90. passert(drive == GPIO_DRIVE_DEFAULT);
  91. /* Set inverted true, check inverted true */
  92. passert(gpio_set_inverted(gpio, true) == 0);
  93. passert(gpio_get_inverted(gpio, &inverted) == 0);
  94. passert(inverted == true);
  95. /* Set inverted false, check inverted false */
  96. passert(gpio_set_inverted(gpio, false) == 0);
  97. passert(gpio_get_inverted(gpio, &inverted) == 0);
  98. passert(inverted == false);
  99. /* Attempt to set interrupt edge on output GPIO */
  100. passert(gpio_set_edge(gpio, GPIO_EDGE_RISING) == GPIO_ERROR_INVALID_OPERATION);
  101. /* Attempt to read event on output GPIO */
  102. passert(gpio_read_event(gpio, &edge, NULL) == GPIO_ERROR_INVALID_OPERATION);
  103. /* Set direction in, check direction in */
  104. passert(gpio_set_direction(gpio, GPIO_DIR_IN) == 0);
  105. passert(gpio_get_direction(gpio, &direction) == 0);
  106. passert(direction == GPIO_DIR_IN);
  107. passert(gpio_read(gpio, &value) == 0);
  108. /* Set edge none, check edge none */
  109. passert(gpio_set_edge(gpio, GPIO_EDGE_NONE) == 0);
  110. passert(gpio_get_edge(gpio, &edge) == 0);
  111. passert(edge == GPIO_EDGE_NONE);
  112. /* Set edge rising, check edge rising */
  113. passert(gpio_set_edge(gpio, GPIO_EDGE_RISING) == 0);
  114. passert(gpio_get_edge(gpio, &edge) == 0);
  115. passert(edge == GPIO_EDGE_RISING);
  116. /* Set edge falling, check edge falling */
  117. passert(gpio_set_edge(gpio, GPIO_EDGE_FALLING) == 0);
  118. passert(gpio_get_edge(gpio, &edge) == 0);
  119. passert(edge == GPIO_EDGE_FALLING);
  120. /* Set edge both, check edge both */
  121. passert(gpio_set_edge(gpio, GPIO_EDGE_BOTH) == 0);
  122. passert(gpio_get_edge(gpio, &edge) == 0);
  123. passert(edge == GPIO_EDGE_BOTH);
  124. /* Set edge none, check edge none */
  125. passert(gpio_set_edge(gpio, GPIO_EDGE_NONE) == 0);
  126. passert(gpio_get_edge(gpio, &edge) == 0);
  127. passert(edge == GPIO_EDGE_NONE);
  128. /* Set bias pull up, check bias pull up */
  129. passert(gpio_set_bias(gpio, GPIO_BIAS_PULL_UP) == 0);
  130. passert(gpio_get_bias(gpio, &bias) == 0);
  131. passert(bias == GPIO_BIAS_PULL_UP);
  132. /* Set bias pull down, check bias pull down */
  133. passert(gpio_set_bias(gpio, GPIO_BIAS_PULL_DOWN) == 0);
  134. passert(gpio_get_bias(gpio, &bias) == 0);
  135. passert(bias == GPIO_BIAS_PULL_DOWN);
  136. /* Set bias disable, check bias disable */
  137. passert(gpio_set_bias(gpio, GPIO_BIAS_DISABLE) == 0);
  138. passert(gpio_get_bias(gpio, &bias) == 0);
  139. passert(bias == GPIO_BIAS_DISABLE);
  140. /* Set bias default, check bias default */
  141. passert(gpio_set_bias(gpio, GPIO_BIAS_DEFAULT) == 0);
  142. passert(gpio_get_bias(gpio, &bias) == 0);
  143. passert(bias == GPIO_BIAS_DEFAULT);
  144. /* Attempt to set drive on input GPIO */
  145. passert(gpio_set_drive(gpio, GPIO_DRIVE_OPEN_DRAIN) == GPIO_ERROR_INVALID_OPERATION);
  146. /* Close GPIO */
  147. passert(gpio_close(gpio) == 0);
  148. /* Open GPIO with advanced open */
  149. gpio_config_t config = {
  150. .direction = GPIO_DIR_IN,
  151. .edge = GPIO_EDGE_RISING,
  152. .bias = GPIO_BIAS_DEFAULT,
  153. .drive = GPIO_DRIVE_DEFAULT,
  154. .inverted = false,
  155. .label = "test123",
  156. };
  157. passert(gpio_open_advanced(gpio, device, pin_input, &config) == 0);
  158. /* Check properties */
  159. passert(gpio_line(gpio) == pin_input);
  160. passert(gpio_fd(gpio) >= 0);
  161. passert(gpio_chip_fd(gpio) >= 0);
  162. /* Check direction */
  163. passert(gpio_get_direction(gpio, &direction) == 0);
  164. passert(direction == GPIO_DIR_IN);
  165. /* Check edge */
  166. passert(gpio_get_edge(gpio, &edge) == 0);
  167. passert(edge == GPIO_EDGE_RISING);
  168. /* Check bias */
  169. passert(gpio_get_bias(gpio, &bias) == 0);
  170. passert(bias == GPIO_BIAS_DEFAULT);
  171. /* Check drive */
  172. passert(gpio_get_drive(gpio, &drive) == 0);
  173. passert(drive == GPIO_DRIVE_DEFAULT);
  174. /* Check inverted */
  175. passert(gpio_get_inverted(gpio, &inverted) == 0);
  176. passert(inverted == false);
  177. /* Check label */
  178. passert(gpio_label(gpio, label, sizeof(label)) == 0);
  179. passert(strncmp(label, "test123", sizeof(label)) == 0);
  180. /* Close GPIO */
  181. passert(gpio_close(gpio) == 0);
  182. /* Free GPIO */
  183. gpio_free(gpio);
  184. }
  185. /* Threaded poll helper functions */
  186. typedef struct {
  187. sem_t *sem;
  188. gpio_t *gpio;
  189. int timeout_ms;
  190. } gpio_poll_args_t;
  191. void *gpio_poll_thread(void *arg) {
  192. gpio_poll_args_t *args = (gpio_poll_args_t *)arg;
  193. gpio_t *gpio = args->gpio;
  194. int timeout_ms = args->timeout_ms;
  195. assert(sem_post(args->sem) == 0);
  196. intptr_t ret = gpio_poll(gpio, timeout_ms);
  197. return (void *)ret;
  198. }
  199. void gpio_poll_start(pthread_t *thread, gpio_t *gpio, int timeout_ms) {
  200. sem_t sem;
  201. gpio_poll_args_t args = {.sem = &sem, .gpio = gpio, .timeout_ms = timeout_ms};
  202. assert(sem_init(&sem, 0, 0) == 0);
  203. assert(pthread_create(thread, NULL, &gpio_poll_thread, &args) == 0);
  204. assert(sem_wait(&sem) == 0);
  205. }
  206. int gpio_poll_join(pthread_t thread) {
  207. void *ret;
  208. assert(pthread_join(thread, &ret) == 0);
  209. return (intptr_t)ret;
  210. }
  211. void test_loopback(void) {
  212. gpio_t *gpio_in, *gpio_out;
  213. pthread_t poll_thread;
  214. bool value;
  215. gpio_edge_t edge;
  216. ptest();
  217. /* Allocate GPIO */
  218. gpio_in = gpio_new();
  219. passert(gpio_in != NULL);
  220. gpio_out = gpio_new();
  221. passert(gpio_out != NULL);
  222. /* Open in and out pins */
  223. passert(gpio_open(gpio_in, device, pin_input, GPIO_DIR_IN) == 0);
  224. passert(gpio_open(gpio_out, device, pin_output, GPIO_DIR_OUT) == 0);
  225. /* Drive out low, check in low */
  226. passert(gpio_write(gpio_out, false) == 0);
  227. passert(gpio_read(gpio_in, &value) == 0);
  228. passert(value == false);
  229. /* Drive out high, check in high */
  230. passert(gpio_write(gpio_out, true) == 0);
  231. passert(gpio_read(gpio_in, &value) == 0);
  232. passert(value == true);
  233. /* Check poll falling 1 -> 0 interrupt */
  234. passert(gpio_set_edge(gpio_in, GPIO_EDGE_FALLING) == 0);
  235. gpio_poll_start(&poll_thread, gpio_in, 1000);
  236. passert(gpio_write(gpio_out, false) == 0);
  237. passert(gpio_poll_join(poll_thread) == 1);
  238. passert(gpio_read(gpio_in, &value) == 0);
  239. passert(value == false);
  240. passert(gpio_read_event(gpio_in, &edge, NULL) == 0);
  241. passert(edge == GPIO_EDGE_FALLING);
  242. /* Check poll rising 0 -> 1 interrupt */
  243. passert(gpio_set_edge(gpio_in, GPIO_EDGE_RISING) == 0);
  244. gpio_poll_start(&poll_thread, gpio_in, 1000);
  245. passert(gpio_write(gpio_out, true) == 0);
  246. passert(gpio_poll_join(poll_thread) == 1);
  247. passert(gpio_read(gpio_in, &value) == 0);
  248. passert(value == true);
  249. passert(gpio_read_event(gpio_in, &edge, NULL) == 0);
  250. passert(edge == GPIO_EDGE_RISING);
  251. /* Set both edge */
  252. passert(gpio_set_edge(gpio_in, GPIO_EDGE_BOTH) == 0);
  253. /* Check poll falling 1 -> 0 interrupt */
  254. gpio_poll_start(&poll_thread, gpio_in, 1000);
  255. passert(gpio_write(gpio_out, false) == 0);
  256. passert(gpio_poll_join(poll_thread) == 1);
  257. passert(gpio_read(gpio_in, &value) == 0);
  258. passert(value == false);
  259. passert(gpio_read_event(gpio_in, &edge, NULL) == 0);
  260. passert(edge == GPIO_EDGE_FALLING);
  261. /* Check poll rising 0 -> 1 interrupt */
  262. gpio_poll_start(&poll_thread, gpio_in, 1000);
  263. passert(gpio_write(gpio_out, true) == 0);
  264. passert(gpio_poll_join(poll_thread) == 1);
  265. passert(gpio_read(gpio_in, &value) == 0);
  266. passert(value == true);
  267. passert(gpio_read_event(gpio_in, &edge, NULL) == 0);
  268. passert(edge == GPIO_EDGE_RISING);
  269. /* Check poll timeout */
  270. passert(gpio_poll(gpio_in, 1000) == 0);
  271. /* Test gpio_poll_multiple() API with one GPIO */
  272. gpio_t *gpios[1] = {gpio_in};
  273. bool gpios_ready[1] = {false};
  274. /* Check poll falling 1 -> 0 interrupt */
  275. passert(gpio_write(gpio_out, false) == 0);
  276. passert(gpio_poll_multiple(gpios, 1, 1000, gpios_ready) == 1);
  277. passert(gpios_ready[0] == true);
  278. passert(gpio_read(gpio_in, &value) == 0);
  279. passert(value == false);
  280. passert(gpio_read_event(gpio_in, &edge, NULL) == 0);
  281. passert(edge == GPIO_EDGE_FALLING);
  282. /* Check poll rising 0 -> 1 interrupt */
  283. passert(gpio_write(gpio_out, true) == 0);
  284. passert(gpio_poll_multiple(gpios, 1, 1000, gpios_ready) == 1);
  285. passert(gpios_ready[0] == true);
  286. passert(gpio_read(gpio_in, &value) == 0);
  287. passert(value == true);
  288. passert(gpio_read_event(gpio_in, &edge, NULL) == 0);
  289. passert(edge == GPIO_EDGE_RISING);
  290. /* Check poll timeout */
  291. passert(gpio_poll_multiple(gpios, 1, 1000, gpios_ready) == 0);
  292. passert(gpios_ready[0] == false);
  293. passert(gpio_close(gpio_in) == 0);
  294. passert(gpio_close(gpio_out) == 0);
  295. /* Open both GPIOs as inputs */
  296. passert(gpio_open(gpio_in, device, pin_input, GPIO_DIR_IN) == 0);
  297. passert(gpio_open(gpio_out, device, pin_output, GPIO_DIR_IN) == 0);
  298. /* Set bias pull-up, check value is high */
  299. passert(gpio_set_bias(gpio_in, GPIO_BIAS_PULL_UP) == 0);
  300. usleep(1000);
  301. passert(gpio_read(gpio_in, &value) == 0);
  302. passert(value == true);
  303. /* Set bias pull-down, check value is low */
  304. passert(gpio_set_bias(gpio_in, GPIO_BIAS_PULL_DOWN) == 0);
  305. usleep(1000);
  306. passert(gpio_read(gpio_in, &value) == 0);
  307. passert(value == false);
  308. passert(gpio_close(gpio_in) == 0);
  309. passert(gpio_close(gpio_out) == 0);
  310. /* Free GPIO */
  311. gpio_free(gpio_in);
  312. gpio_free(gpio_out);
  313. }
  314. bool getc_yes(void) {
  315. char buf[4];
  316. fgets(buf, sizeof(buf), stdin);
  317. return (buf[0] == 'y' || buf[0] == 'Y');
  318. }
  319. void test_interactive(void) {
  320. char str[256];
  321. gpio_t *gpio;
  322. ptest();
  323. /* Allocate GPIO */
  324. gpio = gpio_new();
  325. passert(gpio != NULL);
  326. passert(gpio_open(gpio, device, pin_output, GPIO_DIR_OUT) == 0);
  327. printf("Starting interactive test. Get out your multimeter, buddy!\n");
  328. printf("Press enter to continue...\n");
  329. getc(stdin);
  330. /* Check tostring */
  331. passert(gpio_tostring(gpio, str, sizeof(str)) > 0);
  332. printf("GPIO description: %s\n", str);
  333. printf("GPIO description looks OK? y/n\n");
  334. passert(getc_yes());
  335. /* Drive GPIO out low */
  336. passert(gpio_write(gpio, false) == 0);
  337. printf("GPIO out is low? y/n\n");
  338. passert(getc_yes());
  339. /* Drive GPIO out high */
  340. passert(gpio_write(gpio, true) == 0);
  341. printf("GPIO out is high? y/n\n");
  342. passert(getc_yes());
  343. /* Drive GPIO out low */
  344. passert(gpio_write(gpio, false) == 0);
  345. printf("GPIO out is low? y/n\n");
  346. passert(getc_yes());
  347. passert(gpio_close(gpio) == 0);
  348. /* Free GPIO */
  349. gpio_free(gpio);
  350. }
  351. int main(int argc, char *argv[]) {
  352. if (argc < 4) {
  353. fprintf(stderr, "Usage: %s <GPIO chip device> <GPIO #1> <GPIO #2>\n\n", argv[0]);
  354. fprintf(stderr, "[1/4] Argument test: No requirements.\n");
  355. fprintf(stderr, "[2/4] Open/close test: GPIO #2 should be real.\n");
  356. fprintf(stderr, "[3/4] Loopback test: GPIOs #1 and #2 should be connected with a wire.\n");
  357. fprintf(stderr, "[4/4] Interactive test: GPIO #2 should be observed with a multimeter.\n\n");
  358. fprintf(stderr, "Hint: for Raspberry Pi 3,\n");
  359. fprintf(stderr, "Use GPIO 17 (header pin 11) and GPIO 27 (header pin 13),\n");
  360. fprintf(stderr, "connect a loopback between them, and run this test with:\n");
  361. fprintf(stderr, " %s /dev/gpiochip0 17 27\n\n", argv[0]);
  362. exit(1);
  363. }
  364. device = argv[1];
  365. pin_input = strtoul(argv[2], NULL, 10);
  366. pin_output = strtoul(argv[3], NULL, 10);
  367. test_arguments();
  368. printf(" " STR_OK " Arguments test passed.\n\n");
  369. test_open_config_close();
  370. printf(" " STR_OK " Open/close test passed.\n\n");
  371. test_loopback();
  372. printf(" " STR_OK " Loopback test passed.\n\n");
  373. test_interactive();
  374. printf(" " STR_OK " Interactive test passed.\n\n");
  375. printf("All tests passed!\n");
  376. return 0;
  377. }