diff --git a/Platforms/platform-esp32/source/drivers/esp32_gpio.cpp b/Platforms/platform-esp32/source/drivers/esp32_gpio.cpp index d0b39a29..802b945f 100644 --- a/Platforms/platform-esp32/source/drivers/esp32_gpio.cpp +++ b/Platforms/platform-esp32/source/drivers/esp32_gpio.cpp @@ -12,7 +12,12 @@ #define TAG "esp32_gpio" +struct Esp32GpioInternal { + uint8_t isr_service_ref_count = 0; +}; + #define GET_CONFIG(device) ((struct Esp32GpioConfig*)device->config) +#define GET_INTERNAL(device) ((struct Esp32GpioInternal*)device->internal) extern "C" { @@ -97,15 +102,59 @@ static error_t get_native_pin_number(GpioDescriptor* descriptor, void* pin_numbe return ERROR_NONE; } +static error_t add_callback(struct GpioDescriptor* descriptor, void (*callback)(void*), void* arg) { + auto esp_error = gpio_isr_handler_add(static_cast(descriptor->pin), callback, arg); + return esp_err_to_error(esp_error); +} + +static error_t remove_callback(struct GpioDescriptor* descriptor) { + auto esp_error = gpio_isr_handler_remove(static_cast(descriptor->pin)); + return esp_err_to_error(esp_error); +} + +static error_t enable_interrupt(struct GpioDescriptor* descriptor) { + auto* internal = GET_INTERNAL(descriptor->controller); + if (internal->isr_service_ref_count == 0) { + auto esp_error = gpio_install_isr_service(0); + if (esp_error != ESP_OK && esp_error != ESP_ERR_INVALID_STATE) { + return esp_err_to_error(esp_error); + } + } + auto esp_error = gpio_intr_enable(static_cast(descriptor->pin)); + if (esp_error == ESP_OK) { + internal->isr_service_ref_count++; + } + return esp_err_to_error(esp_error); +} + +static error_t disable_interrupt(struct GpioDescriptor* descriptor) { + auto* internal = GET_INTERNAL(descriptor->controller); + auto esp_error = gpio_intr_disable(static_cast(descriptor->pin)); + if (esp_error == ESP_OK && internal->isr_service_ref_count > 0) { + internal->isr_service_ref_count--; + if (internal->isr_service_ref_count == 0) { + gpio_uninstall_isr_service(); + } + } + return esp_err_to_error(esp_error); +} + static error_t start(Device* device) { ESP_LOGI(TAG, "start %s", device->name); - auto pin_count = GET_CONFIG(device)->gpioCount; - return gpio_controller_init_descriptors(device, pin_count, nullptr); + const Esp32GpioConfig* config = GET_CONFIG(device); + device_set_driver_data(device, new Esp32GpioInternal()); + return gpio_controller_init_descriptors(device, config->gpioCount, nullptr); } static error_t stop(Device* device) { ESP_LOGI(TAG, "stop %s", device->name); - return gpio_controller_deinit_descriptors(device); + auto* internal = GET_INTERNAL(device); + if (internal->isr_service_ref_count > 0) { + gpio_uninstall_isr_service(); + } + check(gpio_controller_deinit_descriptors(device) == ERROR_NONE); + delete internal; + return ERROR_NONE; } const static GpioControllerApi esp32_gpio_api = { @@ -113,7 +162,11 @@ const static GpioControllerApi esp32_gpio_api = { .get_level = get_level, .set_flags = set_flags, .get_flags = get_flags, - .get_native_pin_number = get_native_pin_number + .get_native_pin_number = get_native_pin_number, + .add_callback = add_callback, + .remove_callback = remove_callback, + .enable_interrupt = enable_interrupt, + .disable_interrupt = disable_interrupt }; extern struct Module platform_esp32_module; diff --git a/TactilityKernel/include/tactility/drivers/gpio_controller.h b/TactilityKernel/include/tactility/drivers/gpio_controller.h index 2251e2f0..796041b5 100644 --- a/TactilityKernel/include/tactility/drivers/gpio_controller.h +++ b/TactilityKernel/include/tactility/drivers/gpio_controller.h @@ -52,6 +52,36 @@ struct GpioControllerApi { * @return ERROR_NONE if successful */ error_t (*get_native_pin_number)(struct GpioDescriptor* descriptor, void* pin_number); + + /** + * @brief Adds a callback to be called when a GPIO interrupt occurs. + * @param[in] descriptor the pin descriptor + * @param[in] callback the callback function + * @param[in] arg the argument to pass to the callback + * @return ERROR_NONE if successful, ERROR_NOT_SUPPORTED if not implemented + */ + error_t (*add_callback)(struct GpioDescriptor* descriptor, void (*callback)(void*), void* arg); + + /** + * @brief Removes a callback from a GPIO interrupt. + * @param[in] descriptor the pin descriptor + * @return ERROR_NONE if successful, ERROR_NOT_SUPPORTED if not implemented + */ + error_t (*remove_callback)(struct GpioDescriptor* descriptor); + + /** + * @brief Enables the interrupt for a GPIO pin. + * @param[in] descriptor the pin descriptor + * @return ERROR_NONE if successful, ERROR_NOT_SUPPORTED if not implemented + */ + error_t (*enable_interrupt)(struct GpioDescriptor* descriptor); + + /** + * @brief Disables the interrupt for a GPIO pin. + * @param[in] descriptor the pin descriptor + * @return ERROR_NONE if successful, ERROR_NOT_SUPPORTED if not implemented + */ + error_t (*disable_interrupt)(struct GpioDescriptor* descriptor); }; struct GpioDescriptor* gpio_descriptor_acquire( @@ -118,6 +148,36 @@ error_t gpio_descriptor_get_flags(struct GpioDescriptor* descriptor, gpio_flags_ */ error_t gpio_descriptor_get_native_pin_number(struct GpioDescriptor* descriptor, void* pin_number); +/** + * @brief Adds a callback to be called when a GPIO interrupt occurs. + * @param[in] descriptor the pin descriptor + * @param[in] callback the callback function + * @param[in] arg the argument to pass to the callback + * @return ERROR_NONE if successful, ERROR_NOT_SUPPORTED if not implemented + */ +error_t gpio_descriptor_add_callback(struct GpioDescriptor* descriptor, void (*callback)(void*), void* arg); + +/** + * @brief Removes a callback from a GPIO interrupt. + * @param[in] descriptor the pin descriptor + * @return ERROR_NONE if successful, ERROR_NOT_SUPPORTED if not implemented + */ +error_t gpio_descriptor_remove_callback(struct GpioDescriptor* descriptor); + +/** + * @brief Enables the interrupt for a GPIO pin. + * @param[in] descriptor the pin descriptor + * @return ERROR_NONE if successful, ERROR_NOT_SUPPORTED if not implemented + */ +error_t gpio_descriptor_enable_interrupt(struct GpioDescriptor* descriptor); + +/** + * @brief Disables the interrupt for a GPIO pin. + * @param[in] descriptor the pin descriptor + * @return ERROR_NONE if successful, ERROR_NOT_SUPPORTED if not implemented + */ +error_t gpio_descriptor_disable_interrupt(struct GpioDescriptor* descriptor); + /** * @brief Gets the number of pins supported by the controller. * @param[in] device the GPIO controller device diff --git a/TactilityKernel/source/drivers/gpio_controller.cpp b/TactilityKernel/source/drivers/gpio_controller.cpp index 336183a1..76b0ce1f 100644 --- a/TactilityKernel/source/drivers/gpio_controller.cpp +++ b/TactilityKernel/source/drivers/gpio_controller.cpp @@ -130,6 +130,34 @@ error_t gpio_descriptor_get_native_pin_number(struct GpioDescriptor* descriptor, return GPIO_INTERNAL_API(driver)->get_native_pin_number(descriptor, pin_number); } +error_t gpio_descriptor_add_callback(struct GpioDescriptor* descriptor, void (*callback)(void*), void* arg) { + const auto* driver = device_get_driver(descriptor->controller); + auto* api = GPIO_INTERNAL_API(driver); + if (!api->add_callback) return ERROR_NOT_SUPPORTED; + return api->add_callback(descriptor, callback, arg); +} + +error_t gpio_descriptor_remove_callback(struct GpioDescriptor* descriptor) { + const auto* driver = device_get_driver(descriptor->controller); + auto* api = GPIO_INTERNAL_API(driver); + if (!api->remove_callback) return ERROR_NOT_SUPPORTED; + return api->remove_callback(descriptor); +} + +error_t gpio_descriptor_enable_interrupt(struct GpioDescriptor* descriptor) { + const auto* driver = device_get_driver(descriptor->controller); + auto* api = GPIO_INTERNAL_API(driver); + if (!api->enable_interrupt) return ERROR_NOT_SUPPORTED; + return api->enable_interrupt(descriptor); +} + +error_t gpio_descriptor_disable_interrupt(struct GpioDescriptor* descriptor) { + const auto* driver = device_get_driver(descriptor->controller); + auto* api = GPIO_INTERNAL_API(driver); + if (!api->disable_interrupt) return ERROR_NOT_SUPPORTED; + return api->disable_interrupt(descriptor); +} + error_t gpio_descriptor_get_owner_type(struct GpioDescriptor* descriptor, GpioOwnerType* owner_type) { *owner_type = descriptor->owner_type; return ERROR_NONE;