From 8a3a8e9d1bd5f713912473c360cd8c20af1242e7 Mon Sep 17 00:00:00 2001 From: R0ck Date: Fri, 16 Oct 2026 22:46:12 +0100 Subject: [PATCH 1/2] bridge: a frame the receiver locked onto and lost reaches the chip The engine sends kind 0x0A, [failure][frame...], for a frame this node's receiver locked onto and could not trust. The chip raises HeaderErr for a header it could not read, or RxDone beside CrcErr for a payload it could not, which is what lets the firmware's error paths run at all. --- bridge/main.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bridge/main.cpp b/bridge/main.cpp index 87dc574..dac67cd 100644 --- a/bridge/main.cpp +++ b/bridge/main.cpp @@ -79,6 +79,7 @@ constexpr uint8_t kConsoleIn = 0x06; // bytes typed at the node's UART constexpr uint8_t kConsoleOut = 0x07; // bytes the node printed constexpr uint8_t kChannelBusy = 0x08; // is another station on the air here? constexpr uint8_t kRadioStats = 0x09; // node -> host: what the chip has seen +constexpr uint8_t kFrameFailed = 0x0A; // host -> node: a frame locked onto and lost sock_t gFd = BAD_SOCK; std::deque gConsoleIn; @@ -250,6 +251,14 @@ int main(int argc, char** argv) { sim_hal.chip().inbox.push_back(std::move(payload)); break; + case kFrameFailed: + // The first byte is where it failed, as vsx_deliver_frame_failed numbers + // it: a header the receiver could not read is HeaderErr and nothing + // more, a payload it could not is RxDone beside CrcErr with the corrupt + // bytes in the buffer. Without this the firmware's error paths never run. + if (n >= 1) sim_hal.chip().deliverFrameFailed(payload.data() + 1, n - 1, payload[0]); + break; + case kTxDone: sim_hal.chip().transmitFinished(); break; From 1cc683f227c6fbeb8f47c8a0e562f052de3bd32d Mon Sep 17 00:00:00 2001 From: R0ck Date: Fri, 16 Oct 2026 22:54:05 +0100 Subject: [PATCH 2/2] bridge: say in the radio stats record that it takes a failed frame Byte 47, bit 0. The engine sends kind 0x0A only to a node that says so, because a bridge without that case ends its connection on a kind it does not know and the engine has no other way to tell the two apart. --- bridge/main.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bridge/main.cpp b/bridge/main.cpp index dac67cd..b12caf5 100644 --- a/bridge/main.cpp +++ b/bridge/main.cpp @@ -80,6 +80,7 @@ constexpr uint8_t kConsoleOut = 0x07; // bytes the node printed constexpr uint8_t kChannelBusy = 0x08; // is another station on the air here? constexpr uint8_t kRadioStats = 0x09; // node -> host: what the chip has seen constexpr uint8_t kFrameFailed = 0x0A; // host -> node: a frame locked onto and lost +constexpr uint8_t kCapFrameFailed = 0x01; // stats byte 47: this bridge takes kFrameFailed sock_t gFd = BAD_SOCK; std::deque gConsoleIn; @@ -347,7 +348,7 @@ int main(int argc, char** argv) { // make every comparison between them a comparison of our own code. auto& c = sim_hal.chip(); uint32_t st[4] = {c.irqReads(), c.busyReads(), c.busyMs(), c.spuriousRaises()}; - uint8_t sb[47]; + uint8_t sb[48]; for (int k = 0; k < 4; k++) { sb[k*4+0] = (uint8_t)(st[k] >> 24); sb[k*4+1] = (uint8_t)(st[k] >> 16); sb[k*4+2] = (uint8_t)(st[k] >> 8); sb[k*4+3] = (uint8_t)st[k]; @@ -388,6 +389,11 @@ int main(int argc, char** argv) { // channel, so without this count a firmware that never enabled // PREAMBLE_DETECTED and one that never heard a preamble look alike. put32(43, c.irqSuppressed()); + // What this bridge takes that an older one would hang up on. The + // engine sends kFrameFailed only to a node that says so here, because + // a bridge without that case ends its connection on a kind it does + // not know, and the engine cannot tell the two apart any other way. + sb[47] = kCapFrameFailed; writeMsg(gFd, kRadioStats, sb, sizeof(sb)); } if (!writeMsg(gFd, kAck, payload.data(), 4)) goto done;