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.
KED/peripherals/gpio/gpio.c

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();
}
}