Skip to content

fix(sx126x_hal): add t_woff settle delay to close wake-up BUSY-poll race - #4

Open
Mikefly123 wants to merge 1 commit into
fix/lr-fhss-src-path-flattened-layoutfrom
fix/sx126x-wakeup-busy-race-settle-delay
Open

Mikefly123 wants to merge 1 commit into
fix/lr-fhss-src-path-flattened-layoutfrom
fix/sx126x-wakeup-busy-race-settle-delay

Conversation

@Mikefly123

Copy link
Copy Markdown

What

Inserts a k_busy_wait(500) between the wake-up NSS toggle and the BUSY poll in sx126x_hal_check_device_ready() (drivers/usp/sx126x/sx126x_hal.c).

Why

sx126x_hal_check_device_ready() wakes a sleeping SX126x with a glitch on NSS and immediately polls BUSY. Per the datasheet, the chip needs up to ~340us (t_woff, warm start) after the wake-up NSS edge before it reliably asserts BUSY. Polling immediately can sample BUSY before the chip has driven it, so the caller believes the radio is ready when it is still starting up — the very next SPI command (frequently SET_FREQ) is then clocked into a device that isn't listening yet and is silently dropped. This reproduced ~40% of the time in release builds where the post-wake instruction path is fast enough to win the race.

Validation

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added optional external RF-switch control for SX126x radios through configurable TX and RX GPIOs.
    • RF-switch paths now automatically follow transmit, receive, standby, and sleep states.
    • Added device-tree configuration options for TX and RX RF-switch control.
  • Bug Fixes

    • Improved radio wake-up handling with an initialization delay before status polling.
    • Added error handling when configuring RF-switch GPIOs.

Walkthrough

The SX126x driver now supports optional external RF-switch GPIOs, controls them during mode changes, and adds a wake-up delay. Kconfig removes an incompatible selection. The development environment uses the flattened LR-FHSS source path.

Changes

SX126x RF-switch control

Layer / File(s) Summary
RF-switch configuration and initialization
dts/bindings/usp/semtech,sx126x-new-common.yaml, drivers/usp/sx126x/sx126x_hal_context.h, drivers/usp/sx126x/sx126x_board.c
Device-tree properties and HAL context fields define optional TX/RX GPIOs. Initialization configures them as inactive outputs.
RF-switch mode control
drivers/usp/sx126x/sx126x_hal.c
The HAL selects TX, RX, or inactive GPIO states for mode commands. It deasserts the previous path before asserting the new path and delays BUSY polling after wake-up.

Kconfig compatibility

Layer / File(s) Summary
Kconfig selection update
drivers/usp/Kconfig
The external modem module selection was removed. Comments document the Kconfig generation incompatibility.

LR-FHSS source path

Layer / File(s) Summary
LR-FHSS source path update
modules/usp_drivers/dev_env.cmake
The source path now points directly to LBM_SX126X_LIB_DIR.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant sx126x_hal_write
  participant sx126x_hal_update_rf_switch
  participant RF_Switch_GPIOs
  participant SX126x_SPI
  sx126x_hal_write->>sx126x_hal_update_rf_switch: SX126x mode-change command
  sx126x_hal_update_rf_switch->>RF_Switch_GPIOs: deassert previous path
  sx126x_hal_update_rf_switch->>RF_Switch_GPIOs: assert TX, RX, or inactive path
  sx126x_hal_write->>SX126x_SPI: transmit command
Loading

Poem

A rabbit checks the TX and RX
GPIO paths with careful hops.
Wake the radio, wait a beat,
Then send the command neat.
Kconfig and paths now align—
Carrots for a build that’s fine!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the primary SX126x wake-up BUSY-poll race fix.
Description check ✅ Passed The description explains the wake-up race, the 500 µs delay, its cause, impact, and hardware validation.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…close BUSY-poll race

sx126x_hal_check_device_ready() wakes a sleeping SX126x with a glitch on
NSS and then immediately polls BUSY via sx126x_hal_wait_on_busy(). Per
the datasheet, the chip needs up to ~340us (t_woff, warm start) after
the wake-up NSS edge before it reliably asserts BUSY. Polling right away
can sample BUSY before the chip has driven it, so the caller believes
the radio is ready when it is still starting up; the very next SPI
command (frequently SET_FREQ) is then clocked into a device that isn't
listening yet and is silently dropped. This reproduces ~40% of the time
in release builds where the post-wake instruction path is fast enough
to win the race.

Fix: insert a k_busy_wait(500) between the wake-up NSS toggle and the
BUSY poll to wait out the chip's startup window before trusting BUSY.
@Mikefly123
Mikefly123 force-pushed the fix/sx126x-wakeup-busy-race-settle-delay branch from 1b9ad75 to 31eeaec Compare July 31, 2026 03:01
@Mikefly123
Mikefly123 changed the base branch from main to fix/lr-fhss-src-path-flattened-layout July 31, 2026 03:01
@Mikefly123

Copy link
Copy Markdown
Author

Stack position 4/6 of stack Lora-net#8: base #3. Next: #5.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@drivers/usp/sx126x/sx126x_board.c`:
- Around line 242-261: In sx126x_init, add validation before configuring the
external RF-switch GPIOs to reject any configured tx_enable or rx_enable
together with dio2_as_rf_switch, returning -EINVAL. Preserve the existing
dio2-gpios/dio2_as_rf_switch validation and normal GPIO configuration for valid
combinations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 82e5f199-16ca-411e-ab02-d6ec1e75bc5f

📥 Commits

Reviewing files that changed from the base of the PR and between bfacd43 and 31eeaec.

📒 Files selected for processing (6)
  • drivers/usp/Kconfig
  • drivers/usp/sx126x/sx126x_board.c
  • drivers/usp/sx126x/sx126x_hal.c
  • drivers/usp/sx126x/sx126x_hal_context.h
  • dts/bindings/usp/semtech,sx126x-new-common.yaml
  • modules/usp_drivers/dev_env.cmake

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@drivers/usp/sx126x/sx126x_board.c`:
- Around line 242-261: In sx126x_init, add validation before configuring the
external RF-switch GPIOs to reject any configured tx_enable or rx_enable
together with dio2_as_rf_switch, returning -EINVAL. Preserve the existing
dio2-gpios/dio2_as_rf_switch validation and normal GPIO configuration for valid
combinations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 82e5f199-16ca-411e-ab02-d6ec1e75bc5f

📥 Commits

Reviewing files that changed from the base of the PR and between bfacd43 and 31eeaec.

📒 Files selected for processing (6)
  • drivers/usp/Kconfig
  • drivers/usp/sx126x/sx126x_board.c
  • drivers/usp/sx126x/sx126x_hal.c
  • drivers/usp/sx126x/sx126x_hal_context.h
  • dts/bindings/usp/semtech,sx126x-new-common.yaml
  • modules/usp_drivers/dev_env.cmake
🛑 Comments failed to post (1)
drivers/usp/sx126x/sx126x_board.c (1)

242-261: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Search for any existing mutual-exclusion check between dio2_as_rf_switch and tx_enable/rx_enable.
rg -n -C5 'dio2_as_rf_switch' drivers/usp/sx126x/sx126x_board.c
rg -n -C5 'tx_enable\.port|rx_enable\.port' drivers/usp/sx126x/sx126x_board.c

Repository: Open-Source-Space-Foundation/usp_zephyr

Length of output: 2737


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== sx126x_board.c outline =="
ast-grep outline drivers/usp/sx126x/sx126x_board.c --view compact || true

echo
echo "== sx126x_board.c relevant init/macros =="
sed -n '220,275p;330,395p;1,80p' drivers/usp/sx126x/sx126x_board.c

echo
echo "== search for dio2_as_rf_switch and rf-switch properties in repo =="
rg -n -C4 'dio2_as_rf_switch|tx_enable_gpios|rx_enable_gpios|dio2-as-rf-switch|tx-enable-gpios|rx-enable-gpios' drivers/usp/sx126x .devicetree.dtsi include drivers 2>/dev/null | head -n 240

echo
echo "== read-only behavioral probe for current static guard logic =="
python3 - <<'PY'
from pathlib import Path
text = Path('drivers/usp/sx126x/sx126x_board.c').read_text()
for needle in [
    'dio2_as_rf_switch && config->tx_enable.port',
    'dio2_as_rf_switch && config->rx_enable.port',
    'dio2_as_rf_switch && ( config->tx_enable.port || config->rx_enable.port )',
    'tx-enable-gpios cannot be combined',
    'rx-enable-gpios cannot be combined',
]:
    print(f'{needle}:', needle in text)
print('dio2-gpios only conflict macro:', 'DIO2_CONFLICT' in text and 'dio2-gpios and dio2-as-rf-switch are conflicting' in text)
PY

Repository: Open-Source-Space-Foundation/usp_zephyr

Length of output: 19546


Enforce dio2-as-rf-switch / tx-enable-rx-enable exclusion at init.

sx126x_board.c already rejects dio2-gpios plus dio2-as-rf-switch, but it still accepts tx-enable-gpios/rx-enable-gpios together with dio2-as-rf-switch. This configures external switch GPIOs while dio2_as_rf_switch remains enabled, contrary to the documented hardware mutual exclusion. Reject this invalid DT combination in sx126x_init with -EINVAL.

🛡️ Proposed validation
     /* External RF-switch GPIOs — configure as outputs, inactive (both paths off) */
+    if( config->dio2_as_rf_switch && ( config->tx_enable.port || config->rx_enable.port ) )
+    {
+        LOG_ERR( "dio2-as-rf-switch cannot be combined with tx-enable-gpios/rx-enable-gpios" );
+        return -EINVAL;
+    }
+
     if( config->tx_enable.port )
     {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    /* External RF-switch GPIOs — configure as outputs, inactive (both paths off) */
    if( config->dio2_as_rf_switch && ( config->tx_enable.port || config->rx_enable.port ) )
    {
        LOG_ERR( "dio2-as-rf-switch cannot be combined with tx-enable-gpios/rx-enable-gpios" );
        return -EINVAL;
    }

    if( config->tx_enable.port )
    {
        ret = gpio_pin_configure_dt( &config->tx_enable, GPIO_OUTPUT_INACTIVE );
        if( ret < 0 )
        {
            LOG_ERR( "Could not configure tx-enable gpio" );
            return ret;
        }
    }
    if( config->rx_enable.port )
    {
        ret = gpio_pin_configure_dt( &config->rx_enable, GPIO_OUTPUT_INACTIVE );
        if( ret < 0 )
        {
            LOG_ERR( "Could not configure rx-enable gpio" );
            return ret;
        }
    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@drivers/usp/sx126x/sx126x_board.c` around lines 242 - 261, In sx126x_init,
add validation before configuring the external RF-switch GPIOs to reject any
configured tx_enable or rx_enable together with dio2_as_rf_switch, returning
-EINVAL. Preserve the existing dio2-gpios/dio2_as_rf_switch validation and
normal GPIO configuration for valid combinations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant