| 323 | } |
| 324 | |
| 325 | inline int attach_external_handler(u8 pin, const isr_config_t& config, isr_handle_t* out_handle) FL_NOEXCEPT { |
| 326 | if (!config.handler) { |
| 327 | ESP_LOGW(ESP32_IDF3_ISR_TAG, "attachExternalHandler: handler is null"); |
| 328 | return -1; // Invalid parameter |
| 329 | } |
| 330 | |
| 331 | // Allocate handle data |
| 332 | auto handle_owner = fl::make_unique<esp32_idf3_isr_handle_data>(); |
| 333 | auto* handle_data = handle_owner.get(); |
| 334 | if (!handle_data) { |
| 335 | ESP_LOGW(ESP32_IDF3_ISR_TAG, "attachExternalHandler: failed to allocate handle data"); |
| 336 | return -3; // Out of memory |
| 337 | } |
| 338 | |
| 339 | handle_data->is_timer = false; |
| 340 | handle_data->user_handler = config.handler; |
| 341 | handle_data->user_data = config.user_data; |
| 342 | |
| 343 | // Configure GPIO |
| 344 | gpio_config_t io_conf = {}; |
| 345 | io_conf.pin_bit_mask = (1ULL << pin); |
| 346 | io_conf.mode = GPIO_MODE_INPUT; |
| 347 | io_conf.pull_up_en = GPIO_PULLUP_DISABLE; |
| 348 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; |
| 349 | |
| 350 | // Set interrupt type based on flags |
| 351 | if (config.flags & isr::ISR_FLAG_EDGE_RISING) { |
| 352 | io_conf.intr_type = GPIO_INTR_POSEDGE; |
| 353 | } else if (config.flags & isr::ISR_FLAG_EDGE_FALLING) { |
| 354 | io_conf.intr_type = GPIO_INTR_NEGEDGE; |
| 355 | } else if (config.flags & isr::ISR_FLAG_LEVEL_HIGH) { |
| 356 | io_conf.intr_type = GPIO_INTR_HIGH_LEVEL; |
| 357 | } else if (config.flags & isr::ISR_FLAG_LEVEL_LOW) { |
| 358 | io_conf.intr_type = GPIO_INTR_LOW_LEVEL; |
| 359 | } else { |
| 360 | // Default to any edge |
| 361 | io_conf.intr_type = GPIO_INTR_ANYEDGE; |
| 362 | } |
| 363 | |
| 364 | esp_err_t ret = gpio_config(&io_conf); |
| 365 | if (ret != ESP_OK) { |
| 366 | ESP_LOGW(ESP32_IDF3_ISR_TAG, "attachExternalHandler: gpio_config failed: %s", esp_err_to_name(ret)); |
| 367 | return -9; // GPIO config failed |
| 368 | } |
| 369 | |
| 370 | // Install GPIO ISR service if not already installed |
| 371 | // Use critical section for multi-core safety |
| 372 | static bool gpio_isr_service_installed = false; |
| 373 | if (!gpio_isr_service_installed) { |
| 374 | portENTER_CRITICAL(&gpio_isr_service_mutex_idf3); |
| 375 | // Double-check after acquiring lock (classic double-checked locking pattern) |
| 376 | if (!gpio_isr_service_installed) { |
| 377 | ret = gpio_install_isr_service(0); |
| 378 | if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) { |
| 379 | portEXIT_CRITICAL(&gpio_isr_service_mutex_idf3); |
| 380 | ESP_LOGW(ESP32_IDF3_ISR_TAG, "attachExternalHandler: gpio_install_isr_service failed: %s", esp_err_to_name(ret)); |
| 381 | return -10; // ISR service installation failed |
| 382 | } |
nothing calls this directly
no test coverage detected