From bdeba1710e1a70cf02c178caccdfe7659441bc40 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 28 Feb 2026 14:54:05 -1000 Subject: [PATCH] Address review feedback for ISR wake support - Replace __get_IPSR() with inline asm to avoid CMSIS header dependency on LibreTiny chip families that may not declare it (e.g. beken-72xx) - Pass local variable to wake_from_isr and call portYIELD_FROM_ISR for immediate context switch instead of passing NULL - Update comment in component.cpp to mention both ESP32 and LibreTiny platform-specific context detection - Switch USE_LWIP_FAST_SELECT from cg.add_define to cg.add_build_flag so it is visible in .c translation units, aligning guards across all source files --- esphome/components/socket/__init__.py | 2 +- esphome/core/component.cpp | 5 +++-- esphome/core/lwip_fast_select.c | 28 +++++++++++++++++++-------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/esphome/components/socket/__init__.py b/esphome/components/socket/__init__.py index a83648979c..a1e5d16036 100644 --- a/esphome/components/socket/__init__.py +++ b/esphome/components/socket/__init__.py @@ -191,7 +191,7 @@ async def to_code(config): # ESP32 and LibreTiny both have LwIP >= 2.1.3 with lwip_socket_dbg_get_socket() # and FreeRTOS task notifications — enable fast select to bypass lwip_select() if CORE.is_esp32 or CORE.is_libretiny: - cg.add_define("USE_LWIP_FAST_SELECT") + cg.add_build_flag("-DUSE_LWIP_FAST_SELECT") def FILTER_SOURCE_FILES() -> list[str]: diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index 11e15ea87b..c8875f0705 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -326,8 +326,9 @@ void IRAM_ATTR HOT Component::enable_loop_soon_any_context() { #ifdef USE_LWIP_FAST_SELECT // Wake the main loop if sleeping in ulTaskNotifyTake(). Without this, // the main loop would not wake until the select timeout expires (~16ms). - // Uses xPortInIsrContext() to pick xTaskNotifyGive (task) or - // vTaskNotifyGiveFromISR (ISR) — safe from any calling context. + // Uses platform-specific context detection to choose the correct notify + // API (task vs ISR), e.g. xPortInIsrContext() on ESP32 or IPSR register + // on LibreTiny — safe from any calling context. Application::wake_loop_any_context(); #endif } diff --git a/esphome/core/lwip_fast_select.c b/esphome/core/lwip_fast_select.c index 8c4b26ccac..370638a8e0 100644 --- a/esphome/core/lwip_fast_select.c +++ b/esphome/core/lwip_fast_select.c @@ -105,10 +105,9 @@ // critical sections). Multiple concurrent xTaskNotifyGive calls are safe — // the notification count simply increments. -// USE_ESP32 and USE_LIBRETINY are compiler -D flags, so they are always visible in this .c file. -// Feature macros like USE_LWIP_FAST_SELECT may come from generated headers that are not included here, -// so this implementation is enabled based on platform flags instead of USE_LWIP_FAST_SELECT. -#if defined(USE_ESP32) || defined(USE_LIBRETINY) +// USE_LWIP_FAST_SELECT is set via -D build flag (not cg.add_define) so it is +// visible in both .c and .cpp translation units. +#ifdef USE_LWIP_FAST_SELECT // LwIP headers must come first — they define netconn_callback, struct lwip_sock, etc. #include @@ -242,17 +241,30 @@ void IRAM_ATTR esphome_lwip_wake_main_loop_from_isr(int *px_higher_priority_task // Wake the main loop from any context (ISR, thread, or main loop). // Detects ISR context and delegates to the appropriate variant: // ESP32 (Xtensa/RISC-V): xPortInIsrContext() — checks interrupt nesting counter -// LibreTiny (ARM Cortex-M): __get_IPSR() — reads IPSR register (0 = task context) +// LibreTiny (ARM Cortex-M): IPSR register read via inline asm (0 = task context) +#ifdef USE_LIBRETINY +// Read the ARM Cortex-M IPSR register directly via inline asm. +// Avoids depending on CMSIS __get_IPSR() which may not be declared/available +// in all LibreTiny chip family toolchains (e.g. beken-72xx). +static inline uint32_t esphome_get_ipsr(void) { + uint32_t ipsr; + __asm volatile("mrs %0, ipsr" : "=r"(ipsr)); + return ipsr; +} +#endif + void IRAM_ATTR esphome_lwip_wake_main_loop_any_context(void) { #ifdef USE_ESP32 if (xPortInIsrContext()) { #else - if (__get_IPSR() != 0) { + if (esphome_get_ipsr() != 0) { #endif - esphome_lwip_wake_main_loop_from_isr(NULL); + int px_higher_priority_task_woken = 0; + esphome_lwip_wake_main_loop_from_isr(&px_higher_priority_task_woken); + portYIELD_FROM_ISR(px_higher_priority_task_woken); } else { esphome_lwip_wake_main_loop(); } } -#endif // defined(USE_ESP32) || defined(USE_LIBRETINY) +#endif // USE_LWIP_FAST_SELECT