| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- /*
- * c-periphery
- * https://github.com/vsergeev/c-periphery
- * License: MIT
- */
- #include "test.h"
- #include <stdlib.h>
- #include <errno.h>
- #include <sys/wait.h>
- #include <pthread.h>
- #include <semaphore.h>
- #include <unistd.h>
- #include "../src/gpio.h"
- unsigned int pin_input, pin_output;
- void test_arguments(void) {
- gpio_t *gpio;
- ptest();
- /* Allocate GPIO */
- gpio = gpio_new();
- passert(gpio != NULL);
- /* Invalid direction */
- passert(gpio_open_sysfs(gpio, pin_input, 5) == GPIO_ERROR_ARG);
- /* Free GPIO */
- gpio_free(gpio);
- }
- void test_open_config_close(void) {
- gpio_t *gpio;
- bool value;
- gpio_direction_t direction;
- gpio_edge_t edge;
- gpio_bias_t bias;
- gpio_drive_t drive;
- bool inverted;
- ptest();
- /* Allocate GPIO */
- gpio = gpio_new();
- passert(gpio != NULL);
- /* Open non-existent GPIO -- export should fail with EINVAL */
- passert(gpio_open_sysfs(gpio, 9999, GPIO_DIR_IN) == GPIO_ERROR_OPEN);
- passert(gpio_errno(gpio) == EINVAL);
- /* Open legitimate GPIO */
- passert(gpio_open_sysfs(gpio, pin_output, GPIO_DIR_IN) == 0);
- /* Check properties */
- passert(gpio_line(gpio) == pin_output);
- passert(gpio_fd(gpio) >= 0);
- /* Invalid direction */
- passert(gpio_set_direction(gpio, 5) == GPIO_ERROR_ARG);
- /* Invalid interrupt edge */
- passert(gpio_set_edge(gpio, 5) == GPIO_ERROR_ARG);
- /* Unsupported setting bias */
- passert(gpio_set_bias(gpio, GPIO_BIAS_PULL_UP) == GPIO_ERROR_UNSUPPORTED);
- passert(gpio_get_bias(gpio, &bias) == GPIO_ERROR_UNSUPPORTED);
- /* Unsupported setting drive */
- passert(gpio_set_drive(gpio, GPIO_DRIVE_OPEN_DRAIN) == GPIO_ERROR_UNSUPPORTED);
- passert(gpio_get_drive(gpio, &drive) == GPIO_ERROR_UNSUPPORTED);
- /* Unsupported property */
- passert(gpio_chip_fd(gpio) == GPIO_ERROR_UNSUPPORTED);
- /* Unsupported method */
- passert(gpio_read_event(gpio, &edge, NULL) == GPIO_ERROR_UNSUPPORTED);
- /* Set direction out, check direction out, check value low */
- passert(gpio_set_direction(gpio, GPIO_DIR_OUT) == 0);
- passert(gpio_get_direction(gpio, &direction) == 0);
- passert(direction == GPIO_DIR_OUT);
- passert(gpio_read(gpio, &value) == 0);
- passert(value == false);
- /* Set direction out low, check direction out, check value low */
- passert(gpio_set_direction(gpio, GPIO_DIR_OUT_LOW) == 0);
- passert(gpio_get_direction(gpio, &direction) == 0);
- passert(direction == GPIO_DIR_OUT);
- passert(gpio_read(gpio, &value) == 0);
- passert(value == false);
- /* Set direction out high, check direction out, check value high */
- passert(gpio_set_direction(gpio, GPIO_DIR_OUT_HIGH) == 0);
- passert(gpio_get_direction(gpio, &direction) == 0);
- passert(direction == GPIO_DIR_OUT);
- passert(gpio_read(gpio, &value) == 0);
- passert(value == true);
- /* Set inverted true, check inverted */
- passert(gpio_set_inverted(gpio, true) == 0);
- passert(gpio_get_inverted(gpio, &inverted) == 0);
- passert(inverted == true);
- /* Set inverted false, check inverted */
- passert(gpio_set_inverted(gpio, false) == 0);
- passert(gpio_get_inverted(gpio, &inverted) == 0);
- passert(inverted == false);
- /* Set direction in, check direction in */
- passert(gpio_set_direction(gpio, GPIO_DIR_IN) == 0);
- passert(gpio_get_direction(gpio, &direction) == 0);
- passert(direction == GPIO_DIR_IN);
- passert(gpio_read(gpio, &value) == 0);
- /* Set edge none, check edge none */
- passert(gpio_set_edge(gpio, GPIO_EDGE_NONE) == 0);
- passert(gpio_get_edge(gpio, &edge) == 0);
- passert(edge == GPIO_EDGE_NONE);
- /* Set edge rising, check edge rising */
- passert(gpio_set_edge(gpio, GPIO_EDGE_RISING) == 0);
- passert(gpio_get_edge(gpio, &edge) == 0);
- passert(edge == GPIO_EDGE_RISING);
- /* Set edge falling, check edge falling */
- passert(gpio_set_edge(gpio, GPIO_EDGE_FALLING) == 0);
- passert(gpio_get_edge(gpio, &edge) == 0);
- passert(edge == GPIO_EDGE_FALLING);
- /* Set edge both, check edge both */
- passert(gpio_set_edge(gpio, GPIO_EDGE_BOTH) == 0);
- passert(gpio_get_edge(gpio, &edge) == 0);
- passert(edge == GPIO_EDGE_BOTH);
- /* Set edge none, check edge none */
- passert(gpio_set_edge(gpio, GPIO_EDGE_NONE) == 0);
- passert(gpio_get_edge(gpio, &edge) == 0);
- passert(edge == GPIO_EDGE_NONE);
- /* Close GPIO */
- passert(gpio_close(gpio) == 0);
- /* Free GPIO */
- gpio_free(gpio);
- }
- /* Threaded poll helper functions */
- typedef struct {
- sem_t *sem;
- gpio_t *gpio;
- int timeout_ms;
- } gpio_poll_args_t;
- void *gpio_poll_thread(void *arg) {
- gpio_poll_args_t *args = (gpio_poll_args_t *)arg;
- gpio_t *gpio = args->gpio;
- int timeout_ms = args->timeout_ms;
- assert(sem_post(args->sem) == 0);
- intptr_t ret = gpio_poll(gpio, timeout_ms);
- return (void *)ret;
- }
- void gpio_poll_start(pthread_t *thread, gpio_t *gpio, int timeout_ms) {
- sem_t sem;
- gpio_poll_args_t args = {.sem = &sem, .gpio = gpio, .timeout_ms = timeout_ms};
- assert(sem_init(&sem, 0, 0) == 0);
- assert(pthread_create(thread, NULL, &gpio_poll_thread, &args) == 0);
- assert(sem_wait(&sem) == 0);
- }
- int gpio_poll_join(pthread_t thread) {
- void *ret;
- assert(pthread_join(thread, &ret) == 0);
- return (intptr_t)ret;
- }
- void test_loopback(void) {
- gpio_t *gpio_in, *gpio_out;
- pthread_t poll_thread;
- bool value;
- ptest();
- /* Allocate GPIO */
- gpio_in = gpio_new();
- passert(gpio_in != NULL);
- gpio_out = gpio_new();
- passert(gpio_out != NULL);
- /* Open in and out pins */
- passert(gpio_open_sysfs(gpio_in, pin_input, GPIO_DIR_IN) == 0);
- passert(gpio_open_sysfs(gpio_out, pin_output, GPIO_DIR_OUT) == 0);
- /* Drive out low, check in low */
- passert(gpio_write(gpio_out, false) == 0);
- passert(gpio_read(gpio_in, &value) == 0);
- passert(value == false);
- /* Drive out high, check in high */
- passert(gpio_write(gpio_out, true) == 0);
- passert(gpio_read(gpio_in, &value) == 0);
- passert(value == true);
- /* Check poll falling 1 -> 0 interrupt */
- passert(gpio_set_edge(gpio_in, GPIO_EDGE_FALLING) == 0);
- gpio_poll_start(&poll_thread, gpio_in, 1000);
- passert(gpio_write(gpio_out, false) == 0);
- passert(gpio_poll_join(poll_thread) == 1);
- passert(gpio_read(gpio_in, &value) == 0);
- passert(value == false);
- /* Check poll rising 0 -> 1 interrupt */
- passert(gpio_set_edge(gpio_in, GPIO_EDGE_RISING) == 0);
- gpio_poll_start(&poll_thread, gpio_in, 1000);
- passert(gpio_write(gpio_out, true) == 0);
- passert(gpio_poll_join(poll_thread) == 1);
- passert(gpio_read(gpio_in, &value) == 0);
- passert(value == true);
- /* Check poll timeout */
- passert(gpio_poll(gpio_in, 1000) == 0);
- /* Test gpio_poll_multiple() API with one GPIO */
- gpio_t *gpios[1] = {gpio_in};
- bool gpios_ready[1] = {false};
- passert(gpio_set_edge(gpio_in, GPIO_EDGE_BOTH) == 0);
- /* Check poll falling 1 -> 0 interrupt */
- passert(gpio_write(gpio_out, false) == 0);
- passert(gpio_poll_multiple(gpios, 1, 1000, gpios_ready) == 1);
- passert(gpios_ready[0] == true);
- passert(gpio_read(gpio_in, &value) == 0);
- passert(value == false);
- /* Check poll rising 0 -> 1 interrupt */
- passert(gpio_write(gpio_out, true) == 0);
- passert(gpio_poll_multiple(gpios, 1, 1000, gpios_ready) == 1);
- passert(gpios_ready[0] == true);
- passert(gpio_read(gpio_in, &value) == 0);
- passert(value == true);
- /* Check poll timeout */
- passert(gpio_poll_multiple(gpios, 1, 1000, gpios_ready) == 0);
- passert(gpios_ready[0] == false);
- passert(gpio_close(gpio_in) == 0);
- passert(gpio_close(gpio_out) == 0);
- /* Free GPIO */
- gpio_free(gpio_in);
- gpio_free(gpio_out);
- }
- bool getc_yes(void) {
- char buf[4];
- fgets(buf, sizeof(buf), stdin);
- return (buf[0] == 'y' || buf[0] == 'Y');
- }
- void test_interactive(void) {
- char str[256];
- gpio_t *gpio;
- ptest();
- /* Allocate GPIO */
- gpio = gpio_new();
- passert(gpio != NULL);
- passert(gpio_open_sysfs(gpio, pin_output, GPIO_DIR_OUT) == 0);
- printf("Starting interactive test. Get out your multimeter, buddy!\n");
- printf("Press enter to continue...\n");
- getc(stdin);
- /* Check tostring */
- passert(gpio_tostring(gpio, str, sizeof(str)) > 0);
- printf("GPIO description: %s\n", str);
- printf("GPIO description looks OK? y/n\n");
- passert(getc_yes());
- /* Drive GPIO out low */
- passert(gpio_write(gpio, false) == 0);
- printf("GPIO out is low? y/n\n");
- passert(getc_yes());
- /* Drive GPIO out high */
- passert(gpio_write(gpio, true) == 0);
- printf("GPIO out is high? y/n\n");
- passert(getc_yes());
- /* Drive GPIO out low */
- passert(gpio_write(gpio, false) == 0);
- printf("GPIO out is low? y/n\n");
- passert(getc_yes());
- passert(gpio_close(gpio) == 0);
- /* Free GPIO */
- gpio_free(gpio);
- }
- int main(int argc, char *argv[]) {
- if (argc < 3) {
- fprintf(stderr, "Usage: %s <GPIO #1> <GPIO #2>\n\n", argv[0]);
- fprintf(stderr, "[1/4] Argument test: No requirements.\n");
- fprintf(stderr, "[2/4] Open/close test: GPIO #2 should be real.\n");
- fprintf(stderr, "[3/4] Loopback test: GPIOs #1 and #2 should be connected with a wire.\n");
- fprintf(stderr, "[4/4] Interactive test: GPIO #2 should be observed with a multimeter.\n\n");
- fprintf(stderr, "Hint: for Raspberry Pi 3,\n");
- fprintf(stderr, "Use GPIO 17 (header pin 11) and GPIO 27 (header pin 13),\n");
- fprintf(stderr, "connect a loopback between them, and run this test with:\n");
- fprintf(stderr, " %s 17 27\n\n", argv[0]);
- exit(1);
- }
- pin_input = strtoul(argv[1], NULL, 10);
- pin_output = strtoul(argv[2], NULL, 10);
- test_arguments();
- printf(" " STR_OK " Arguments test passed.\n\n");
- test_open_config_close();
- printf(" " STR_OK " Open/close test passed.\n\n");
- test_loopback();
- printf(" " STR_OK " Loopback test passed.\n\n");
- test_interactive();
- printf(" " STR_OK " Interactive test passed.\n\n");
- printf("All tests passed!\n");
- return 0;
- }
|