mirror of
https://github.com/vsergeev/c-periphery.git
synced 2026-09-21 11:53:40 +08:00
+29
-2
@@ -27,10 +27,16 @@ int gpio_poll_multiple(gpio_t **gpios, size_t count, int timeout_ms, bool *gpios
|
||||
/* Getters */
|
||||
int gpio_get_direction(gpio_t *gpio, gpio_direction_t *direction);
|
||||
int gpio_get_edge(gpio_t *gpio, gpio_edge_t *edge);
|
||||
int gpio_get_bias(gpio_t *gpio, gpio_bias_t *bias);
|
||||
int gpio_get_drive(gpio_t *gpio, gpio_drive_t *drive);
|
||||
int gpio_get_inverted(gpio_t *gpio, bool *inverted);
|
||||
|
||||
/* Setters */
|
||||
int gpio_set_direction(gpio_t *gpio, gpio_direction_t direction);
|
||||
int gpio_set_edge(gpio_t *gpio, gpio_edge_t edge);
|
||||
int gpio_set_bias(gpio_t *gpio, gpio_bias_t edge);
|
||||
int gpio_set_drive(gpio_t *gpio, gpio_drive_t drive);
|
||||
int gpio_set_inverted(gpio_t *gpio, bool inverted);
|
||||
|
||||
/* Miscellaneous Properties */
|
||||
unsigned int gpio_line(gpio_t *gpio);
|
||||
@@ -61,6 +67,17 @@ const char *gpio_errmsg(gpio_t *gpio);
|
||||
* `GPIO_EDGE_FALLING`: Falling edge (1 -> 0 transition)
|
||||
* `GPIO_EDGE_BOTH`: Both edges (X -> !X transition)
|
||||
|
||||
* `gpio_bias_t`
|
||||
* `GPIO_BIAS_DEFAULT`: Default line bias
|
||||
* `GPIO_BIAS_PULL_UP`: Pull-up
|
||||
* `GPIO_BIAS_PULL_DOWN`: Pull-down
|
||||
* `GPIO_BIAS_DISABLE`: Disable line bias
|
||||
|
||||
* `gpio_drive_t`
|
||||
* `GPIO_DRIVE_DEFAULT`: Default line drive (push-pull)
|
||||
* `GPIO_DRIVE_OPEN_DRAIN`: Open drain
|
||||
* `GPIO_DRIVE_OPEN_SOURCE`: Open source
|
||||
|
||||
### DESCRIPTION
|
||||
|
||||
``` c
|
||||
@@ -188,8 +205,13 @@ Free a GPIO handle.
|
||||
```c
|
||||
int gpio_get_direction(gpio_t *gpio, gpio_direction_t *direction);
|
||||
int gpio_get_edge(gpio_t *gpio, gpio_edge_t *edge);
|
||||
int gpio_get_bias(gpio_t *gpio, gpio_bias_t *bias);
|
||||
int gpio_get_drive(gpio_t *gpio, gpio_drive_t *drive);
|
||||
int gpio_get_inverted(gpio_t *gpio, bool *inverted);
|
||||
```
|
||||
Get the configured direction or interrupt edge, respectively, of the GPIO.
|
||||
Get the configured direction, interrupt edge, line bias, line drive, inverted (active low) properties, respectively, of the GPIO.
|
||||
|
||||
Line bias and line drive properties are not supported by sysfs GPIOs.
|
||||
|
||||
`gpio` should be a valid pointer to a GPIO handle opened with one of the `gpio_open*()` functions.
|
||||
|
||||
@@ -200,8 +222,13 @@ Returns 0 on success, or a negative [GPIO error code](#return-value) on failure.
|
||||
```c
|
||||
int gpio_set_direction(gpio_t *gpio, gpio_direction_t direction);
|
||||
int gpio_set_edge(gpio_t *gpio, gpio_edge_t edge);
|
||||
int gpio_set_bias(gpio_t *gpio, gpio_bias_t edge);
|
||||
int gpio_set_drive(gpio_t *gpio, gpio_drive_t drive);
|
||||
int gpio_set_inverted(gpio_t *gpio, bool inverted);
|
||||
```
|
||||
Set the direction or interrupt edge, respectively, of the GPIO.
|
||||
Set the direction, interrupt edge, line bias, line drive, inverted (active low) properties, respectively, of the GPIO.
|
||||
|
||||
Line bias and line drive properties are not supported by sysfs GPIOs.
|
||||
|
||||
`gpio` should be a valid pointer to a GPIO handle opened with one of the `gpio_open*()` functions.
|
||||
|
||||
|
||||
+251
-22
@@ -34,8 +34,14 @@ struct gpio_ops {
|
||||
int (*close)(gpio_t *gpio);
|
||||
int (*get_direction)(gpio_t *gpio, gpio_direction_t *direction);
|
||||
int (*get_edge)(gpio_t *gpio, gpio_edge_t *edge);
|
||||
int (*get_bias)(gpio_t *gpio, gpio_bias_t *bias);
|
||||
int (*get_drive)(gpio_t *gpio, gpio_drive_t *drive);
|
||||
int (*get_inverted)(gpio_t *gpio, bool *inverted);
|
||||
int (*set_direction)(gpio_t *gpio, gpio_direction_t direction);
|
||||
int (*set_edge)(gpio_t *gpio, gpio_edge_t edge);
|
||||
int (*set_bias)(gpio_t *gpio, gpio_bias_t bias);
|
||||
int (*set_drive)(gpio_t *gpio, gpio_drive_t drive);
|
||||
int (*set_inverted)(gpio_t *gpio, bool inverted);
|
||||
unsigned int (*line)(gpio_t *gpio);
|
||||
int (*fd)(gpio_t *gpio);
|
||||
int (*name)(gpio_t *gpio, char *str, size_t len);
|
||||
@@ -56,6 +62,10 @@ struct gpio_handle {
|
||||
int chip_fd;
|
||||
gpio_direction_t direction;
|
||||
gpio_edge_t edge;
|
||||
gpio_bias_t bias;
|
||||
gpio_drive_t drive;
|
||||
bool inverted;
|
||||
char label[32];
|
||||
} cdev;
|
||||
struct {
|
||||
unsigned int line;
|
||||
@@ -130,6 +140,18 @@ int gpio_get_edge(gpio_t *gpio, gpio_edge_t *edge) {
|
||||
return gpio->ops->get_edge(gpio, edge);
|
||||
}
|
||||
|
||||
int gpio_get_bias(gpio_t *gpio, gpio_bias_t *bias) {
|
||||
return gpio->ops->get_bias(gpio, bias);
|
||||
}
|
||||
|
||||
int gpio_get_drive(gpio_t *gpio, gpio_drive_t *drive) {
|
||||
return gpio->ops->get_drive(gpio, drive);
|
||||
}
|
||||
|
||||
int gpio_get_inverted(gpio_t *gpio, bool *inverted) {
|
||||
return gpio->ops->get_inverted(gpio, inverted);
|
||||
}
|
||||
|
||||
int gpio_set_direction(gpio_t *gpio, gpio_direction_t direction) {
|
||||
return gpio->ops->set_direction(gpio, direction);
|
||||
}
|
||||
@@ -138,6 +160,18 @@ int gpio_set_edge(gpio_t *gpio, gpio_edge_t edge) {
|
||||
return gpio->ops->set_edge(gpio, edge);
|
||||
}
|
||||
|
||||
int gpio_set_bias(gpio_t *gpio, gpio_bias_t bias) {
|
||||
return gpio->ops->set_bias(gpio, bias);
|
||||
}
|
||||
|
||||
int gpio_set_drive(gpio_t *gpio, gpio_drive_t drive) {
|
||||
return gpio->ops->set_drive(gpio, drive);
|
||||
}
|
||||
|
||||
int gpio_set_inverted(gpio_t *gpio, bool inverted) {
|
||||
return gpio->ops->set_inverted(gpio, inverted);
|
||||
}
|
||||
|
||||
unsigned int gpio_line(gpio_t *gpio) {
|
||||
return gpio->ops->line(gpio);
|
||||
}
|
||||
@@ -479,6 +513,77 @@ static int gpio_sysfs_get_edge(gpio_t *gpio, gpio_edge_t *edge) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_sysfs_set_bias(gpio_t *gpio, gpio_bias_t bias) {
|
||||
return _gpio_error(gpio, GPIO_ERROR_UNSUPPORTED, 0, "GPIO of type sysfs does not support line bias attribute");
|
||||
}
|
||||
|
||||
static int gpio_sysfs_get_bias(gpio_t *gpio, gpio_bias_t *bias) {
|
||||
return _gpio_error(gpio, GPIO_ERROR_UNSUPPORTED, 0, "GPIO of type sysfs does not support line bias attribute");
|
||||
}
|
||||
|
||||
static int gpio_sysfs_set_drive(gpio_t *gpio, gpio_drive_t drive) {
|
||||
return _gpio_error(gpio, GPIO_ERROR_UNSUPPORTED, 0, "GPIO of type sysfs does not support line drive attribute");
|
||||
}
|
||||
|
||||
static int gpio_sysfs_get_drive(gpio_t *gpio, gpio_drive_t *drive) {
|
||||
return _gpio_error(gpio, GPIO_ERROR_UNSUPPORTED, 0, "GPIO of type sysfs does not support line drive attribute");
|
||||
}
|
||||
|
||||
static int gpio_sysfs_set_inverted(gpio_t *gpio, bool inverted) {
|
||||
char gpio_path[P_PATH_MAX];
|
||||
static const char *inverted_str[2] = {"0\n", "1\n"};
|
||||
int fd;
|
||||
|
||||
/* Write active_low */
|
||||
snprintf(gpio_path, sizeof(gpio_path), "/sys/class/gpio/gpio%u/active_low", gpio->u.sysfs.line);
|
||||
|
||||
if ((fd = open(gpio_path, O_WRONLY)) < 0)
|
||||
return _gpio_error(gpio, GPIO_ERROR_CONFIGURE, errno, "Opening GPIO 'active_low'");
|
||||
|
||||
if (write(fd, inverted_str[inverted], strlen(inverted_str[inverted])) < 0) {
|
||||
int errsv = errno;
|
||||
close(fd);
|
||||
return _gpio_error(gpio, GPIO_ERROR_CONFIGURE, errsv, "Writing GPIO 'active_low'");
|
||||
}
|
||||
|
||||
if (close(fd) < 0)
|
||||
return _gpio_error(gpio, GPIO_ERROR_CONFIGURE, errno, "Closing GPIO 'active_low'");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_sysfs_get_inverted(gpio_t *gpio, bool *inverted) {
|
||||
char gpio_path[P_PATH_MAX];
|
||||
char buf[4];
|
||||
int fd, ret;
|
||||
|
||||
/* Read active_low */
|
||||
snprintf(gpio_path, sizeof(gpio_path), "/sys/class/gpio/gpio%u/active_low", gpio->u.sysfs.line);
|
||||
|
||||
if ((fd = open(gpio_path, O_RDONLY)) < 0)
|
||||
return _gpio_error(gpio, GPIO_ERROR_QUERY, errno, "Opening GPIO 'active_low'");
|
||||
|
||||
if ((ret = read(fd, buf, sizeof(buf))) < 0) {
|
||||
int errsv = errno;
|
||||
close(fd);
|
||||
return _gpio_error(gpio, GPIO_ERROR_QUERY, errsv, "Reading GPIO 'active_low'");
|
||||
}
|
||||
|
||||
if (close(fd) < 0)
|
||||
return _gpio_error(gpio, GPIO_ERROR_QUERY, errno, "Closing GPIO 'active_low'");
|
||||
|
||||
buf[ret] = '\0';
|
||||
|
||||
if (buf[0] == '0')
|
||||
*inverted = false;
|
||||
else if (buf[0] == '1')
|
||||
*inverted = true;
|
||||
else
|
||||
return _gpio_error(gpio, GPIO_ERROR_QUERY, 0, "Unknown GPIO active_low value");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int gpio_sysfs_line(gpio_t *gpio) {
|
||||
return gpio->u.sysfs.line;
|
||||
}
|
||||
@@ -560,6 +665,8 @@ static int gpio_sysfs_tostring(gpio_t *gpio, char *str, size_t len) {
|
||||
const char *direction_str;
|
||||
gpio_edge_t edge;
|
||||
const char *edge_str;
|
||||
bool inverted;
|
||||
const char *inverted_str;
|
||||
char chip_name[32];
|
||||
const char *chip_name_str;
|
||||
char chip_label[32];
|
||||
@@ -579,6 +686,11 @@ static int gpio_sysfs_tostring(gpio_t *gpio, char *str, size_t len) {
|
||||
(edge == GPIO_EDGE_FALLING) ? "falling" :
|
||||
(edge == GPIO_EDGE_BOTH) ? "both" : "unknown";
|
||||
|
||||
if (gpio_sysfs_get_inverted(gpio, &inverted) < 0)
|
||||
inverted_str = "<error>";
|
||||
else
|
||||
inverted_str = inverted ? "true" : "false";
|
||||
|
||||
if (gpio_sysfs_chip_name(gpio, chip_name, sizeof(chip_name)) < 0)
|
||||
chip_name_str = "<error>";
|
||||
else
|
||||
@@ -589,8 +701,8 @@ static int gpio_sysfs_tostring(gpio_t *gpio, char *str, size_t len) {
|
||||
else
|
||||
chip_label_str = chip_label;
|
||||
|
||||
return snprintf(str, len, "GPIO %u (fd=%d, direction=%s, edge=%s, chip_name=\"%s\", chip_label=\"%s\", type=sysfs)",
|
||||
gpio->u.sysfs.line, gpio->u.sysfs.line_fd, direction_str, edge_str, chip_name_str, chip_label_str);
|
||||
return snprintf(str, len, "GPIO %u (fd=%d, direction=%s, edge=%s, inverted=%s, chip_name=\"%s\", chip_label=\"%s\", type=sysfs)",
|
||||
gpio->u.sysfs.line, gpio->u.sysfs.line_fd, direction_str, edge_str, inverted_str, chip_name_str, chip_label_str);
|
||||
}
|
||||
|
||||
static const struct gpio_ops gpio_sysfs_ops = {
|
||||
@@ -601,8 +713,14 @@ static const struct gpio_ops gpio_sysfs_ops = {
|
||||
.close = gpio_sysfs_close,
|
||||
.get_direction = gpio_sysfs_get_direction,
|
||||
.get_edge = gpio_sysfs_get_edge,
|
||||
.get_bias = gpio_sysfs_get_bias,
|
||||
.get_drive = gpio_sysfs_get_drive,
|
||||
.get_inverted = gpio_sysfs_get_inverted,
|
||||
.set_direction = gpio_sysfs_set_direction,
|
||||
.set_edge = gpio_sysfs_set_edge,
|
||||
.set_bias = gpio_sysfs_set_bias,
|
||||
.set_drive = gpio_sysfs_set_drive,
|
||||
.set_inverted = gpio_sysfs_set_inverted,
|
||||
.line = gpio_sysfs_line,
|
||||
.fd = gpio_sysfs_fd,
|
||||
.name = gpio_sysfs_name,
|
||||
@@ -713,12 +831,43 @@ int gpio_open_sysfs(gpio_t *gpio, unsigned int line, gpio_direction_t direction)
|
||||
/* cdev implementation */
|
||||
/*********************************************************************************/
|
||||
|
||||
static int _gpio_cdev_reopen(gpio_t *gpio, gpio_direction_t direction, gpio_edge_t edge) {
|
||||
static const char gpio_label[] = "periphery";
|
||||
static int _gpio_cdev_reopen(gpio_t *gpio, gpio_direction_t direction, gpio_edge_t edge, gpio_bias_t bias, gpio_drive_t drive, bool inverted) {
|
||||
uint32_t flags = 0;
|
||||
|
||||
#ifdef GPIOHANDLE_REQUEST_BIAS_PULL_UP
|
||||
if (bias == GPIO_BIAS_PULL_UP)
|
||||
flags |= GPIOHANDLE_REQUEST_BIAS_PULL_UP;
|
||||
else if (bias == GPIO_BIAS_PULL_DOWN)
|
||||
flags |= GPIOHANDLE_REQUEST_BIAS_PULL_DOWN;
|
||||
else if (bias == GPIO_BIAS_DISABLE)
|
||||
flags |= GPIOHANDLE_REQUEST_BIAS_DISABLE;
|
||||
#else
|
||||
if (bias != GPIO_BIAS_DEFAULT)
|
||||
return _gpio_error(gpio, GPIO_ERROR_UNSUPPORTED, 0, "Kernel version does not support configuring GPIO line bias");
|
||||
#endif
|
||||
|
||||
#ifdef GPIOHANDLE_REQUEST_OPEN_DRAIN
|
||||
if (drive == GPIO_DRIVE_OPEN_DRAIN)
|
||||
flags |= GPIOHANDLE_REQUEST_OPEN_DRAIN;
|
||||
else if (drive == GPIO_DRIVE_OPEN_SOURCE)
|
||||
flags |= GPIOHANDLE_REQUEST_OPEN_SOURCE;
|
||||
#else
|
||||
if (drive != GPIO_DRIVE_DEFAULT)
|
||||
return _gpio_error(gpio, GPIO_ERROR_UNSUPPORTED, 0, "Kernel version does not support configuring GPIO line drive");
|
||||
#endif
|
||||
|
||||
if (inverted)
|
||||
flags |= GPIOHANDLE_REQUEST_ACTIVE_LOW;
|
||||
|
||||
/* FIXME this should really use GPIOHANDLE_SET_CONFIG_IOCTL instead of
|
||||
* closing and reopening, especially to preserve output value on
|
||||
* configuration changes */
|
||||
|
||||
if (gpio->u.cdev.line_fd >= 0) {
|
||||
if (close(gpio->u.cdev.line_fd) < 0)
|
||||
return _gpio_error(gpio, GPIO_ERROR_CLOSE, errno, "Closing GPIO line");
|
||||
|
||||
gpio->u.cdev.line_fd = -1;
|
||||
}
|
||||
|
||||
if (direction == GPIO_DIR_IN) {
|
||||
@@ -726,51 +875,52 @@ static int _gpio_cdev_reopen(gpio_t *gpio, gpio_direction_t direction, gpio_edge
|
||||
struct gpiohandle_request request = {0};
|
||||
|
||||
request.lineoffsets[0] = gpio->u.cdev.line;
|
||||
request.flags = GPIOHANDLE_REQUEST_INPUT;
|
||||
strncpy(request.consumer_label, gpio_label, sizeof(request.consumer_label));
|
||||
request.flags = flags | GPIOHANDLE_REQUEST_INPUT;
|
||||
strncpy(request.consumer_label, gpio->u.cdev.label, sizeof(request.consumer_label));
|
||||
request.lines = 1;
|
||||
|
||||
if (ioctl(gpio->u.cdev.chip_fd, GPIO_GET_LINEHANDLE_IOCTL, &request) < 0)
|
||||
return _gpio_error(gpio, GPIO_ERROR_OPEN, errno, "Opening input line handle");
|
||||
|
||||
gpio->u.cdev.line_fd = request.fd;
|
||||
gpio->u.cdev.direction = GPIO_DIR_IN;
|
||||
gpio->u.cdev.edge = GPIO_EDGE_NONE;
|
||||
} else {
|
||||
struct gpioevent_request request = {0};
|
||||
|
||||
request.lineoffset = gpio->u.cdev.line;
|
||||
request.handleflags = GPIOHANDLE_REQUEST_INPUT;
|
||||
request.handleflags = flags | GPIOHANDLE_REQUEST_INPUT;
|
||||
request.eventflags = (edge == GPIO_EDGE_RISING) ? GPIOEVENT_REQUEST_RISING_EDGE :
|
||||
(edge == GPIO_EDGE_FALLING) ? GPIOEVENT_REQUEST_FALLING_EDGE :
|
||||
GPIOEVENT_REQUEST_BOTH_EDGES;
|
||||
strncpy(request.consumer_label, gpio_label, sizeof(request.consumer_label));
|
||||
strncpy(request.consumer_label, gpio->u.cdev.label, sizeof(request.consumer_label));
|
||||
|
||||
if (ioctl(gpio->u.cdev.chip_fd, GPIO_GET_LINEEVENT_IOCTL, &request) < 0)
|
||||
return _gpio_error(gpio, GPIO_ERROR_OPEN, errno, "Opening input event line handle");
|
||||
|
||||
gpio->u.cdev.line_fd = request.fd;
|
||||
gpio->u.cdev.direction = GPIO_DIR_IN;
|
||||
gpio->u.cdev.edge = edge;
|
||||
}
|
||||
} else {
|
||||
struct gpiohandle_request request = {0};
|
||||
bool initial_value = (direction == GPIO_DIR_OUT_HIGH) ? true : false;
|
||||
initial_value ^= inverted;
|
||||
|
||||
request.lineoffsets[0] = gpio->u.cdev.line;
|
||||
request.flags = GPIOHANDLE_REQUEST_OUTPUT;
|
||||
request.flags = flags | GPIOHANDLE_REQUEST_OUTPUT;
|
||||
request.default_values[0] = initial_value;
|
||||
strncpy(request.consumer_label, gpio_label, sizeof(request.consumer_label));
|
||||
strncpy(request.consumer_label, gpio->u.cdev.label, sizeof(request.consumer_label));
|
||||
request.lines = 1;
|
||||
|
||||
if (ioctl(gpio->u.cdev.chip_fd, GPIO_GET_LINEHANDLE_IOCTL, &request) < 0)
|
||||
return _gpio_error(gpio, GPIO_ERROR_OPEN, errno, "Opening output line handle");
|
||||
|
||||
gpio->u.cdev.line_fd = request.fd;
|
||||
gpio->u.cdev.direction = GPIO_DIR_OUT;
|
||||
gpio->u.cdev.edge = GPIO_EDGE_NONE;
|
||||
}
|
||||
|
||||
gpio->u.cdev.direction = (direction == GPIO_DIR_IN) ? GPIO_DIR_IN : GPIO_DIR_OUT;
|
||||
gpio->u.cdev.edge = edge;
|
||||
gpio->u.cdev.bias = bias;
|
||||
gpio->u.cdev.drive = drive;
|
||||
gpio->u.cdev.inverted = inverted;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -857,6 +1007,21 @@ static int gpio_cdev_get_edge(gpio_t *gpio, gpio_edge_t *edge) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_cdev_get_bias(gpio_t *gpio, gpio_bias_t *bias) {
|
||||
*bias = gpio->u.cdev.bias;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_cdev_get_drive(gpio_t *gpio, gpio_drive_t *drive) {
|
||||
*drive = gpio->u.cdev.drive;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_cdev_get_inverted(gpio_t *gpio, bool *inverted) {
|
||||
*inverted = gpio->u.cdev.inverted;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_cdev_set_direction(gpio_t *gpio, gpio_direction_t direction) {
|
||||
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)");
|
||||
@@ -864,7 +1029,7 @@ static int gpio_cdev_set_direction(gpio_t *gpio, gpio_direction_t direction) {
|
||||
if (gpio->u.cdev.direction == direction)
|
||||
return 0;
|
||||
|
||||
return _gpio_cdev_reopen(gpio, direction, GPIO_EDGE_NONE);
|
||||
return _gpio_cdev_reopen(gpio, direction, GPIO_EDGE_NONE, gpio->u.cdev.bias, gpio->u.cdev.drive, gpio->u.cdev.inverted);
|
||||
}
|
||||
|
||||
static int gpio_cdev_set_edge(gpio_t *gpio, gpio_edge_t edge) {
|
||||
@@ -877,7 +1042,37 @@ static int gpio_cdev_set_edge(gpio_t *gpio, gpio_edge_t edge) {
|
||||
if (gpio->u.cdev.edge == edge)
|
||||
return 0;
|
||||
|
||||
return _gpio_cdev_reopen(gpio, GPIO_DIR_IN, edge);
|
||||
return _gpio_cdev_reopen(gpio, gpio->u.cdev.direction, edge, gpio->u.cdev.bias, gpio->u.cdev.drive, gpio->u.cdev.inverted);
|
||||
}
|
||||
|
||||
static int gpio_cdev_set_bias(gpio_t *gpio, gpio_bias_t bias) {
|
||||
if (bias != GPIO_BIAS_DEFAULT && bias != GPIO_BIAS_PULL_UP && bias != GPIO_BIAS_PULL_DOWN && 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 (gpio->u.cdev.bias == bias)
|
||||
return 0;
|
||||
|
||||
return _gpio_cdev_reopen(gpio, gpio->u.cdev.direction, gpio->u.cdev.edge, bias, gpio->u.cdev.drive, gpio->u.cdev.inverted);
|
||||
}
|
||||
|
||||
static int gpio_cdev_set_drive(gpio_t *gpio, gpio_drive_t drive) {
|
||||
if (drive != GPIO_DRIVE_DEFAULT && drive != GPIO_DRIVE_OPEN_DRAIN && 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 (gpio->u.cdev.direction != GPIO_DIR_OUT && drive != GPIO_DRIVE_DEFAULT)
|
||||
return _gpio_error(gpio, GPIO_ERROR_INVALID_OPERATION, 0, "Invalid operation: cannot set line drive on input GPIO");
|
||||
|
||||
if (gpio->u.cdev.drive == drive)
|
||||
return 0;
|
||||
|
||||
return _gpio_cdev_reopen(gpio, gpio->u.cdev.direction, gpio->u.cdev.edge, gpio->u.cdev.bias, drive, gpio->u.cdev.inverted);
|
||||
}
|
||||
|
||||
static int gpio_cdev_set_inverted(gpio_t *gpio, bool inverted) {
|
||||
if (gpio->u.cdev.inverted == inverted)
|
||||
return 0;
|
||||
|
||||
return _gpio_cdev_reopen(gpio, gpio->u.cdev.direction, gpio->u.cdev.edge, gpio->u.cdev.bias, gpio->u.cdev.drive, inverted);
|
||||
}
|
||||
|
||||
static unsigned int gpio_cdev_line(gpio_t *gpio) {
|
||||
@@ -949,6 +1144,12 @@ static int gpio_cdev_tostring(gpio_t *gpio, char *str, size_t len) {
|
||||
const char *direction_str;
|
||||
gpio_edge_t edge;
|
||||
const char *edge_str;
|
||||
gpio_bias_t bias;
|
||||
const char *bias_str;
|
||||
gpio_drive_t drive;
|
||||
const char *drive_str;
|
||||
bool inverted;
|
||||
const char *inverted_str;
|
||||
char line_name[32];
|
||||
const char *line_name_str;
|
||||
char line_label[32];
|
||||
@@ -972,6 +1173,26 @@ static int gpio_cdev_tostring(gpio_t *gpio, char *str, size_t len) {
|
||||
(edge == GPIO_EDGE_FALLING) ? "falling" :
|
||||
(edge == GPIO_EDGE_BOTH) ? "both" : "unknown";
|
||||
|
||||
if (gpio_cdev_get_bias(gpio, &bias) < 0)
|
||||
bias_str = "<error>";
|
||||
else
|
||||
bias_str = (bias == GPIO_BIAS_DEFAULT) ? "default" :
|
||||
(bias == GPIO_BIAS_PULL_UP) ? "pull_up" :
|
||||
(bias == GPIO_BIAS_PULL_DOWN) ? "pull_down" :
|
||||
(bias == GPIO_BIAS_DISABLE) ? "disable" : "unknown";
|
||||
|
||||
if (gpio_cdev_get_drive(gpio, &drive) < 0)
|
||||
drive_str = "<error>";
|
||||
else
|
||||
drive_str = (drive == GPIO_DRIVE_DEFAULT) ? "default" :
|
||||
(drive == GPIO_DRIVE_OPEN_DRAIN) ? "open_drain" :
|
||||
(drive == GPIO_DRIVE_OPEN_SOURCE) ? "open_source" : "unknown";
|
||||
|
||||
if (gpio_cdev_get_inverted(gpio, &inverted) < 0)
|
||||
inverted_str = "<error>";
|
||||
else
|
||||
inverted_str = inverted ? "true" : "false";
|
||||
|
||||
if (gpio_cdev_name(gpio, line_name, sizeof(line_name)) < 0)
|
||||
line_name_str = "<error>";
|
||||
else
|
||||
@@ -992,8 +1213,8 @@ static int gpio_cdev_tostring(gpio_t *gpio, char *str, size_t len) {
|
||||
else
|
||||
chip_label_str = chip_label;
|
||||
|
||||
return snprintf(str, len, "GPIO %u (name=\"%s\", label=\"%s\", line_fd=%d, chip_fd=%d, direction=%s, edge=%s, chip_name=\"%s\", chip_label=\"%s\", type=cdev)",
|
||||
gpio->u.cdev.line, line_name_str, line_label_str, gpio->u.cdev.line_fd, gpio->u.cdev.chip_fd, direction_str, edge_str, chip_name_str, chip_label_str);
|
||||
return snprintf(str, len, "GPIO %u (name=\"%s\", label=\"%s\", line_fd=%d, chip_fd=%d, direction=%s, edge=%s, bias=%s, drive=%s, inverted=%s, chip_name=\"%s\", chip_label=\"%s\", type=cdev)",
|
||||
gpio->u.cdev.line, line_name_str, line_label_str, gpio->u.cdev.line_fd, gpio->u.cdev.chip_fd, direction_str, edge_str, bias_str, drive_str, inverted_str, chip_name_str, chip_label_str);
|
||||
}
|
||||
|
||||
static const struct gpio_ops gpio_cdev_ops = {
|
||||
@@ -1004,8 +1225,14 @@ static const struct gpio_ops gpio_cdev_ops = {
|
||||
.close = gpio_cdev_close,
|
||||
.get_direction = gpio_cdev_get_direction,
|
||||
.get_edge = gpio_cdev_get_edge,
|
||||
.get_bias = gpio_cdev_get_bias,
|
||||
.get_drive = gpio_cdev_get_drive,
|
||||
.get_inverted = gpio_cdev_get_inverted,
|
||||
.set_direction = gpio_cdev_set_direction,
|
||||
.set_edge = gpio_cdev_set_edge,
|
||||
.set_bias = gpio_cdev_set_bias,
|
||||
.set_drive = gpio_cdev_set_drive,
|
||||
.set_inverted = gpio_cdev_set_inverted,
|
||||
.line = gpio_cdev_line,
|
||||
.fd = gpio_cdev_fd,
|
||||
.name = gpio_cdev_name,
|
||||
@@ -1031,9 +1258,10 @@ 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));
|
||||
|
||||
/* Open GPIO line */
|
||||
ret = _gpio_cdev_reopen(gpio, direction, GPIO_EDGE_NONE);
|
||||
ret = _gpio_cdev_reopen(gpio, direction, GPIO_EDGE_NONE, GPIO_BIAS_DEFAULT, GPIO_DRIVE_DEFAULT, false);
|
||||
if (ret < 0) {
|
||||
close(fd);
|
||||
return ret;
|
||||
@@ -1089,9 +1317,10 @@ int gpio_open_name(gpio_t *gpio, const char *path, const char *name, gpio_direct
|
||||
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));
|
||||
|
||||
/* Open GPIO line */
|
||||
ret = _gpio_cdev_reopen(gpio, direction, GPIO_EDGE_NONE);
|
||||
ret = _gpio_cdev_reopen(gpio, direction, GPIO_EDGE_NONE, GPIO_BIAS_DEFAULT, GPIO_DRIVE_DEFAULT, false);
|
||||
if (ret < 0) {
|
||||
close(fd);
|
||||
return ret;
|
||||
|
||||
+19
@@ -41,6 +41,19 @@ typedef enum gpio_edge {
|
||||
GPIO_EDGE_BOTH /* Both edges X -> !X */
|
||||
} gpio_edge_t;
|
||||
|
||||
typedef enum gpio_bias {
|
||||
GPIO_BIAS_DEFAULT, /* Default line bias */
|
||||
GPIO_BIAS_PULL_UP, /* Pull-up */
|
||||
GPIO_BIAS_PULL_DOWN, /* Pull-down */
|
||||
GPIO_BIAS_DISABLE, /* Disable line bias */
|
||||
} gpio_bias_t;
|
||||
|
||||
typedef enum gpio_drive {
|
||||
GPIO_DRIVE_DEFAULT, /* Default line drive (push-pull) */
|
||||
GPIO_DRIVE_OPEN_DRAIN, /* Open drain */
|
||||
GPIO_DRIVE_OPEN_SOURCE, /* Open source */
|
||||
} gpio_drive_t;
|
||||
|
||||
typedef struct gpio_handle gpio_t;
|
||||
|
||||
/* Primary Functions */
|
||||
@@ -63,10 +76,16 @@ int gpio_poll_multiple(gpio_t **gpios, size_t count, int timeout_ms, bool *gpios
|
||||
/* Getters */
|
||||
int gpio_get_direction(gpio_t *gpio, gpio_direction_t *direction);
|
||||
int gpio_get_edge(gpio_t *gpio, gpio_edge_t *edge);
|
||||
int gpio_get_bias(gpio_t *gpio, gpio_bias_t *bias);
|
||||
int gpio_get_drive(gpio_t *gpio, gpio_drive_t *drive);
|
||||
int gpio_get_inverted(gpio_t *gpio, bool *inverted);
|
||||
|
||||
/* Setters */
|
||||
int gpio_set_direction(gpio_t *gpio, gpio_direction_t direction);
|
||||
int gpio_set_edge(gpio_t *gpio, gpio_edge_t edge);
|
||||
int gpio_set_bias(gpio_t *gpio, gpio_bias_t edge);
|
||||
int gpio_set_drive(gpio_t *gpio, gpio_drive_t drive);
|
||||
int gpio_set_inverted(gpio_t *gpio, bool inverted);
|
||||
|
||||
/* Miscellaneous Properties */
|
||||
unsigned int gpio_line(gpio_t *gpio);
|
||||
|
||||
@@ -42,6 +42,9 @@ void test_open_config_close(void) {
|
||||
gpio_direction_t direction;
|
||||
gpio_edge_t edge;
|
||||
char label[32];
|
||||
gpio_bias_t bias;
|
||||
gpio_drive_t drive;
|
||||
bool inverted;
|
||||
|
||||
ptest();
|
||||
|
||||
@@ -69,6 +72,10 @@ void test_open_config_close(void) {
|
||||
passert(gpio_set_direction(gpio, 5) == GPIO_ERROR_ARG);
|
||||
/* Invalid interrupt edge */
|
||||
passert(gpio_set_edge(gpio, 5) == GPIO_ERROR_ARG);
|
||||
/* Invalid bias */
|
||||
passert(gpio_set_bias(gpio, 5) == GPIO_ERROR_ARG);
|
||||
/* Invalid drive */
|
||||
passert(gpio_set_drive(gpio, 5) == GPIO_ERROR_ARG);
|
||||
|
||||
/* Set direction out, check direction out, check value low */
|
||||
passert(gpio_set_direction(gpio, GPIO_DIR_OUT) == 0);
|
||||
@@ -88,6 +95,29 @@ void test_open_config_close(void) {
|
||||
passert(direction == GPIO_DIR_OUT);
|
||||
passert(gpio_read(gpio, &value) == 0);
|
||||
passert(value == true);
|
||||
|
||||
/* Set drive open drain, check drive open drain */
|
||||
passert(gpio_set_drive(gpio, GPIO_DRIVE_OPEN_DRAIN) == 0);
|
||||
passert(gpio_get_drive(gpio, &drive) == 0);
|
||||
passert(drive == GPIO_DRIVE_OPEN_DRAIN);
|
||||
/* Set drive open source, check drive open source */
|
||||
passert(gpio_set_drive(gpio, GPIO_DRIVE_OPEN_SOURCE) == 0);
|
||||
passert(gpio_get_drive(gpio, &drive) == 0);
|
||||
passert(drive == GPIO_DRIVE_OPEN_SOURCE);
|
||||
/* Set drive default, check drive default */
|
||||
passert(gpio_set_drive(gpio, GPIO_DRIVE_DEFAULT) == 0);
|
||||
passert(gpio_get_drive(gpio, &drive) == 0);
|
||||
passert(drive == GPIO_DRIVE_DEFAULT);
|
||||
|
||||
/* Set inverted true, check inverted true */
|
||||
passert(gpio_set_inverted(gpio, true) == 0);
|
||||
passert(gpio_get_inverted(gpio, &inverted) == 0);
|
||||
passert(inverted == true);
|
||||
/* Set inverted false, check inverted false */
|
||||
passert(gpio_set_inverted(gpio, false) == 0);
|
||||
passert(gpio_get_inverted(gpio, &inverted) == 0);
|
||||
passert(inverted == false);
|
||||
|
||||
/* Attempt to set interrupt edge on output GPIO */
|
||||
passert(gpio_set_edge(gpio, GPIO_EDGE_RISING) == GPIO_ERROR_INVALID_OPERATION);
|
||||
/* Attempt to read event on output GPIO */
|
||||
@@ -120,6 +150,26 @@ void test_open_config_close(void) {
|
||||
passert(gpio_get_edge(gpio, &edge) == 0);
|
||||
passert(edge == GPIO_EDGE_NONE);
|
||||
|
||||
/* Set bias pull up, check bias pull up */
|
||||
passert(gpio_set_bias(gpio, GPIO_BIAS_PULL_UP) == 0);
|
||||
passert(gpio_get_bias(gpio, &bias) == 0);
|
||||
passert(bias == GPIO_BIAS_PULL_UP);
|
||||
/* Set bias pull down, check bias pull down */
|
||||
passert(gpio_set_bias(gpio, GPIO_BIAS_PULL_DOWN) == 0);
|
||||
passert(gpio_get_bias(gpio, &bias) == 0);
|
||||
passert(bias == GPIO_BIAS_PULL_DOWN);
|
||||
/* Set bias disable, check bias disable */
|
||||
passert(gpio_set_bias(gpio, GPIO_BIAS_DISABLE) == 0);
|
||||
passert(gpio_get_bias(gpio, &bias) == 0);
|
||||
passert(bias == GPIO_BIAS_DISABLE);
|
||||
/* Set bias default, check bias default */
|
||||
passert(gpio_set_bias(gpio, GPIO_BIAS_DEFAULT) == 0);
|
||||
passert(gpio_get_bias(gpio, &bias) == 0);
|
||||
passert(bias == GPIO_BIAS_DEFAULT);
|
||||
|
||||
/* Attempt to set drive on input GPIO */
|
||||
passert(gpio_set_drive(gpio, GPIO_DRIVE_OPEN_DRAIN) == GPIO_ERROR_INVALID_OPERATION);
|
||||
|
||||
/* Close GPIO */
|
||||
passert(gpio_close(gpio) == 0);
|
||||
|
||||
@@ -265,6 +315,25 @@ void test_loopback(void) {
|
||||
passert(gpio_close(gpio_in) == 0);
|
||||
passert(gpio_close(gpio_out) == 0);
|
||||
|
||||
/* Open both GPIOs as inputs */
|
||||
passert(gpio_open(gpio_in, device, pin_input, GPIO_DIR_IN) == 0);
|
||||
passert(gpio_open(gpio_out, device, pin_output, GPIO_DIR_IN) == 0);
|
||||
|
||||
/* Set bias pull-up, check value is high */
|
||||
passert(gpio_set_bias(gpio_in, GPIO_BIAS_PULL_UP) == 0);
|
||||
usleep(1000);
|
||||
passert(gpio_read(gpio_in, &value) == 0);
|
||||
passert(value == true);
|
||||
|
||||
/* Set bias pull-down, check value is low */
|
||||
passert(gpio_set_bias(gpio_in, GPIO_BIAS_PULL_DOWN) == 0);
|
||||
usleep(1000);
|
||||
passert(gpio_read(gpio_in, &value) == 0);
|
||||
passert(value == false);
|
||||
|
||||
passert(gpio_close(gpio_in) == 0);
|
||||
passert(gpio_close(gpio_out) == 0);
|
||||
|
||||
/* Free GPIO */
|
||||
gpio_free(gpio_in);
|
||||
gpio_free(gpio_out);
|
||||
|
||||
@@ -39,6 +39,9 @@ void test_open_config_close(void) {
|
||||
bool value;
|
||||
gpio_direction_t direction;
|
||||
gpio_edge_t edge;
|
||||
gpio_bias_t bias;
|
||||
gpio_drive_t drive;
|
||||
bool inverted;
|
||||
|
||||
ptest();
|
||||
|
||||
@@ -61,6 +64,12 @@ void test_open_config_close(void) {
|
||||
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 */
|
||||
@@ -85,6 +94,15 @@ void test_open_config_close(void) {
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user