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

152 lines
4.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* @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;
}
}