Skip to content

Commit

Permalink
gpio: add advanced open functions for cdev implementation
Browse files Browse the repository at this point in the history
resolves #22.
  • Loading branch information
vsergeev committed May 29, 2020
1 parent 0f20c96 commit 7e7b91a
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 22 deletions.
42 changes: 42 additions & 0 deletions docs/gpio.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
```
Expand Down
72 changes: 50 additions & 22 deletions src/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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);
}
12 changes: 12 additions & 0 deletions src/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
37 changes: 37 additions & 0 deletions tests/test_gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit 7e7b91a

Please sign in to comment.