|
|
/**
|
|
|
* @file gpio_linux.c
|
|
|
* @brief GPIO implementation for Linux using libgpiod and internal context management.
|
|
|
*/
|
|
|
|
|
|
#include "../../ked/peripherals/gpio/gpio.h"
|
|
|
#include <gpiod.h>
|
|
|
#include <stdlib.h>
|
|
|
#include <stdio.h>
|
|
|
|
|
|
/**
|
|
|
* @brief Internal Linux GPIO context.
|
|
|
*
|
|
|
* Holds the active chip and line handles.
|
|
|
* This implementation assumes one active GPIO at a time (can be extended to multiple if needed).
|
|
|
*/
|
|
|
typedef struct {
|
|
|
struct gpiod_chip* chip;
|
|
|
struct gpiod_line* line;
|
|
|
} gpio_linux_ctx_t;
|
|
|
|
|
|
static gpio_linux_ctx_t gpio_ctx;
|
|
|
|
|
|
/* === Centralized Error Handling === */
|
|
|
|
|
|
/**
|
|
|
* @brief Centralized error handler for GPIO-related failures.
|
|
|
*
|
|
|
* Logs the error to stderr and can be extended to trigger hooks or diagnostics.
|
|
|
*
|
|
|
* @param err The GPIO error code encountered.
|
|
|
*/
|
|
|
void ked_gpio_handle_error(gpio_error_t err) {
|
|
|
fprintf(stderr, "[GPIO ERROR] Code: %d\n", err);
|
|
|
// Future: report to diagnostic logger or panic handler
|
|
|
}
|
|
|
|
|
|
/* === GPIO Operation Implementations === */
|
|
|
|
|
|
/**
|
|
|
* @brief Set the output level of the GPIO line.
|
|
|
*
|
|
|
* @param value 1 = HIGH, 0 = LOW
|
|
|
*/
|
|
|
void ked_gpio_set(gpio_t* gpio, uint8_t value) {
|
|
|
if (gpio_ctx.line) {
|
|
|
gpiod_line_set_value(gpio_ctx.line, value);
|
|
|
} else {
|
|
|
ked_gpio_handle_error(T_GPIO_ERR_NOT_AVAILABLE);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief Read the current logic level of the GPIO line.
|
|
|
*
|
|
|
* @return Logic level (1 = HIGH, 0 = LOW)
|
|
|
*/
|
|
|
uint8_t ked_gpio_read(gpio_t* gpio) {
|
|
|
if (!gpio_ctx.line) {
|
|
|
ked_gpio_handle_error(T_GPIO_ERR_NOT_AVAILABLE);
|
|
|
return 0;
|
|
|
}
|
|
|
return (uint8_t)gpiod_line_get_value(gpio_ctx.line);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief Toggle the current logic level of the GPIO line.
|
|
|
*/
|
|
|
void ked_gpio_toggle(gpio_t* gpio) {
|
|
|
if (!gpio_ctx.line) {
|
|
|
ked_gpio_handle_error(T_GPIO_ERR_NOT_AVAILABLE);
|
|
|
return;
|
|
|
}
|
|
|
uint8_t current = (uint8_t)gpiod_line_get_value(gpio_ctx.line);
|
|
|
gpiod_line_set_value(gpio_ctx.line, !current);
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* @brief Update GPIO configuration dynamically.
|
|
|
*
|
|
|
* Currently not supported on Linux – placeholder only.
|
|
|
*/
|
|
|
gpio_error_t ked_gpio_update(gpio_t* gpio) {
|
|
|
// TODO: implement runtime reconfiguration if needed
|
|
|
return T_GPIO_ERR_UNSUPPORTED_MODE;
|
|
|
}
|
|
|
|
|
|
/* === Public API Implementation === */
|
|
|
|
|
|
/**
|
|
|
* @brief Initialize the GPIO using the given Linux gpio_t config and callback struct.
|
|
|
*
|
|
|
* This sets direction, bias, and binds callbacks to the gpio_fn_ptr_t table.
|
|
|
*
|
|
|
* @param gpio Pointer to GPIO configuration structure
|
|
|
* @param callbacks Optional pointer to gpio_fn_ptr_t to receive platform-specific handlers
|
|
|
* @return T_GPIO_ERR_OK on success, or appropriate error code
|
|
|
*/
|
|
|
gpio_error_t ked_gpio_init(const gpio_t* gpio) {
|
|
|
gpio_ctx.chip = gpiod_chip_open(gpio->chipname);
|
|
|
if (!gpio_ctx.chip) {
|
|
|
ked_gpio_handle_error(T_GPIO_ERR_NOT_AVAILABLE);
|
|
|
return T_GPIO_ERR_NOT_AVAILABLE;
|
|
|
}
|
|
|
|
|
|
gpio_ctx.line = gpiod_chip_get_line(gpio_ctx.chip, gpio->line_offset);
|
|
|
if (!gpio_ctx.line) {
|
|
|
gpiod_chip_close(gpio_ctx.chip);
|
|
|
ked_gpio_handle_error(T_GPIO_ERR_NOT_AVAILABLE);
|
|
|
return T_GPIO_ERR_NOT_AVAILABLE;
|
|
|
}
|
|
|
|
|
|
struct gpiod_line_request_config req_cfg = {
|
|
|
.consumer = "ked_gpio",
|
|
|
.request_type = (gpio->direction == T_GPIO_DIRECTION_OUTPUT)
|
|
|
? GPIOD_LINE_REQUEST_DIRECTION_OUTPUT
|
|
|
: GPIOD_LINE_REQUEST_DIRECTION_INPUT,
|
|
|
.flags = 0
|
|
|
};
|
|
|
|
|
|
if (gpio->bias == T_GPIO_BIAS_PULL_UP)
|
|
|
req_cfg.flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP;
|
|
|
else if (gpio->bias == T_GPIO_BIAS_PULL_DOWN)
|
|
|
req_cfg.flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN;
|
|
|
else if (gpio->bias == T_GPIO_BIAS_DISABLE)
|
|
|
req_cfg.flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE;
|
|
|
else if (gpio->bias == T_GPIO_BIAS_DEFAULT)
|
|
|
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);
|
|
|
ked_gpio_handle_error(T_GPIO_ERR_BUS_BUSY);
|
|
|
return T_GPIO_ERR_BUS_BUSY;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief Deinitialize and release resources for the GPIO.
|
|
|
*/
|
|
|
void ked_gpio_deinit(gpio_t* gpio) {
|
|
|
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;
|
|
|
}
|
|
|
}
|