/** * @file gpio_linux.c * @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 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. */ 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 Set the output level of the GPIO line. * * @param value 1 = HIGH, 0 = LOW */ 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 Read the current logic level of the GPIO line. * * @return Logic level (1 = HIGH, 0 = LOW) */ 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 Toggle the current logic level of the GPIO line. */ 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 Update GPIO configuration dynamically. * * Currently not supported on Linux – placeholder only. */ static gpio_error_t gpio_linux_update(void) { // TODO: implement runtime reconfiguration if needed return T_GPIO_ERR_UNSUPPORTED_MODE; } /* === Static GPIO Function Table Instance === */ /** * @brief Shared Linux GPIO function table for all gpio_t instances. * * Each GPIO object holds a pointer to this table via its `vtable` field. * This avoids redundant copies of function pointers per object. */ static const gpio_fn_ptr_t gpio_linux_fn = { .set = gpio_linux_set, .read = gpio_linux_read, .toggle = gpio_linux_toggle, .update = gpio_linux_update, .on_error = gpio_linux_handle_error }; /* === 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(gpio_t* gpio) { gpio_ctx.chip = gpiod_chip_open(gpio->chipname); if (!gpio_ctx.chip) { 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, gpio->line_offset); if (!gpio_ctx.line) { gpiod_chip_close(gpio_ctx.chip); gpio_linux_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); gpio_linux_handle_error(T_GPIO_ERR_BUS_BUSY); return T_GPIO_ERR_BUS_BUSY; } gpio->func = &gpio_linux_fn; return T_GPIO_ERR_OK; } /** * @brief Deinitialize GPIO from platform and free resources. * * @param gpio Unused in this implementation * @param callbacks Pointer to callback table that includes `deinit` */ 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; } }