Files
unleashed-firmware/applications/drivers
8009e81350 SubGHz: survive an external CC1101 module unplugged mid-session (#1139)
* SubGHz: survive an external module unplugged mid-session

The external CC1101 was probed once, in subghz_txrx_alloc(), and the
result latched for the life of the app. Unplug it and enter Read, and the
app drove a chip that was not there: subghz_txrx_rx_start() ->
subghz_txrx_begin() -> ... -> subghz_device_cc1101_ext_rx() waiting for
CC1101StateRX, which never arrives, and the furi_check around it took the
firmware down.

App side, the probe now happens where the radio is about to be used
instead of once at startup. subghz_txrx_radio_device_set() tears the
module down first and honours subghz_devices_begin()'s return value - the
self-test that actually distinguishes a present module from a floating
header, and previously discarded - so it returns what came up rather than
what was asked for. subghz_txrx_radio_device_poll() re-checks before every
RX and TX start, on every hop (hopping skips begin()) and on the Sub-GHz
menu, falling back to the internal radio and picking the external one back
up once it is plugged in again. A missing module reads back as a steady
-74 dBm rather than as nothing, so Read would otherwise show a receiver
that looks healthier than an idle one; poll_active() catches that from the
receiver tick and restarts RX on whatever answered.

Driver side, the three furi_check(cc1101_wait_status_state(...)) calls now
log and carry on. A hot-pluggable peripheral being absent is an I/O
condition, not an invariant violation - the internal radio keeps its
furi_check, since that chip is soldered on and there is nothing to fall
back to. This is also the only part of the fix that reaches the other
consumers of the driver (CLI, JS, subghz_remote, subghz_tx_rx_worker),
none of which re-probe. Alongside it:

- set_frequency() waited for CC1101StateIDLE in an unbounded while(true)
  while holding the SPI bus; the hopper reaches it every hop, so a module
  that stopped answering was a watchdog reset waiting to happen
- is_connect() tested PARTNUM, which a genuine CC1101 reports as 0x00 -
  the same as an empty bus. It reads VERSION now, which is what makes the
  cheap liveness check on the receiver tick possible
- the E07 amp is no longer keyed without confirmation that the chip
  reached TX, and is still dropped on the IDLE path when the transition
  times out

Known gaps, deliberately left: SubGhz Remote keeps its own copy of the
txrx helper and gets only the driver half of this; TX failing because the
module is gone still reports "Transmission is blocked / Frequency is
outside of default range", which needs its own error state.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* CHANGELOG: external SubGHz module hot-unplug

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* apply review fixes

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: MX <10697207+xMasterX@users.noreply.github.com>
2026-09-10 01:59:20 +03:00
..