From a422e9ea8bee4e11ce2a8be24f0ce0bebce68126 Mon Sep 17 00:00:00 2001 From: Jon Ringle Date: Wed, 25 Mar 2026 08:25:50 -0400 Subject: [PATCH] drivers: lora: sx12xx: clear async_rx_cb when cancelling async reception When lora_recv_async() is called with cb=NULL to cancel ongoing reception, modem_release() is called but async_rx_cb is left pointing to the previous callback. If a stale DIO1 work item fires after the radio has been reconfigured for TX, sx12xx_ev_rx_done() checks async_rx_cb (still set), calls Radio.Rx(0) during an active TX, and fires the stale callback. This corrupts the radio state and leaks packet buffers allocated by the callback. Clear async_rx_cb before calling modem_release() so that any stale DIO1 work item that fires after cancellation takes the synchronous path in sx12xx_ev_rx_done(), which properly checks modem_usage and bails out. Signed-off-by: Jon Ringle --- drivers/lora/loramac_node/sx12xx_common.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/lora/loramac_node/sx12xx_common.c b/drivers/lora/loramac_node/sx12xx_common.c index 83a307f69883f..4488ea54088e6 100644 --- a/drivers/lora/loramac_node/sx12xx_common.c +++ b/drivers/lora/loramac_node/sx12xx_common.c @@ -103,6 +103,12 @@ static void sx12xx_ev_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, { struct k_poll_signal *sig = dev_data.operation_done; + if (!dev_data.async_rx_cb && !sig) { + LOG_WRN("RxDone event with no registered consumer " + "(modem_usage=%d)", + (int)atomic_get(&dev_data.modem_usage)); + } + /* Receiving in asynchronous mode */ if (dev_data.async_rx_cb) { /* Start receiving again */ @@ -312,6 +318,7 @@ int sx12xx_lora_recv_async(const struct device *dev, lora_recv_cb cb, void *user { /* Cancel ongoing reception */ if (cb == NULL) { + dev_data.async_rx_cb = NULL; if (!modem_release(&dev_data)) { /* Not receiving or already being stopped */ return -EINVAL;