From ce69a95e0031506bd84cf378f376211d2421327d Mon Sep 17 00:00:00 2001 From: liquidraver <504870+liquidraver@users.noreply.github.com> Date: Thu, 9 Jul 2026 21:55:42 +0200 Subject: [PATCH] fix lr1110 CRC bug --- .../drivers/lora/lr11xx/lr11xx_lora.c | 44 ++++++++++++++----- .../drivers/lora/lr20xx/lr20xx_lora.c | 35 +++++++++++---- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/zephcore/patches/zephyr-new/drivers/lora/lr11xx/lr11xx_lora.c b/zephcore/patches/zephyr-new/drivers/lora/lr11xx/lr11xx_lora.c index 7a6295e..5647ec5 100644 --- a/zephcore/patches/zephyr-new/drivers/lora/lr11xx/lr11xx_lora.c +++ b/zephcore/patches/zephyr-new/drivers/lora/lr11xx/lr11xx_lora.c @@ -429,8 +429,19 @@ static void lr11xx_dio1_work_handler(struct k_work *work) data->dio1_stuck_count = 0; } - /* ── RX done ── */ - if (irq & LR11XX_SYSTEM_IRQ_RX_DONE) { + /* ── RX done ── + * Gated on no error bits: a CRC-failed packet asserts RX_DONE and + * CRC_ERROR together on this chip family (same as SX126x — see the + * sx126x patch's "they co-fire here"; LBM's radio_planner likewise + * checks errors before RX_DONE), so an ungated done-first read + * would deliver corrupted payloads as valid. A good packet + * coalesced with an earlier HEADER_ERROR in the same handler + * window is dropped too — the header error may have shifted the + * RX buffer pointer (errata handling below), making the read + * suspect. The error branch below owns the window instead. */ + if ((irq & LR11XX_SYSTEM_IRQ_RX_DONE) && + !(irq & (LR11XX_SYSTEM_IRQ_CRC_ERROR | + LR11XX_SYSTEM_IRQ_HEADER_ERROR))) { lr11xx_radio_rx_buffer_status_t rx_stat; lr11xx_radio_get_rx_buffer_status(ctx, &rx_stat); @@ -525,21 +536,34 @@ static void lr11xx_dio1_work_handler(struct k_work *work) } } - /* ── CRC / Header error ── */ - if (irq & LR11XX_SYSTEM_IRQ_CRC_ERROR || - ((irq & LR11XX_SYSTEM_IRQ_HEADER_ERROR) && - !(irq & LR11XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID))) { - LOG_DBG("RX error: CRC=%d HDR=%d", + /* ── CRC / Header error ── + * Plain `CRC || HDR` — no SYNC_WORD_HEADER_VALID gate (RadioLib + * readData's false-alarm filter). IRQ status is bulk-cleared on + * every handler entry, so a set HEADER_ERROR always belongs to + * this window; a SYNC_VALID bit latched by another packet in the + * same window must not suppress the errata standby below. */ + if (irq & (LR11XX_SYSTEM_IRQ_CRC_ERROR | + LR11XX_SYSTEM_IRQ_HEADER_ERROR)) { + LOG_DBG("RX error: CRC=%d HDR=%d RXDONE=%d", (irq & LR11XX_SYSTEM_IRQ_CRC_ERROR) ? 1 : 0, - (irq & LR11XX_SYSTEM_IRQ_HEADER_ERROR) ? 1 : 0); + (irq & LR11XX_SYSTEM_IRQ_HEADER_ERROR) ? 1 : 0, + (irq & LR11XX_SYSTEM_IRQ_RX_DONE) ? 1 : 0); - /* LR1110 errata: header error can shift the RX buffer - * pointer by 4 bytes. Standby clears the shift. */ + /* LR1110 errata: a header error makes the chip's reported + * buffer_start_pointer stale by +4 for every subsequent + * packet (stacking) until a standby resets the shift. + * Arduino MeshCore's CustomLR1110 calls standby() on every + * header error unconditionally — mirror that. */ if (irq & LR11XX_SYSTEM_IRQ_HEADER_ERROR) { lr11xx_system_set_standby(ctx, LR11XX_SYSTEM_STANDBY_CFG_RC); } + /* Drop whatever the failed (or coalesced) packet left in + * the RX buffer — RadioLib clears it on the CRC-error read + * path too. */ + lr11xx_regmem_clear_rxbuffer(ctx); + if (!data->tx_active) { lr11xx_restart_rx(data); rx_restarted = true; diff --git a/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c index 766b46d..57acd7f 100644 --- a/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c +++ b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c @@ -601,8 +601,17 @@ static void lr20xx_dio1_work_handler(struct k_work *work) data->dio1_stuck_count = 0; } - /* ── RX done ── */ - if (irq & LR20XX_SYSTEM_IRQ_RX_DONE) { + /* ── RX done ── + * Gated on no error bits: a CRC-failed packet asserts RX_DONE and + * CRC_ERROR together on this chip family (same as SX126x/LR11xx), + * so an ungated done-first read would deliver corrupted payloads + * as valid. A good packet coalesced with an earlier header error + * in the same handler window is dropped too — the aborted packet + * may have left bytes in the RX FIFO, misaligning the read. The + * error branch below owns the window instead. */ + if ((irq & LR20XX_SYSTEM_IRQ_RX_DONE) && + !(irq & (LR20XX_SYSTEM_IRQ_CRC_ERROR | + LR20XX_SYSTEM_IRQ_LORA_HEADER_ERROR))) { uint16_t pkt_len = 0; lr20xx_radio_common_get_rx_packet_length(ctx, &pkt_len); @@ -688,13 +697,23 @@ static void lr20xx_dio1_work_handler(struct k_work *work) } } - /* ── CRC / Header error ── */ - if (irq & LR20XX_SYSTEM_IRQ_CRC_ERROR || - ((irq & LR20XX_SYSTEM_IRQ_LORA_HEADER_ERROR) && - !(irq & LR20XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID))) { - LOG_WRN("RX error: CRC=%d HDR=%d", + /* ── CRC / Header error ── + * Plain `CRC || HDR` — no SYNC_WORD_HEADER_VALID gate. IRQ status + * is bulk-cleared on every handler entry, so a set header error + * always belongs to this window; a SYNC_VALID bit latched by + * another packet in the same window must not suppress it. */ + if (irq & (LR20XX_SYSTEM_IRQ_CRC_ERROR | + LR20XX_SYSTEM_IRQ_LORA_HEADER_ERROR)) { + LOG_WRN("RX error: CRC=%d HDR=%d RXDONE=%d", (irq & LR20XX_SYSTEM_IRQ_CRC_ERROR) ? 1 : 0, - (irq & LR20XX_SYSTEM_IRQ_LORA_HEADER_ERROR) ? 1 : 0); + (irq & LR20XX_SYSTEM_IRQ_LORA_HEADER_ERROR) ? 1 : 0, + (irq & LR20XX_SYSTEM_IRQ_RX_DONE) ? 1 : 0); + + /* Drop whatever the failed (or coalesced) packet left in + * the RX FIFO so the next packet's read starts aligned — + * lr20xx_restart_rx does not clear it (only full start_rx + * does). */ + lr20xx_radio_fifo_clear_rx(ctx); if (!data->tx_active) { lr20xx_restart_rx(data);