From e78e197dee44076ba19c8012b00bdea9f8d08271 Mon Sep 17 00:00:00 2001 From: liquidraver <504870+liquidraver@users.noreply.github.com> Date: Mon, 11 May 2026 10:13:27 +0200 Subject: [PATCH] sx1262 fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - calibrate_image: revert to datasheet band table. Narrow ±2 MHz window had truncation bug placing 433/869 MHz operating freq outside their own calibration windows. DS §9.2.1 confirms cal is range-validity, not point-precision - reset_agc: K_FOREVER mutex → K_MSEC(50). Was holding dispatcher thread up to ~3 s if a long TX/RX held the SPI lock. Now bails with WARN log; caller retries next maintenance interval. --- .gitignore | 1 + .../zephyr/0003-lora-sx126x-native.patch | 108 ++++++++---------- 2 files changed, 48 insertions(+), 61 deletions(-) diff --git a/.gitignore b/.gitignore index 5b69c50..aca3ac7 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,4 @@ THINKNODE_M1_HANDOVER.md CLAUDE.md zephcore/apc_checklist.md zephcore/apc.md +RADIO_AUDIT_INDEX.md diff --git a/zephcore/patches/zephyr/0003-lora-sx126x-native.patch b/zephcore/patches/zephyr/0003-lora-sx126x-native.patch index 7e4265e..619f667 100644 --- a/zephcore/patches/zephyr/0003-lora-sx126x-native.patch +++ b/zephcore/patches/zephyr/0003-lora-sx126x-native.patch @@ -68,7 +68,7 @@ index 17689720dd2..09982dc2e70 100644 return -EINVAL; } diff --git a/drivers/lora/native/sx126x/sx126x.c b/drivers/lora/native/sx126x/sx126x.c -index 30243ba5dc7..befb84797eb 100644 +index 30243ba5dc7..86c10f39fc3 100644 --- a/drivers/lora/native/sx126x/sx126x.c +++ b/drivers/lora/native/sx126x/sx126x.c @@ -10,10 +10,16 @@ @@ -88,34 +88,7 @@ index 30243ba5dc7..befb84797eb 100644 #define SX126X_REST_STATE \ (IS_ENABLED(CONFIG_LORA_SX126X_NATIVE_SLEEP) \ ? SX126X_STATE_SLEEP : SX126X_STATE_IDLE) -@@ -177,22 +183,10 @@ static int sx126x_calibrate_image(const struct device *dev, uint32_t freq) - { - uint8_t buf[2]; - -- if (freq > 900000000) { -- buf[0] = 0xE1; -- buf[1] = 0xE9; -- } else if (freq > 850000000) { -- buf[0] = 0xD7; -- buf[1] = 0xDB; -- } else if (freq > 770000000) { -- buf[0] = 0xC1; -- buf[1] = 0xC5; -- } else if (freq > 460000000) { -- buf[0] = 0x75; -- buf[1] = 0x81; -- } else { -- buf[0] = 0x6B; -- buf[1] = 0x6F; -- } -+ /* Calibrate ±2 MHz around the actual frequency. -+ * CalibrateImage bytes are in 4 MHz steps (freq_Hz / 4000000). */ -+ buf[0] = (uint8_t)((freq - 2000000) / 4000000); -+ buf[1] = (uint8_t)((freq + 2000000) / 4000000); - - return sx126x_hal_write_cmd(dev, SX126X_CMD_CALIBRATE_IMAGE, buf, 2); - } -@@ -222,6 +216,7 @@ static int sx126x_set_packet_params(const struct device *dev, +@@ -222,6 +228,7 @@ static int sx126x_set_packet_params(const struct device *dev, uint8_t invert_iq) { uint8_t buf[6]; @@ -123,7 +96,7 @@ index 30243ba5dc7..befb84797eb 100644 sys_put_be16(preamble_len, &buf[0]); buf[2] = header_type; -@@ -229,7 +224,31 @@ static int sx126x_set_packet_params(const struct device *dev, +@@ -229,7 +236,31 @@ static int sx126x_set_packet_params(const struct device *dev, buf[4] = crc_mode; buf[5] = invert_iq; @@ -156,7 +129,7 @@ index 30243ba5dc7..befb84797eb 100644 } static int sx126x_set_sync_word(const struct device *dev, bool public_network) -@@ -247,6 +266,13 @@ static int sx126x_set_rx_gain(const struct device *dev, bool boosted) +@@ -247,6 +278,13 @@ static int sx126x_set_rx_gain(const struct device *dev, bool boosted) { uint8_t val = boosted ? SX126X_RX_GAIN_BOOSTED : SX126X_RX_GAIN_POWER_SAVING; @@ -170,7 +143,7 @@ index 30243ba5dc7..befb84797eb 100644 return sx126x_hal_write_regs(dev, SX126X_REG_RX_GAIN, &val, 1); } -@@ -296,7 +322,7 @@ static int sx126x_get_packet_status(const struct device *dev, +@@ -296,7 +334,7 @@ static int sx126x_get_packet_status(const struct device *dev, uint8_t buf[3]; int ret; @@ -179,15 +152,20 @@ index 30243ba5dc7..befb84797eb 100644 if (ret == 0) { /* RSSI is -value/2 dBm */ *rssi = -((int16_t)buf[0] >> 1); -@@ -367,6 +393,21 @@ static int sx126x_chip_init(const struct device *dev) +@@ -367,6 +405,26 @@ static int sx126x_chip_init(const struct device *dev) return ret; } -+ /* After TX/RX, fall back to STDBY_XOSC instead of STDBY_RC. -+ * Keeps the crystal warm so the next SetRx skips the ~500 us -+ * XOSC startup, reducing the deaf window between packets. */ ++ /* After TX/RX, fall back to FS mode instead of the default STDBY_RC. ++ * FS keeps both XOSC and PLL active, so the next SetRx skips both the ++ * ~500 us XOSC startup AND the ~50-100 us PLL settle — minimising the ++ * deaf window between packets. Costs ~5-10 mA while in this state, but ++ * we are typically only here for the brief ISR-to-handler window before ++ * sleep or the next operation. Critical for duty-cycle restart where ++ * every microsecond of deafness can mean a missed preamble. ++ * (datasheet Table 13-23: 0x40=FS, 0x30=STDBY_XOSC, 0x20=STDBY_RC) */ + { -+ uint8_t fallback = 0x40; /* STDBY_XOSC */ ++ uint8_t fallback = 0x40; /* FS mode */ + + ret = sx126x_hal_write_cmd(dev, + SX126X_CMD_SET_RX_TX_FALLBACK_MODE, @@ -201,8 +179,7 @@ index 30243ba5dc7..befb84797eb 100644 /* Set packet type to LoRa */ ret = sx126x_set_packet_type(dev, SX126X_PACKET_TYPE_LORA); if (ret < 0) { -@@ -374,10 +415,16 @@ static int sx126x_chip_init(const struct device *dev) - LOG_ERR("Set packet type failed: %d", ret); +@@ -374,10 +432,16 @@ static int sx126x_chip_init(const struct device *dev) return ret; } @@ -221,7 +198,8 @@ index 30243ba5dc7..befb84797eb 100644 + ret = sx126x_set_dio_irq_params(dev, irq_mask, dio1_mask, 0, 0); if (ret < 0) { LOG_ERR("Set IRQ params failed: %d", ret); -@@ -398,7 +445,7 @@ static void sx126x_dio1_callback(const struct device *dev) + return ret; +@@ -398,7 +462,7 @@ static void sx126x_dio1_callback(const struct device *dev) { struct sx126x_data *data = dev->data; @@ -230,7 +208,7 @@ index 30243ba5dc7..befb84797eb 100644 } static void sx126x_set_rf_path(const struct device *dev, bool enable, bool tx) -@@ -406,7 +453,20 @@ static void sx126x_set_rf_path(const struct device *dev, bool enable, bool tx) +@@ -406,7 +470,20 @@ static void sx126x_set_rf_path(const struct device *dev, bool enable, bool tx) const struct sx126x_hal_config *config = dev->config; sx126x_hal_set_antenna_enable(dev, enable); @@ -252,7 +230,7 @@ index 30243ba5dc7..befb84797eb 100644 sx126x_hal_set_rf_switch(dev, enable, tx); } } -@@ -455,6 +515,80 @@ static int sx126x_reconnect_rf_gpios(const struct device *dev) +@@ -455,6 +532,80 @@ static int sx126x_reconnect_rf_gpios(const struct device *dev) } #endif /* CONFIG_PM_DEVICE */ @@ -333,7 +311,7 @@ index 30243ba5dc7..befb84797eb 100644 static int sx126x_set_sleep(const struct device *dev) { struct sx126x_data *data = dev->data; -@@ -533,6 +667,9 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta +@@ -533,6 +684,9 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta struct sx126x_data *data = dev->data; struct sx126x_rx_result result = { 0 }; uint8_t payload_len = 0, offset = 0; @@ -343,7 +321,7 @@ index 30243ba5dc7..befb84797eb 100644 int ret; /* Get received packet info */ -@@ -564,20 +701,34 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta +@@ -564,20 +718,34 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta } } @@ -390,7 +368,7 @@ index 30243ba5dc7..befb84797eb 100644 } } else { /* Sync mode */ -@@ -589,8 +740,46 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta +@@ -589,8 +757,46 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta static void sx126x_handle_irq_timeout(const struct device *dev) { struct sx126x_data *data = dev->data; @@ -437,7 +415,7 @@ index 30243ba5dc7..befb84797eb 100644 sx126x_set_sleep(dev); if (data->tx_async_signal != NULL) { -@@ -637,6 +826,25 @@ static void sx126x_irq_work_handler(struct k_work *work) +@@ -637,6 +843,25 @@ static void sx126x_irq_work_handler(struct k_work *work) sx126x_handle_irq_timeout(dev); } @@ -463,7 +441,7 @@ index 30243ba5dc7..befb84797eb 100644 /* Re-enable the DIO1 interrupt for the next event (unless sleeping) */ if (atomic_get(&data->state) != SX126X_REST_STATE) { sx126x_hal_dio1_irq_enable(dev); -@@ -682,7 +890,7 @@ static int sx126x_lora_config(const struct device *dev, +@@ -682,7 +907,7 @@ static int sx126x_lora_config(const struct device *dev, /* Configure PA and TX power based on chip variant and frequency */ ret = sx126x_hal_configure_tx_params(dev, config->tx_power, config->frequency, @@ -472,7 +450,7 @@ index 30243ba5dc7..befb84797eb 100644 if (ret < 0) { goto out; } -@@ -698,6 +906,29 @@ static int sx126x_lora_config(const struct device *dev, +@@ -698,6 +923,29 @@ static int sx126x_lora_config(const struct device *dev, goto out; } @@ -502,7 +480,7 @@ index 30243ba5dc7..befb84797eb 100644 /* Set sync word */ ret = sx126x_set_sync_word(dev, config->public_network); if (ret < 0) { -@@ -721,6 +952,8 @@ out: +@@ -721,6 +969,8 @@ out: return ret; } @@ -511,7 +489,7 @@ index 30243ba5dc7..befb84797eb 100644 static int sx126x_lora_send_async(const struct device *dev, uint8_t *data_buf, uint32_t data_len, struct k_poll_signal *async) -@@ -752,6 +985,65 @@ static int sx126x_lora_send_async(const struct device *dev, +@@ -752,6 +1002,65 @@ static int sx126x_lora_send_async(const struct device *dev, return ret; } @@ -577,7 +555,7 @@ index 30243ba5dc7..befb84797eb 100644 data->tx_async_signal = async; k_msgq_purge(&data->tx_msgq); -@@ -777,6 +1069,29 @@ static int sx126x_lora_send_async(const struct device *dev, +@@ -777,6 +1086,29 @@ static int sx126x_lora_send_async(const struct device *dev, /* Enable antenna and set TX path */ sx126x_set_rf_path(dev, true, true); @@ -607,7 +585,7 @@ index 30243ba5dc7..befb84797eb 100644 /* Start transmission with 10 second timeout */ ret = sx126x_set_tx(dev, 10000); if (ret < 0) { -@@ -847,6 +1162,8 @@ static int sx126x_lora_recv(const struct device *dev, uint8_t *data_buf, +@@ -847,6 +1179,8 @@ static int sx126x_lora_recv(const struct device *dev, uint8_t *data_buf, } data->rx_cb = NULL; @@ -616,7 +594,7 @@ index 30243ba5dc7..befb84797eb 100644 k_msgq_purge(&data->rx_msgq); /* Set packet parameters for variable length reception */ -@@ -918,6 +1235,7 @@ static int sx126x_lora_recv_async(const struct device *dev, +@@ -918,6 +1252,7 @@ static int sx126x_lora_recv_async(const struct device *dev, /* Stop async reception */ data->rx_cb = NULL; data->rx_cb_user_data = NULL; @@ -624,7 +602,7 @@ index 30243ba5dc7..befb84797eb 100644 if (atomic_cas(&data->state, SX126X_STATE_RX, SX126X_STATE_IDLE)) { sx126x_set_standby(dev, SX126X_STANDBY_RC); sx126x_set_sleep(dev); -@@ -947,6 +1265,8 @@ static int sx126x_lora_recv_async(const struct device *dev, +@@ -947,6 +1282,8 @@ static int sx126x_lora_recv_async(const struct device *dev, data->rx_cb = cb; data->rx_cb_user_data = user_data; @@ -633,7 +611,7 @@ index 30243ba5dc7..befb84797eb 100644 /* Set packet parameters */ ret = sx126x_set_packet_params(dev, -@@ -959,6 +1279,7 @@ static int sx126x_lora_recv_async(const struct device *dev, +@@ -959,6 +1296,7 @@ static int sx126x_lora_recv_async(const struct device *dev, SX126X_LORA_IQ_INVERTED : SX126X_LORA_IQ_STANDARD); if (ret < 0) { data->rx_cb = NULL; @@ -641,7 +619,7 @@ index 30243ba5dc7..befb84797eb 100644 sx126x_set_sleep(dev); k_mutex_unlock(&data->lock); return ret; -@@ -971,11 +1292,21 @@ static int sx126x_lora_recv_async(const struct device *dev, +@@ -971,11 +1309,21 @@ static int sx126x_lora_recv_async(const struct device *dev, ret = sx126x_set_rx(dev, 0); if (ret < 0) { data->rx_cb = NULL; @@ -663,7 +641,7 @@ index 30243ba5dc7..befb84797eb 100644 k_mutex_unlock(&data->lock); return 0; } -@@ -1051,7 +1382,7 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency, +@@ -1051,7 +1399,7 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency, /* Set PA config and TX power */ ret = sx126x_hal_configure_tx_params(dev, tx_power, frequency, @@ -672,7 +650,7 @@ index 30243ba5dc7..befb84797eb 100644 if (ret < 0) { sx126x_set_sleep(dev); k_mutex_unlock(&data->lock); -@@ -1083,14 +1414,505 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency, +@@ -1083,14 +1431,513 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency, return 0; } @@ -753,7 +731,15 @@ index 30243ba5dc7..befb84797eb 100644 + struct sx126x_data *data = dev->data; + const struct sx126x_hal_config *config = dev->config; + -+ k_mutex_lock(&data->lock, K_FOREVER); ++ /* Bounded wait: a long TX/RX (e.g. SF12 packet ~3 s airtime) holds ++ * data->lock for its full duration. K_FOREVER here would stall the ++ * caller (typically the dispatcher main thread) for that whole time ++ * and freeze packet routing. Instead, give up after 50 ms and let ++ * the caller retry on the next maintenance interval. */ ++ if (k_mutex_lock(&data->lock, K_MSEC(50)) != 0) { ++ LOG_WRN("reset_agc: mutex busy, skipping this cycle"); ++ return; ++ } + + /* Warm sleep — powers down the analog frontend (resets AGC state) + * but preserves register configuration. */ @@ -1185,7 +1171,7 @@ index 30243ba5dc7..befb84797eb 100644 }; #ifdef CONFIG_PM_DEVICE -@@ -1112,6 +1934,7 @@ static int sx126x_pm_action(const struct device *dev, +@@ -1112,6 +1959,7 @@ static int sx126x_pm_action(const struct device *dev, static int sx126x_init(const struct device *dev) { struct sx126x_data *data = dev->data; @@ -1193,7 +1179,7 @@ index 30243ba5dc7..befb84797eb 100644 int ret; /* Initialize data structures */ -@@ -1121,9 +1944,25 @@ static int sx126x_init(const struct device *dev) +@@ -1121,9 +1969,25 @@ static int sx126x_init(const struct device *dev) k_msgq_init(&data->rx_msgq, (char *)&data->rx_result, sizeof(struct sx126x_rx_result), 1); k_work_init(&data->irq_work, sx126x_irq_work_handler);