diff --git a/docs/gpio.md b/docs/gpio.md index 2975256..5b40308 100644 --- a/docs/gpio.md +++ b/docs/gpio.md @@ -11,6 +11,8 @@ GPIO wrapper functions for Linux userspace character device `gpio-cdev` and sysf gpio_t *gpio_new(void); int gpio_open(gpio_t *gpio, const char *path, unsigned int line, gpio_direction_t direction); int gpio_open_name(gpio_t *gpio, const char *path, const char *name, gpio_direction_t direction); +int gpio_open_advanced(gpio_t *gpio, const char *path, unsigned int line, const gpio_config_t *config); +int gpio_open_name_advanced(gpio_t *gpio, const char *path, const char *name, const gpio_config_t *config); int gpio_open_sysfs(gpio_t *gpio, unsigned int line, gpio_direction_t direction); int gpio_read(gpio_t *gpio, bool *value); int gpio_write(gpio_t *gpio, bool value); @@ -111,6 +113,46 @@ Returns 0 on success, or a negative [GPIO error code](#return-value) on failure. ------ +``` c +typedef struct gpio_config { + gpio_direction_t direction; + gpio_edge_t edge; + gpio_bias_t bias; + gpio_drive_t drive; + bool inverted; + const char *label; +} gpio_config_t; + +int gpio_open_advanced(gpio_t *gpio, const char *path, unsigned int line, const gpio_config_t *config); +``` +Open the character device GPIO with the specified GPIO line and configuration at the specified character device GPIO chip path (e.g. `/dev/gpiochip0`). + +`gpio` should be a valid pointer to an allocated GPIO handle structure. `path` is the GPIO chip character device path. `line` is the GPIO line number. `config` should be a valid pointer to a `gpio_config_t` structure with valid values. `label` can be `NULL` for a default consumer label. + +Returns 0 on success, or a negative [GPIO error code](#return-value) on failure. + +------ + +``` c +typedef struct gpio_config { + gpio_direction_t direction; + gpio_edge_t edge; + gpio_bias_t bias; + gpio_drive_t drive; + bool inverted; + const char *label; +} gpio_config_t; + +int gpio_open_name_advanced(gpio_t *gpio, const char *path, const char *name, const gpio_config_t *config); +``` +Open the character device GPIO with the specified GPIO name and configuration at the specified character device GPIO chip path (e.g. `/dev/gpiochip0`). + +`gpio` should be a valid pointer to an allocated GPIO handle structure. `path` is the GPIO chip character device path. `name` is the GPIO line name. `config` should be a valid pointer to a `gpio_config_t` structure with valid values. `label` can be `NULL` for a default consumer label. + +Returns 0 on success, or a negative [GPIO error code](#return-value) on failure. + +------ + ``` c int gpio_open_sysfs(gpio_t *gpio, unsigned int line, gpio_direction_t direction); ``` diff --git a/src/gpio.c b/src/gpio.c index 96f5be2..b4039f1 100644 --- a/src/gpio.c +++ b/src/gpio.c @@ -1247,12 +1247,27 @@ static const struct gpio_ops gpio_cdev_ops = { .tostring = gpio_cdev_tostring, }; -int gpio_open(gpio_t *gpio, const char *path, unsigned int line, gpio_direction_t direction) { +int gpio_open_advanced(gpio_t *gpio, const char *path, unsigned int line, const gpio_config_t *config) { int ret, fd; - if (direction != GPIO_DIR_IN && direction != GPIO_DIR_OUT && direction != GPIO_DIR_OUT_LOW && direction != GPIO_DIR_OUT_HIGH) + if (config->direction != GPIO_DIR_IN && config->direction != GPIO_DIR_OUT && config->direction != GPIO_DIR_OUT_LOW && config->direction != GPIO_DIR_OUT_HIGH) return _gpio_error(gpio, GPIO_ERROR_ARG, 0, "Invalid GPIO direction (can be in, out, low, high)"); + if (config->edge != GPIO_EDGE_NONE && config->edge != GPIO_EDGE_RISING && config->edge != GPIO_EDGE_FALLING && config->edge != GPIO_EDGE_BOTH) + return _gpio_error(gpio, GPIO_ERROR_ARG, 0, "Invalid GPIO interrupt edge (can be none, rising, falling, both)"); + + if (config->direction != GPIO_DIR_IN && config->edge != GPIO_EDGE_NONE) + return _gpio_error(gpio, GPIO_ERROR_ARG, 0, "Invalid GPIO edge for output GPIO"); + + if (config->bias != GPIO_BIAS_DEFAULT && config->bias != GPIO_BIAS_PULL_UP && config->bias != GPIO_BIAS_PULL_DOWN && config->bias != GPIO_BIAS_DISABLE) + return _gpio_error(gpio, GPIO_ERROR_ARG, 0, "Invalid GPIO line bias (can be default, pull_up, pull_down, disable)"); + + if (config->drive != GPIO_DRIVE_DEFAULT && config->drive != GPIO_DRIVE_OPEN_DRAIN && config->drive != GPIO_DRIVE_OPEN_SOURCE) + return _gpio_error(gpio, GPIO_ERROR_ARG, 0, "Invalid GPIO line drive (can be default, open_drain, open_source)"); + + if (config->direction == GPIO_DIR_IN && config->drive != GPIO_DRIVE_DEFAULT) + return _gpio_error(gpio, GPIO_ERROR_ARG, 0, "Invalid GPIO line drive for input GPIO"); + /* Open GPIO chip */ if ((fd = open(path, 0)) < 0) return _gpio_error(gpio, GPIO_ERROR_OPEN, errno, "Opening GPIO chip"); @@ -1262,10 +1277,11 @@ int gpio_open(gpio_t *gpio, const char *path, unsigned int line, gpio_direction_ gpio->u.cdev.line = line; gpio->u.cdev.line_fd = -1; gpio->u.cdev.chip_fd = fd; - strncpy(gpio->u.cdev.label, "periphery", sizeof(gpio->u.cdev.label)); + strncpy(gpio->u.cdev.label, config->label ? config->label : "periphery", sizeof(gpio->u.cdev.label)); + gpio->u.cdev.label[sizeof(gpio->u.cdev.label) - 1] = '\0'; /* Open GPIO line */ - ret = _gpio_cdev_reopen(gpio, direction, GPIO_EDGE_NONE, GPIO_BIAS_DEFAULT, GPIO_DRIVE_DEFAULT, false); + ret = _gpio_cdev_reopen(gpio, config->direction, config->edge, config->bias, config->drive, config->inverted); if (ret < 0) { close(fd); return ret; @@ -1274,11 +1290,8 @@ int gpio_open(gpio_t *gpio, const char *path, unsigned int line, gpio_direction_ return 0; } -int gpio_open_name(gpio_t *gpio, const char *path, const char *name, gpio_direction_t direction) { - int ret, fd; - - if (direction != GPIO_DIR_IN && direction != GPIO_DIR_OUT && direction != GPIO_DIR_OUT_LOW && direction != GPIO_DIR_OUT_HIGH) - return _gpio_error(gpio, GPIO_ERROR_ARG, 0, "Invalid GPIO direction (can be in, out, low, high)"); +int gpio_open_name_advanced(gpio_t *gpio, const char *path, const char *name, const gpio_config_t *config) { + int fd; /* Open GPIO chip */ if ((fd = open(path, 0)) < 0) @@ -1316,19 +1329,34 @@ int gpio_open_name(gpio_t *gpio, const char *path, const char *name, gpio_direct return _gpio_error(gpio, GPIO_ERROR_NOT_FOUND, 0, "GPIO line \"%s\" not found by name", name); } - memset(gpio, 0, sizeof(gpio_t)); - gpio->ops = &gpio_cdev_ops; - gpio->u.cdev.line = line; - gpio->u.cdev.line_fd = -1; - gpio->u.cdev.chip_fd = fd; - strncpy(gpio->u.cdev.label, "periphery", sizeof(gpio->u.cdev.label)); + if (close(fd) < 0) + return _gpio_error(gpio, GPIO_ERROR_CLOSE, errno, "Closing GPIO chip"); - /* Open GPIO line */ - ret = _gpio_cdev_reopen(gpio, direction, GPIO_EDGE_NONE, GPIO_BIAS_DEFAULT, GPIO_DRIVE_DEFAULT, false); - if (ret < 0) { - close(fd); - return ret; - } + return gpio_open_advanced(gpio, path, line, config); +} - return 0; +int gpio_open(gpio_t *gpio, const char *path, unsigned int line, gpio_direction_t direction) { + gpio_config_t config = { + .direction = direction, + .edge = GPIO_EDGE_NONE, + .bias = GPIO_BIAS_DEFAULT, + .drive = GPIO_DRIVE_DEFAULT, + .inverted = false, + .label = NULL, + }; + + return gpio_open_advanced(gpio, path, line, &config); +} + +int gpio_open_name(gpio_t *gpio, const char *path, const char *name, gpio_direction_t direction) { + gpio_config_t config = { + .direction = direction, + .edge = GPIO_EDGE_NONE, + .bias = GPIO_BIAS_DEFAULT, + .drive = GPIO_DRIVE_DEFAULT, + .inverted = false, + .label = NULL, + }; + + return gpio_open_name_advanced(gpio, path, name, &config); } diff --git a/src/gpio.h b/src/gpio.h index e9ae442..dd5dde7 100644 --- a/src/gpio.h +++ b/src/gpio.h @@ -54,12 +54,24 @@ typedef enum gpio_drive { GPIO_DRIVE_OPEN_SOURCE, /* Open source */ } gpio_drive_t; +/* Configuration structure for gpio_open_*advanced() functions */ +typedef struct gpio_config { + gpio_direction_t direction; + gpio_edge_t edge; + gpio_bias_t bias; + gpio_drive_t drive; + bool inverted; + const char *label; /* Can be NULL for default consumer label */ +} gpio_config_t; + typedef struct gpio_handle gpio_t; /* Primary Functions */ gpio_t *gpio_new(void); int gpio_open(gpio_t *gpio, const char *path, unsigned int line, gpio_direction_t direction); int gpio_open_name(gpio_t *gpio, const char *path, const char *name, gpio_direction_t direction); +int gpio_open_advanced(gpio_t *gpio, const char *path, unsigned int line, const gpio_config_t *config); +int gpio_open_name_advanced(gpio_t *gpio, const char *path, const char *name, const gpio_config_t *config); int gpio_open_sysfs(gpio_t *gpio, unsigned int line, gpio_direction_t direction); int gpio_read(gpio_t *gpio, bool *value); int gpio_write(gpio_t *gpio, bool value); diff --git a/tests/test_gpio.c b/tests/test_gpio.c index b66cad2..76875ad 100644 --- a/tests/test_gpio.c +++ b/tests/test_gpio.c @@ -173,6 +173,43 @@ void test_open_config_close(void) { /* Close GPIO */ passert(gpio_close(gpio) == 0); + /* Open GPIO with advanced open */ + gpio_config_t config = { + .direction = GPIO_DIR_IN, + .edge = GPIO_EDGE_RISING, + .bias = GPIO_BIAS_DEFAULT, + .drive = GPIO_DRIVE_DEFAULT, + .inverted = false, + .label = "test123", + }; + passert(gpio_open_advanced(gpio, device, pin_input, &config) == 0); + + /* Check properties */ + passert(gpio_line(gpio) == pin_input); + passert(gpio_fd(gpio) >= 0); + passert(gpio_chip_fd(gpio) >= 0); + /* Check direction */ + passert(gpio_get_direction(gpio, &direction) == 0); + passert(direction == GPIO_DIR_IN); + /* Check edge */ + passert(gpio_get_edge(gpio, &edge) == 0); + passert(edge == GPIO_EDGE_RISING); + /* Check bias */ + passert(gpio_get_bias(gpio, &bias) == 0); + passert(bias == GPIO_BIAS_DEFAULT); + /* Check drive */ + passert(gpio_get_drive(gpio, &drive) == 0); + passert(drive == GPIO_DRIVE_DEFAULT); + /* Check inverted */ + passert(gpio_get_inverted(gpio, &inverted) == 0); + passert(inverted == false); + /* Check label */ + passert(gpio_label(gpio, label, sizeof(label)) == 0); + passert(strncmp(label, "test123", sizeof(label)) == 0); + + /* Close GPIO */ + passert(gpio_close(gpio) == 0); + /* Free GPIO */ gpio_free(gpio); }