| 244 | } |
| 245 | |
| 246 | inline int attach_external_handler(u8 pin, const isr_config_t& config, isr_handle_t* out_handle) FL_NOEXCEPT { |
| 247 | if (!config.handler) { |
| 248 | ESP_LOGW(ESP32_ISR_TAG, "attachExternalHandler: handler is null"); |
| 249 | return -1; // Invalid parameter |
| 250 | } |
| 251 | |
| 252 | // Allocate handle data |
| 253 | auto handle_owner = fl::make_unique<esp32_isr_handle_data>(); |
| 254 | auto* handle_data = handle_owner.get(); |
| 255 | if (!handle_data) { |
| 256 | ESP_LOGW(ESP32_ISR_TAG, "attachExternalHandler: failed to allocate handle data"); |
| 257 | return -3; // Out of memory |
| 258 | } |
| 259 | |
| 260 | handle_data->is_timer = false; |
| 261 | handle_data->user_handler = config.handler; |
| 262 | handle_data->user_data = config.user_data; |
| 263 | |
| 264 | // Configure GPIO |
| 265 | gpio_config_t io_conf = {}; |
| 266 | io_conf.pin_bit_mask = (1ULL << pin); |
| 267 | io_conf.mode = GPIO_MODE_INPUT; |
| 268 | io_conf.pull_up_en = GPIO_PULLUP_DISABLE; |
| 269 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; |
| 270 | |
| 271 | // Set interrupt type based on flags |
| 272 | if (config.flags & isr::ISR_FLAG_EDGE_RISING) { |
| 273 | io_conf.intr_type = GPIO_INTR_POSEDGE; |
| 274 | } else if (config.flags & isr::ISR_FLAG_EDGE_FALLING) { |
| 275 | io_conf.intr_type = GPIO_INTR_NEGEDGE; |
| 276 | } else if (config.flags & isr::ISR_FLAG_LEVEL_HIGH) { |
| 277 | io_conf.intr_type = GPIO_INTR_HIGH_LEVEL; |
| 278 | } else if (config.flags & isr::ISR_FLAG_LEVEL_LOW) { |
| 279 | io_conf.intr_type = GPIO_INTR_LOW_LEVEL; |
| 280 | } else { |
| 281 | // Default to any edge |
| 282 | io_conf.intr_type = GPIO_INTR_ANYEDGE; |
| 283 | } |
| 284 | |
| 285 | esp_err_t ret = gpio_config(&io_conf); |
| 286 | if (ret != ESP_OK) { |
| 287 | ESP_LOGW(ESP32_ISR_TAG, "attachExternalHandler: gpio_config failed: %s", esp_err_to_name(ret)); |
| 288 | return -9; // GPIO config failed |
| 289 | } |
| 290 | |
| 291 | // Install GPIO ISR service if not already installed |
| 292 | // Use critical section for multi-core safety |
| 293 | static bool gpio_isr_service_installed = false; |
| 294 | if (!gpio_isr_service_installed) { |
| 295 | taskENTER_CRITICAL(&gpio_isr_service_mutex); |
| 296 | // Double-check after acquiring lock (classic double-checked locking pattern) |
| 297 | if (!gpio_isr_service_installed) { |
| 298 | ret = gpio_install_isr_service(0); |
| 299 | if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) { |
| 300 | taskEXIT_CRITICAL(&gpio_isr_service_mutex); |
| 301 | ESP_LOGW(ESP32_ISR_TAG, "attachExternalHandler: gpio_install_isr_service failed: %s", esp_err_to_name(ret)); |
| 302 | return -10; // ISR service installation failed |
| 303 | } |
nothing calls this directly
no test coverage detected