fix(radio): guarantee TX_DONE/TX_FAILED reply on every CmdTx via Transmitting exit action

Add exit: respond_tx_failed on the Transmitting super-state. Per hsmc spec
§6, exit actions fire on every departure from a state, including the
Operational-level on(WedgeDetected) => Recovering transition that bypasses
Transmitting's own on(TransmitFailed) => respond_tx_failed handler.

respond_tx_failed is a clean no-op when last_tx_tag is None (the
normal-completion path consumed it via .take()), so this is a safe
unconditional exit action. Net effect: every CmdTx gets exactly one
tag-correlated TX_DONE/TX_FAILED/CHANNEL_BUSY reply no matter how
Transmitting is exited. The host (mux/ai-bot) stops eating 2s firmware-
timeouts on every CAD wedge mid-TX.
This commit is contained in:
swaits committed 2026-04-28 10:59:28 -06:00
1 parent 276f941fbb
commit e5aeb3896a
1 file changed
+13
+13
View File
@@ -418,6 +418,19 @@ statechart! {
state Transmitting {
entry: announce_entered_tx;
// Per hsmc README rule §6, exit actions fire on every
// departure from Transmitting — including the
// Operational-level `on(WedgeDetected) => Recovering`
// transition that bypasses Transmitting's own
// `on(TransmitFailed) => respond_tx_failed` handler.
// Without this the host never gets a TX_DONE/TX_FAILED
// reply on a wedge mid-TX and waits its 2 s firmware-
// timeout. `respond_tx_failed` no-ops if `last_tx_tag`
// is None (the normal-completion path consumed it via
// `take()`), so it's a clean tag-correlated reply
// GUARANTEE: every CmdTx gets a response no matter
// how Transmitting is exited.
exit: respond_tx_failed;
exit: announce_packet_tx;
default(TransmittingFromIdle);