You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
3.9 KiB
140 lines
3.9 KiB
/**
|
|
* @file gpio_linux.c
|
|
* @brief GPIO implementation for Linux using libgpiod and KED callback injection.
|
|
*/
|
|
|
|
#include "../../ked/peripherals/gpio/gpio.h"
|
|
#include <gpiod.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
/**
|
|
* @brief Internal Linux GPIO context.
|
|
*
|
|
* Holds pointers to the open GPIO chip and the requested line.
|
|
*/
|
|
typedef struct {
|
|
struct gpiod_chip* chip; /**< File descriptor to the opened GPIO chip */
|
|
struct gpiod_line* line; /**< Reference to the requested GPIO line */
|
|
} gpio_linux_ctx_t;
|
|
|
|
static gpio_linux_ctx_t gpio_ctx;
|
|
|
|
/**
|
|
* @brief Set the output level of the GPIO line.
|
|
*
|
|
* @param value Logic level to set (true for HIGH, false for LOW).
|
|
*/
|
|
static void gpio_linux_set(bool value) {
|
|
gpiod_line_set_value(gpio_ctx.line, value);
|
|
}
|
|
|
|
/**
|
|
* @brief Read the current logic level of the GPIO line.
|
|
*
|
|
* @return Logic level (true for HIGH, false for LOW).
|
|
*/
|
|
static bool gpio_linux_read(void) {
|
|
return gpiod_line_get_value(gpio_ctx.line);
|
|
}
|
|
|
|
/**
|
|
* @brief Toggle the current state of the GPIO output.
|
|
*/
|
|
static void gpio_linux_toggle(void) {
|
|
int current = gpiod_line_get_value(gpio_ctx.line);
|
|
gpiod_line_set_value(gpio_ctx.line, !current);
|
|
}
|
|
|
|
/**
|
|
* @brief Release the GPIO line and chip.
|
|
*
|
|
* Called during deinitialization to free resources.
|
|
*/
|
|
static void gpio_linux_deinit(void) {
|
|
if (gpio_ctx.line) {
|
|
gpiod_line_release(gpio_ctx.line);
|
|
gpio_ctx.line = NULL;
|
|
}
|
|
if (gpio_ctx.chip) {
|
|
gpiod_chip_close(gpio_ctx.chip);
|
|
gpio_ctx.chip = NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Default error handling function.
|
|
*
|
|
* @param err The GPIO error code to report.
|
|
*/
|
|
static void gpio_linux_handle_error(gpio_error_t err) {
|
|
fprintf(stderr, "[GPIO ERROR] Code: %d\n", err);
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize a GPIO using a Linux-specific gpio_config.
|
|
*
|
|
* Sets the direction and bias of the line according to the config.
|
|
*
|
|
* @param gpio Pointer to a gpio_t handle to be configured.
|
|
* @param config Pointer to a Linux-specific gpio_config structure.
|
|
* @return GPIO_OK on success or an appropriate gpio_error_t on failure.
|
|
*/
|
|
gpio_error_t ked_gpio_init(gpio_t* gpio, const gpio_config* config) {
|
|
gpio_ctx.chip = gpiod_chip_open(config->chipname);
|
|
if (!gpio_ctx.chip) {
|
|
gpio->on_error(GPIO_ERR_NOT_AVAILABLE);
|
|
return GPIO_ERR_NOT_AVAILABLE;
|
|
}
|
|
|
|
gpio_ctx.line = gpiod_chip_get_line(gpio_ctx.chip, config->line_offset);
|
|
if (!gpio_ctx.line) {
|
|
gpiod_chip_close(gpio_ctx.chip);
|
|
gpio->on_error(GPIO_ERR_NOT_AVAILABLE);
|
|
return GPIO_ERR_NOT_AVAILABLE;
|
|
}
|
|
|
|
struct gpiod_line_request_config req_cfg = {
|
|
.consumer = "ked",
|
|
.request_type = (config->direction == GPIO_DIRECTION_OUTPUT)
|
|
? GPIOD_LINE_REQUEST_DIRECTION_OUTPUT
|
|
: GPIOD_LINE_REQUEST_DIRECTION_INPUT,
|
|
.flags = 0
|
|
};
|
|
|
|
if (config->bias == GPIO_BIAS_PULL_UP) {
|
|
req_cfg.flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP;
|
|
} else if (config->bias == GPIO_BIAS_PULL_DOWN) {
|
|
req_cfg.flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN;
|
|
} else if (config->bias == GPIO_BIAS_DISABLE) {
|
|
req_cfg.flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE;
|
|
} else if (config->bias == GPIO_BIAS_NONE) {
|
|
req_cfg.request_type = GPIOD_LINE_REQUEST_DIRECTION_AS_IS;
|
|
}
|
|
|
|
if (gpiod_line_request(gpio_ctx.line, &req_cfg, 0) < 0) {
|
|
gpiod_chip_close(gpio_ctx.chip);
|
|
gpio->on_error(GPIO_ERR_LINE_BUSY);
|
|
return GPIO_ERR_LINE_BUSY;
|
|
}
|
|
|
|
gpio->set = gpio_linux_set;
|
|
gpio->read = gpio_linux_read;
|
|
gpio->toggle = gpio_linux_toggle;
|
|
gpio->deinit = gpio_linux_deinit;
|
|
gpio->on_error = gpio_linux_handle_error;
|
|
|
|
return GPIO_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Generic GPIO deinitialization entry point.
|
|
*
|
|
* @param gpio Pointer to the gpio_t structure with deinit function populated.
|
|
*/
|
|
void ked_gpio_deinit(gpio_t* gpio) {
|
|
if (gpio && gpio->deinit) {
|
|
gpio->deinit();
|
|
}
|
|
}
|