Skip to content

refactor(rx): migrate rx_spi.c to extDevice_t/DMA-aware transfers - #1390

Draft
nerdCopter wants to merge 13 commits into
emuflight:masterfrom
nerdCopter:refactor/rx-spi-dma-migration
Draft

nerdCopter wants to merge 13 commits into
emuflight:masterfrom
nerdCopter:refactor/rx-spi-dma-migration

Conversation

@nerdCopter

@nerdCopter nerdCopter commented Aug 15, 2026 •

Copy link
Copy Markdown
Member

AI Generated pull-request

Summary

drivers/rx/rx_spi.c called spiTransferByte() directly against the raw SPI
instance, bypassing dev/segment/DMA handling entirely. BF 4.5-maintenance
and master both route this through extDevice_t + spiReadWrite()/spiWrite()/
spiWriteReg()/spiWriteRegBuf()/spiReadReg()/spiReadRegBuf().

rxSpiDeviceInit() gains two calls required for correctness on the new
per-device path: spiSetClkPhasePolarity(dev, true) (the field
zero-initializes to Mode 3, wrong for nRF24L01/CC2500/A7105) and
spiSetClkDivisor(dev, SPI_CLOCK_STANDARD) (replaces the old instance-based
spiSetDivisor() call; the field zero-initializes to an undefined divisor).

Write-family functions (rxSpiWriteByte/WriteCommand/WriteCommandMulti/
ReadCommandMulti and their callers in rx_nrf24l01.c/rx_cc2500.c/
rx_xn297.c) change uint8_t -> void to match BF's current signatures.
Every call site was traced up through callers-of-callers across the tree
before changing — the discarded status byte is never read anywhere in EF
or BF.

Scope: this PR is the DMA-plumbing migration only. BF's EXTI
interrupt-driven frame-detection feature is deliberately deferred to a
separate follow-up (#1391).

Closes part of #1366.

Hardware test result: FAILED — real, reproducible regression, root cause not yet confirmed

First hardware flash on NBDHMBF4PRO (real board, FrSky/CC2500) reproduced a severe,
deterministic failure: connecting shows normal sensor data, but entering CLI mode causes a
total board lockup — zero CLI output ever (not even the static entry banner), MSP requests
time out indefinitely, and the entire scheduler (confirmed via a raw SysTick-ISR-driven
heartbeat, independent of the normal task loop) stops. Reflash-verified deterministic
(identical result twice on the same commit); confirmed NOT the board's known,
already-documented CC2500 solder-defect (current master works fine on this same hardware).

A fault-handler redirect test ruled out a normal caught/handled fault — no distinctive
blink pattern ever appears, meaning the CPU likely locks up before even entering the fault
handler (consistent with memory corruption, most plausibly a stack overflow, though
unconfirmed). Root-causing this further requires a hardware debugger (SWD/JTAG), which is
not currently available.

BF-parity check: this migration's direction is validated by BF's own fleet — BF ships
this exact combination (F411 + CC2500/FrSky + extDevice_t) on real, popular boards (e.g.
CRAZYBEEF4FR), so the architecture itself is not the problem. That comparison did surface
one real, concrete gap: BF's shipped board wires interrupt-driven GDO detection
(RX_SPI_EXTI_PIN → rxSpiExtiConfigured()/rxSpiExtiInit()), while EF's rx/rx_spi.c has
no such mechanism at all — cc2500getGdo() still polls (already tracked as #1391, previously
scoped as a separate follow-up, not yet confirmed to be the root cause of this specific
lockup).

This PR is not safe to merge or un-draft pending further root-causing with a debugger.
Full investigation trace: refactor/rx-spi-dma-migration/CONTEXT_rx-spi-dma-migration.md in
the AI docs tree.

Test plan

  • Build passes: make test (unit suite, unchanged), NBDHMBF4PRO,
    CRAZYBEEF4FS, NUCLEOF446RE, SITL — 0 warnings
  • Hardware tested: NBDHMBF4PRO + FrSky (CC2500) — FAILED, CLI-entry lockup, see above

drivers/rx/rx_spi.c called spiTransferByte() directly against the raw SPI
instance, bypassing dev/segment/DMA handling entirely. BF 4.5-maintenance
and master both route this through extDevice_t + spiReadWrite()/spiWrite()/
spiWriteReg()/spiWriteRegBuf()/spiReadReg()/spiReadRegBuf().

rxSpiDeviceInit() gains two calls required for correctness on the new
per-device path, not optional: spiSetClkPhasePolarity(dev, true) (the field
zero-initializes to Mode 3, wrong for nRF24L01/CC2500/A7105) and
spiSetClkDivisor(dev, SPI_CLOCK_STANDARD) (replaces the old instance-based
spiSetDivisor() call; the field zero-initializes to an undefined divisor).

Write-family functions (rxSpiWriteByte/WriteCommand/WriteCommandMulti/
ReadCommandMulti and their callers in rx_nrf24l01.c/rx_cc2500.c/rx_xn297.c)
change uint8_t -> void to match BF's current signatures. Verified every
call site up through callers-of-callers across the tree before changing:
the discarded status byte is never read anywhere in EF or BF.

Closes part of emuflight#1366 (DMA-plumbing stage only; EXTI interrupt-driven frame
detection deferred to a follow-up).
@coderabbitai

coderabbitai Bot commented Aug 15, 2026 •

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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.

@nerdCopter nerdCopter added the do-not-merge hold from merging until fixed or otherwise label Sep 5, 2026

This branch has not been deployed

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

Labels

do-not-merge hold from merging until fixed or otherwise

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant