/** * @file gpio_linux.c * @brief GPIO implementation for Linux using libgpiod and KED callback injection. */ #include "../../ked/peripherals/gpio/gpio.h" #include #include #include /** * @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(); } }