From 78abf2697946928d6db6b8c373050ea4763c9d67 Mon Sep 17 00:00:00 2001 From: kerem Date: Fri, 25 Apr 2025 16:49:15 +0200 Subject: [PATCH] Modified to be as identical to the linux_test_nopointer in order to have a good benchmark nedd to comparre size wehn addin more than one pin --- examples/pin_control.c | 89 ++++++++------- ked/peripherals/gpio/gpio.h | 214 ++++++++++++++++-------------------- peripherals/gpio/gpio.c | 150 ++++++++++++++----------- 3 files changed, 235 insertions(+), 218 deletions(-) diff --git a/examples/pin_control.c b/examples/pin_control.c index 2f6541f..813f00f 100644 --- a/examples/pin_control.c +++ b/examples/pin_control.c @@ -1,6 +1,6 @@ /** * @file gpio_control.c - * @brief CLI utility to control GPIOs using KED GPIO interface (Linux). + * @brief CLI utility to control GPIOs using the KED GPIO interface (Linux). */ #include "../ked/peripherals/gpio/gpio.h" @@ -8,64 +8,79 @@ #include #include +#define GPIOCHIP_DEFAULT "/dev/gpiochip0" + +/** + * @brief Print usage message for the CLI tool. + */ +void print_usage(const char* prog_name) { + printf("Usage: %s -pin -set|-reset|-toggle|-read\n", prog_name); +} + /** - * @brief Simple command-line tool to test the GPIO API. + * @brief Entry point for GPIO control CLI. */ int main(int argc, char* argv[]) { if (argc < 4) { - printf("Usage: %s -pin -[set|reset|toggle|read]\n", argv[0]); + print_usage(argv[0]); return 1; } - unsigned int pin = 0; - const char* action = NULL; + uint32_t pin = 0; + uint8_t action = 0xFF; // 0=set, 1=reset, 2=toggle, 3=read - for (int i = 1; i < argc; i++) { + for (int i = 1; i < argc; ++i) { if (strcmp(argv[i], "-pin") == 0 && i + 1 < argc) { - pin = (unsigned int)atoi(argv[++i]); - } else if (strncmp(argv[i], "-", 1) == 0) { - action = argv[i] + 1; // skip '-' prefix + pin = atoi(argv[++i]); + } else if (strcmp(argv[i], "-set") == 0) { + action = 0; + } else if (strcmp(argv[i], "-reset") == 0) { + action = 1; + } else if (strcmp(argv[i], "-toggle") == 0) { + action = 2; + } else if (strcmp(argv[i], "-read") == 0) { + action = 3; } } - if (pin == 0 || !action) { - fprintf(stderr, "Invalid usage.\n"); - return 1; + if (pin == 0 || action == 0xFF) { + print_usage(argv[0]); + return 2; } - gpio_t gpio; - gpio_config cfg = { + gpio_t gpio_conf = { .chipname = "/dev/gpiochip0", .line_offset = pin, - .mode = GPIO_MODE_DEFAULT, - .direction = (strcmp(action, "read") == 0) ? GPIO_DIRECTION_INPUT : GPIO_DIRECTION_OUTPUT, - .bias = GPIO_BIAS_NONE + .direction = (action == 3) ? T_GPIO_DIRECTION_INPUT : T_GPIO_DIRECTION_OUTPUT, + .bias = T_GPIO_BIAS_DEFAULT, + .mode = T_GPIO_MODE_DEFAULT, + .status = T_GPIO_STATUS_NOT_INIT }; - gpio_error_t err = ked_gpio_init(&gpio, &cfg); - if (err != GPIO_OK) { - if (gpio.on_error) gpio.on_error(err); + + gpio_fn_ptr_t led = {0}; + + gpio_error_t err = ked_gpio_init(&gpio_conf, &led); + if (err != T_GPIO_ERR_OK) { + if (led.on_error) led.on_error(err); return 1; } - if (strcmp(action, "set") == 0) { - gpio.set(true); - //printf("GPIO%u set HIGH\n", pin); - } else if (strcmp(action, "reset") == 0) { - gpio.set(false); - //printf("GPIO%u set LOW\n", pin); - } else if (strcmp(action, "toggle") == 0) { - gpio.toggle(); - //printf("GPIO%u toggled\n", pin); - } else if (strcmp(action, "read") == 0) { - bool value = gpio.read(); - printf("%s\n", value ? "1" : "0"); - } else { - fprintf(stderr, "Unknown action: %s\n", action); - if (gpio.on_error) gpio.on_error(GPIO_ERR_UNKNOWN); - return 1; + switch (action) { + case 0: + if (led.set) led.set(1); + break; + case 1: + if (led.set) led.set(0); + break; + case 2: + if (led.toggle) led.toggle(); + break; + case 3: + printf("GPIO%u value: %d\n", pin, led.read()); + break; } - gpio.deinit(); + // Clean up return 0; } diff --git a/ked/peripherals/gpio/gpio.h b/ked/peripherals/gpio/gpio.h index 7aaddf5..c84bc56 100644 --- a/ked/peripherals/gpio/gpio.h +++ b/ked/peripherals/gpio/gpio.h @@ -1,6 +1,6 @@ /** ************************************************************************************************** - * @file pin.h + * @file gpio.h * @author Kerem Yollu & Edwin Koch * @date 24.04.2025 * @version 0.0.1 @@ -12,166 +12,144 @@ * that allows unified control of digital I/O pins across different platforms, * such as microcontrollers or embedded Linux systems (e.g., Raspberry Pi). * - * The interface provides a driver table through which initialization, - * read/write, and toggle operations can be performed in a platform-agnostic way. - * Platform-specific implementations must define and populate the `KED_GPIO` - * driver table with appropriate function pointers. - * - * Usage Example: - * @code - * gpio_t* pin = KED_GPIO.init(&config); - * KED_GPIO.set(pin, true); - * bool state = KED_GPIO.read(pin); - * KED_GPIO.toggle(pin); - * @endcode - * - * @todo - * - 24.04.2025: Implement more function pointer to enhance available gpio operations - * - 24.04.2025: Implement Defined States, Errors, Configuration Options + * The implementation uses a centralized internal function pointer table to map + * platform-specific logic (e.g., Linux or STM32) to a unified interface. + * Application code can use `ked_gpio_set()`, `ked_gpio_read()` etc. without dealing with callbacks. ************************************************************************************************** + */ -#ifndef KED_PERIPHERALS_GPIO_GPIO_H_ -#define KED_PERIPHERALS_GPIO_GPIO_H_ -#include +#ifndef KED_PERIPHERALS_GPIO_H_ +#define KED_PERIPHERALS_GPIO_H_ -/** - * @brief Enumeration for GPIO functional modes. - */ -typedef enum { - GPIO_MODE_DEFAULT, - GPIO_MODE_INPUT, - GPIO_MODE_OUTPUT, - GPIO_MODE_ANALOG, - GPIO_MODE_ALTERNATE -} gpio_mode_t; +#include -/** - * @brief Enumeration for GPIO direction settings. - */ +#ifdef __cplusplus +extern "C" { +#endif + +/** GPIO Mode Types */ typedef enum { - GPIO_DIRECTION_INPUT, - GPIO_DIRECTION_OUTPUT + T_GPIO_MODE_DEFAULT, + T_GPIO_MODE_INPUT, + T_GPIO_MODE_OUTPUT, + T_GPIO_MODE_ANALOG, + T_GPIO_MODE_ALTERNATE +} gpio_function_t; + +/** GPIO Direction (explicit) */ +typedef enum { + T_GPIO_DIRECTION_INPUT, + T_GPIO_DIRECTION_OUTPUT } gpio_direction_t; -/** - * @brief Enumeration for GPIO drive configurations. - */ +/** Drive types for output pins */ typedef enum { - GPIO_DRIVE_DEFAULT, - GPIO_DRIVE_PUSH_PULL, - GPIO_DRIVE_OPEN_DRAIN + T_GPIO_DRIVE_DEFAULT, + T_GPIO_DRIVE_PUSH_PULL, + T_GPIO_DRIVE_OPEN_DRAIN } gpio_drive_t; -/** - * @brief Enumeration for internal bias resistor settings. - */ +/** Pull-up/pull-down configurations */ typedef enum { - GPIO_BIAS_NONE, - GPIO_BIAS_DISABLE, - GPIO_BIAS_PULL_UP, - GPIO_BIAS_PULL_DOWN + T_GPIO_BIAS_DEFAULT, + T_GPIO_BIAS_DISABLE, + T_GPIO_BIAS_PULL_UP, + T_GPIO_BIAS_PULL_DOWN } gpio_bias_t; -/** - * @brief Enumeration for GPIO speed settings. - */ +/** Optional pin speed enumeration */ typedef enum { - GPIO_SPEED_DEFAULT, - GPIO_SPEED_LOW, - GPIO_SPEED_MEDIUM, - GPIO_SPEED_HIGH, - GPIO_SPEED_VERY_HIGH + T_GPIO_SPEED_DEFAULT, + T_GPIO_SPEED_LOW, + T_GPIO_SPEED_MEDIUM, + T_GPIO_SPEED_HIGH, + T_GPIO_SPEED_VERY_HIGH } gpio_speed_t; -/** - * @brief Enumeration for enabling or disabling GPIO interrupts. - */ +/** Enable/disable pin interrupt */ typedef enum { - GPIO_IRQ_DISABLED, - GPIO_IRQ_ENABLED + T_GPIO_IRQ_DISABLED, + T_GPIO_IRQ_ENABLED } gpio_irq_t; -/** - * @brief GPIO error status codes. - */ +/** GPIO Status */ +typedef enum { + T_GPIO_STATUS_NOT_INIT, /**< GPIO is not initialized */ + T_GPIO_STATUS_READY, /**< GPIO is ready for use */ + T_GPIO_STATUS_BUSY, /**< GPIO is currently in use */ + T_GPIO_STATUS_ERROR, /**< GPIO encountered an error */ + T_GPIO_STATUS_UPDATING /**< GPIO is currently being reconfigured */ +} gpio_status_t; + +/** GPIO error status codes */ typedef enum { - GPIO_OK, - GPIO_ERR_UNSUPPORTED_MODE, - GPIO_ERR_LINE_BUSY, - GPIO_ERR_NOT_AVAILABLE, - GPIO_ERR_NO_PULL, - GPIO_ERR_NOT_ANALOG, - GPIO_ERR_UNKNOWN + T_GPIO_ERR_OK, /*!< Suscess flag for the Init Function */ + T_GPIO_ERR_UNSUPPORTED_MODE, /*!< Mode is either not valid orsupported by **Hardwaware** */ + T_GPIO_ERR_UNSUPPORTED_ALTERNATE, /*!< Alternateive mode is unvalid */ + T_GPIO_ERR_BUS_BUSY, /*!< The chosen GPIO BUS is already in Use */ + T_GPIO_ERR_NOT_AVAILABLE, /*!< Pin does Not aloow GPIO functionalities or does not Exists */ + T_GPIO_ERR_NOT_BIASABLE, /*!< No Pull-Up or Pull-Down Option are awailable */ + T_GPIO_ERR_NO_INTERRUPT_SUPPORT, /*!< This GPIO Doesn't allow interrupts */ + T_GPIO_ERR_UNSUPPORTED_DRIVE, /*!< This gate griving option is Unsupported (Pushpull, open drain etc..) */ + T_GPIO_ERR_UNSUPPORTED_SPEED, /*!< The choosen bus speed is not supported */ + T_GPIO_ERR_UNKNOWN /*!< Goal Would be To never be Used, but can be helpful during Developpment */ } gpio_error_t; -/** - * @brief Function pointer type for writing a logic level. - */ -typedef void (*gpio_write_fn)(bool value); +/* === Function pointer types (used internally only) === */ -/** - * @brief Function pointer type for reading a logic level. - */ -typedef bool (*gpio_read_fn)(void); - -/** - * @brief Function pointer type for toggling a GPIO pin. - */ -typedef void (*gpio_toggle_fn)(void); - - -/** - * @brief Function pointer type for GPIO deinitialization. - */ -typedef void (*gpio_deinit_fn)(void); - -/** - * @brief Abstract GPIO handle with callback operations. - */ -typedef void (*gpio_error_fn)(gpio_error_t); +typedef void (*fn_ptr_gpio_write)(uint8_t value); +typedef uint8_t (*fn_ptr_gpio_read)(void); +typedef void (*fn_ptr_gpio_toggle)(void); +typedef void (*fn_ptr_gpio_error)(gpio_error_t); +typedef gpio_error_t (*fn_ptr_gpio_update)(void); /** - * @brief Abstract GPIO handle with callback operations. + * @brief Internal function pointer table used by platform implementations. */ typedef struct { - gpio_write_fn set; - gpio_read_fn read; - gpio_toggle_fn toggle; - gpio_deinit_fn deinit; /**< Deinitialization function pointer */ - gpio_error_fn on_error; /**< Error handling callback */ -} gpio_t; - -/** - * @brief Function pointer type for platform-specific GPIO initialization. - */ -typedef gpio_error_t (*gpio_init_fn)(gpio_t* gpio, const void* config); - + fn_ptr_gpio_write set; + fn_ptr_gpio_read read; + fn_ptr_gpio_toggle toggle; + fn_ptr_gpio_update update; + fn_ptr_gpio_error on_error; +} gpio_fn_ptr_t; + +/* === Platform-specific types === */ +typedef struct gpio_s gpio_t; #ifdef KED_USES_LINUX -/** - * @brief Platform-specific GPIO configuration structure for Linux. - */ -typedef struct { +/** GPIO configuration structure for Linux */ +struct gpio_s { const char* chipname; unsigned int line_offset; - gpio_mode_t mode; + gpio_function_t mode; gpio_direction_t direction; gpio_bias_t bias; -} gpio_config; + gpio_status_t status; +}; #endif +/* === Public API === */ + /** * @brief Initialize a GPIO using the specified configuration. * * @param gpio Pointer to target gpio_t handle - * @param config Platform-specific GPIO configuration struct * @return GPIO_OK on success, or an appropriate gpio_error_t */ -gpio_error_t ked_gpio_init(gpio_t* gpio, const gpio_config* config); +gpio_error_t ked_gpio_init(gpio_t* gpio, const gpio_fn_ptr_t* callbacks); /** * @brief Deinitialize and release resources associated with a GPIO pin. + * + * @param gpio Pointer to GPIO object */ -void ked_gpio_deinit(gpio_t* gpio); +void ked_gpio_deinit(gpio_t* gpio, const gpio_fn_ptr_t* callbacks); + +/* === Platform-agnostic GPIO control interface === */ + +#ifdef __cplusplus +} +#endif -#endif /* KED_PERIPHERALS_GPIO_GPIO_H_ */ +#endif /* KED_PERIPHERALS_GPIO_H_ */ diff --git a/peripherals/gpio/gpio.c b/peripherals/gpio/gpio.c index 2001873..f4306fa 100644 --- a/peripherals/gpio/gpio.c +++ b/peripherals/gpio/gpio.c @@ -1,139 +1,163 @@ /** * @file gpio_linux.c - * @brief GPIO implementation for Linux using libgpiod and KED callback injection. + * @brief GPIO implementation for Linux using libgpiod and KED function pointer injection. */ #include "../../ked/peripherals/gpio/gpio.h" #include #include #include +#include /** * @brief Internal Linux GPIO context. * - * Holds pointers to the open GPIO chip and the requested line. + * 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; /**< File descriptor to the opened GPIO chip */ - struct gpiod_line* line; /**< Reference to the requested GPIO line */ + struct gpiod_chip* chip; + struct gpiod_line* line; } gpio_linux_ctx_t; static gpio_linux_ctx_t gpio_ctx; +/* === Centralized Error Handling === */ + /** - * @brief Set the output level of the GPIO line. + * @brief Centralized error handler for GPIO-related failures. + * + * Logs the error to stderr and can be extended to trigger hooks or diagnostics. * - * @param value Logic level to set (true for HIGH, false for LOW). + * @param err The GPIO error code encountered. */ -static void gpio_linux_set(bool value) { - gpiod_line_set_value(gpio_ctx.line, value); +static void gpio_linux_handle_error(gpio_error_t err) { + fprintf(stderr, "[GPIO/Linux] ERROR: %d\n", err); + // Future: report to diagnostic logger or panic handler } +/* === GPIO Operation Implementations === */ + /** - * @brief Read the current logic level of the GPIO line. + * @brief Set the output level of the GPIO line. * - * @return Logic level (true for HIGH, false for LOW). + * @param value 1 = HIGH, 0 = LOW */ -static bool gpio_linux_read(void) { - return gpiod_line_get_value(gpio_ctx.line); +static void gpio_linux_set(uint8_t value) { + if (gpio_ctx.line) { + gpiod_line_set_value(gpio_ctx.line, value); + } else { + gpio_linux_handle_error(T_GPIO_ERR_NOT_AVAILABLE); + } } /** - * @brief Toggle the current state of the GPIO output. + * @brief Read the current logic level of the GPIO line. + * + * @return Logic level (1 = HIGH, 0 = LOW) */ -static void gpio_linux_toggle(void) { - int current = gpiod_line_get_value(gpio_ctx.line); - gpiod_line_set_value(gpio_ctx.line, !current); +static uint8_t gpio_linux_read(void) { + if (!gpio_ctx.line) { + gpio_linux_handle_error(T_GPIO_ERR_NOT_AVAILABLE); + return 0; + } + return (uint8_t)gpiod_line_get_value(gpio_ctx.line); } /** - * @brief Release the GPIO line and chip. - * - * Called during deinitialization to free resources. + * @brief Toggle the current logic level of the GPIO line. */ -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; +static void gpio_linux_toggle(void) { + if (!gpio_ctx.line) { + gpio_linux_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 Default error handling function. + * @brief Update GPIO configuration dynamically. * - * @param err The GPIO error code to report. + * Currently not supported on Linux – placeholder only. */ -static void gpio_linux_handle_error(gpio_error_t err) { - fprintf(stderr, "[GPIO ERROR] Code: %d\n", err); +static gpio_error_t gpio_linux_update(void) { + // TODO: implement runtime reconfiguration if needed + return T_GPIO_ERR_UNSUPPORTED_MODE; } +/* === Public API Implementation === */ + /** - * @brief Initialize a GPIO using a Linux-specific gpio_config. + * @brief Initialize the GPIO using the given Linux gpio_t config and callback struct. * - * Sets the direction and bias of the line according to the config. + * This sets direction, bias, and binds callbacks to the gpio_fn_ptr_t table. * - * @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. + * @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(gpio_t* gpio, const gpio_config* config) { - gpio_ctx.chip = gpiod_chip_open(config->chipname); +gpio_error_t ked_gpio_init(gpio_t* gpio, const gpio_fn_ptr_t* callbacks) { + gpio_ctx.chip = gpiod_chip_open(gpio->chipname); if (!gpio_ctx.chip) { - gpio->on_error(GPIO_ERR_NOT_AVAILABLE); - return GPIO_ERR_NOT_AVAILABLE; + gpio_linux_handle_error(T_GPIO_ERR_NOT_AVAILABLE); + return T_GPIO_ERR_NOT_AVAILABLE; } - gpio_ctx.line = gpiod_chip_get_line(gpio_ctx.chip, config->line_offset); + gpio_ctx.line = gpiod_chip_get_line(gpio_ctx.chip, gpio->line_offset); if (!gpio_ctx.line) { gpiod_chip_close(gpio_ctx.chip); - gpio->on_error(GPIO_ERR_NOT_AVAILABLE); - return GPIO_ERR_NOT_AVAILABLE; + gpio_linux_handle_error(T_GPIO_ERR_NOT_AVAILABLE); + return T_GPIO_ERR_NOT_AVAILABLE; } struct gpiod_line_request_config req_cfg = { - .consumer = "ked", - .request_type = (config->direction == GPIO_DIRECTION_OUTPUT) + .consumer = "ked_gpio", + .request_type = (gpio->direction == T_GPIO_DIRECTION_OUTPUT) ? GPIOD_LINE_REQUEST_DIRECTION_OUTPUT : GPIOD_LINE_REQUEST_DIRECTION_INPUT, .flags = 0 }; - if (config->bias == GPIO_BIAS_PULL_UP) { + if (gpio->bias == T_GPIO_BIAS_PULL_UP) req_cfg.flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP; - } else if (config->bias == GPIO_BIAS_PULL_DOWN) { + else if (gpio->bias == T_GPIO_BIAS_PULL_DOWN) req_cfg.flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN; - } else if (config->bias == GPIO_BIAS_DISABLE) { + else if (gpio->bias == T_GPIO_BIAS_DISABLE) req_cfg.flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE; - } else if (config->bias == GPIO_BIAS_NONE) { + 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); - gpio->on_error(GPIO_ERR_LINE_BUSY); - return GPIO_ERR_LINE_BUSY; + gpio_linux_handle_error(T_GPIO_ERR_BUS_BUSY); + return T_GPIO_ERR_BUS_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; + if (callbacks) { + ((gpio_fn_ptr_t*)callbacks)->set = gpio_linux_set; + ((gpio_fn_ptr_t*)callbacks)->read = gpio_linux_read; + ((gpio_fn_ptr_t*)callbacks)->toggle = gpio_linux_toggle; + ((gpio_fn_ptr_t*)callbacks)->update = gpio_linux_update; + ((gpio_fn_ptr_t*)callbacks)->on_error = gpio_linux_handle_error; + } - return GPIO_OK; + return T_GPIO_ERR_OK; } /** - * @brief Generic GPIO deinitialization entry point. + * @brief Deinitialize GPIO from platform and free resources. * - * @param gpio Pointer to the gpio_t structure with deinit function populated. + * @param gpio Unused in this implementation + * @param callbacks Pointer to callback table that includes `deinit` */ -void ked_gpio_deinit(gpio_t* gpio) { - if (gpio && gpio->deinit) { - gpio->deinit(); +void ked_gpio_deinit(gpio_t* gpio, const gpio_fn_ptr_t* callbacks) { + 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; } }