From 80d16234e500ad9cfcf9e66393d2d3a0632e2cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Mon, 10 Aug 2026 14:39:25 +0200 Subject: [PATCH] fix(lora): skip DIO detach when no ISR is attached (#11386) * fix(lora): skip DIO detach when no ISR is attached Fixes #11371 * fix(lora): latch the ISR-armed flag instead of tracking attach state The flag is now written once from task context and only read from ISR context. --- src/mesh/LR11x0Interface.cpp | 2 +- src/mesh/LR11x0Interface.h | 4 ++-- src/mesh/LR20x0Interface.cpp | 2 +- src/mesh/LR20x0Interface.h | 4 ++-- src/mesh/RF95Interface.cpp | 2 +- src/mesh/RF95Interface.h | 4 ++-- src/mesh/RadioLibInterface.h | 24 ++++++++++++++++++++++-- src/mesh/SX126xInterface.cpp | 4 ++-- src/mesh/SX126xInterface.h | 4 ++-- src/mesh/SX128xInterface.cpp | 2 +- src/mesh/SX128xInterface.h | 4 ++-- 11 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index b8e18bf57..8be0b6413 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -318,7 +318,7 @@ template bool LR11x0Interface::reconfigure() return true; } -template void LR11x0Interface::disableInterrupt() +template void LR11x0Interface::clearRadioIsr() { lora.clearIrqAction(); } diff --git a/src/mesh/LR11x0Interface.h b/src/mesh/LR11x0Interface.h index 552dd5e5e..9280c05de 100644 --- a/src/mesh/LR11x0Interface.h +++ b/src/mesh/LR11x0Interface.h @@ -47,12 +47,12 @@ template class LR11x0Interface : public RadioLibInterface /** * Glue functions called from ISR land */ - virtual void disableInterrupt() override; + virtual void clearRadioIsr() override; /** * Enable a particular ISR callback glue function */ - virtual void enableInterrupt(void (*callback)()) { lora.setIrqAction(callback); } + virtual void setRadioIsr(void (*callback)()) override { lora.setIrqAction(callback); } /** can we detect a LoRa preamble on the current channel? */ virtual bool isChannelActive() override; diff --git a/src/mesh/LR20x0Interface.cpp b/src/mesh/LR20x0Interface.cpp index fc6d12347..dcc514041 100644 --- a/src/mesh/LR20x0Interface.cpp +++ b/src/mesh/LR20x0Interface.cpp @@ -323,7 +323,7 @@ template bool LR20x0Interface::reconfigure() return success; } -template void LR20x0Interface::disableInterrupt() +template void LR20x0Interface::clearRadioIsr() { lora.clearIrqAction(); } diff --git a/src/mesh/LR20x0Interface.h b/src/mesh/LR20x0Interface.h index 263c83429..ed04dfb0e 100644 --- a/src/mesh/LR20x0Interface.h +++ b/src/mesh/LR20x0Interface.h @@ -42,12 +42,12 @@ template class LR20x0Interface : public RadioLibInterface /** * Glue functions called from ISR land */ - virtual void disableInterrupt() override; + virtual void clearRadioIsr() override; /** * Enable a particular ISR callback glue function */ - virtual void enableInterrupt(void (*callback)()) { lora.setIrqAction(callback); } + virtual void setRadioIsr(void (*callback)()) override { lora.setIrqAction(callback); } /** can we detect a LoRa preamble on the current channel? */ virtual bool isChannelActive() override; diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index 54fd6f109..6968b5654 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -201,7 +201,7 @@ bool RF95Interface::init() return res == RADIOLIB_ERR_NONE; } -void RF95Interface::disableInterrupt() +void RF95Interface::clearRadioIsr() { lora->clearDio0Action(); } diff --git a/src/mesh/RF95Interface.h b/src/mesh/RF95Interface.h index 222606764..e01dfe376 100644 --- a/src/mesh/RF95Interface.h +++ b/src/mesh/RF95Interface.h @@ -35,14 +35,14 @@ class RF95Interface : public RadioLibInterface /** * Glue functions called from ISR land */ - virtual void disableInterrupt() override; + virtual void clearRadioIsr() override; int16_t getCurrentRSSI() override; /** * Enable a particular ISR callback glue function */ - virtual void enableInterrupt(void (*callback)()) { lora->setDio0Action(callback, RISING); } + virtual void setRadioIsr(void (*callback)()) override { lora->setDio0Action(callback, RISING); } /** can we detect a LoRa preamble on the current channel? */ virtual bool isChannelActive() override; diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index 295ccc160..82471e760 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -99,6 +99,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified /// are _trying_ to receive a packet currently (note - we might just be waiting for one) bool isReceiving = false; + /// has the radio IRQ ever been armed? latches true and is never cleared, so ISR context only reads it + volatile bool isrEverArmed = false; + protected: // Noise floor tracking - rolling window of samples. static const uint8_t NOISE_FLOOR_SAMPLES = 20; @@ -144,13 +147,26 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified /** * Glue functions called from ISR land + * + * Skip the detach until the IRQ has been armed once: the first setStandby() runs before any + * enableInterrupt(), and ESP-IDF logs "GPIO isr service is not installed" for that call. */ - virtual void disableInterrupt() = 0; + void disableInterrupt() + { + if (!isrEverArmed) + return; + clearRadioIsr(); + } /** * Enable a particular ISR callback glue function */ - virtual void enableInterrupt(void (*)()) = 0; + void enableInterrupt(void (*callback)()) + { + // Latch before arming: the ISR can fire the moment the handler is installed. + isrEverArmed = true; + setRadioIsr(callback); + } /** * Poll as a backup to catch missed edge-triggered interrupts. @@ -300,6 +316,10 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified */ virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) = 0; + /** Chip specific arm/disarm of the radio IRQ; call enableInterrupt()/disableInterrupt() instead */ + virtual void setRadioIsr(void (*callback)()) = 0; + virtual void clearRadioIsr() = 0; + /** * Subclasses must override, implement and then call into this base class implementation */ diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index a06d66cc4..e8d5baf10 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -250,7 +250,7 @@ template int16_t SX126xInterface::getCurrentRSSI() return (int16_t)round(rssi); } -template void SX126xInterface::enableInterrupt(void (*callback)()) +template void SX126xInterface::setRadioIsr(void (*callback)()) { #ifdef LORA_DIO1_SOFTWARE_POLL irqPollingActive = true; @@ -261,7 +261,7 @@ template void SX126xInterface::enableInterrupt(void (*callback)( #endif } -template void SX126xInterface::disableInterrupt() +template void SX126xInterface::clearRadioIsr() { #ifdef LORA_DIO1_SOFTWARE_POLL irqPollingActive = false; diff --git a/src/mesh/SX126xInterface.h b/src/mesh/SX126xInterface.h index 0bf977ba2..9465064b8 100644 --- a/src/mesh/SX126xInterface.h +++ b/src/mesh/SX126xInterface.h @@ -47,12 +47,12 @@ template class SX126xInterface : public RadioLibInterface /** * Glue functions called from ISR land */ - virtual void disableInterrupt() override; + virtual void clearRadioIsr() override; /** * Enable a particular ISR callback glue function */ - virtual void enableInterrupt(void (*callback)()) override; + virtual void setRadioIsr(void (*callback)()) override; #ifdef LORA_DIO1_SOFTWARE_POLL void handleSoftwareLoraIrqPoll() override; diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index 7848d51db..bb1d89024 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -156,7 +156,7 @@ template bool SX128xInterface::reconfigure() return true; } -template void SX128xInterface::disableInterrupt() +template void SX128xInterface::clearRadioIsr() { lora.clearDio1Action(); } diff --git a/src/mesh/SX128xInterface.h b/src/mesh/SX128xInterface.h index 1205087b7..3b9015249 100644 --- a/src/mesh/SX128xInterface.h +++ b/src/mesh/SX128xInterface.h @@ -43,12 +43,12 @@ template class SX128xInterface : public RadioLibInterface /** * Glue functions called from ISR land */ - virtual void disableInterrupt() override; + virtual void clearRadioIsr() override; /** * Enable a particular ISR callback glue function */ - virtual void enableInterrupt(void (*callback)()) { lora.setDio1Action(callback); } + virtual void setRadioIsr(void (*callback)()) override { lora.setDio1Action(callback); } /** can we detect a LoRa preamble on the current channel? */ virtual bool isChannelActive() override;