fix(companion): recover radio profile settings after flash
This commit is contained in:
1 parent
d1c18edd88
commit
e7160dfd26
7 files changed
+139
-68
No files matched your search
+55
-5
@@ -254,9 +254,16 @@ int main(int argc, char** argv) {
|
||||
restored.loop();
|
||||
assert(radio.p.reply_tx==mesh::RADIO_TX_BOTH && !radio.p.reply_force);
|
||||
assert(!radio.p.enabled());
|
||||
assert(restored.handle("set tx.reply radio",f.reply));
|
||||
assert(strstr(f.reply,"Error"));
|
||||
assert(f.fs.files["/radio_profiles"]==damaged);
|
||||
assert(restored.handle("set radio2.cross on",f.reply));
|
||||
assert(!strcmp(f.reply,"OK"));
|
||||
assert(radio.p.cross==mesh::RadioCrossMode::On);
|
||||
assert(restored.handle("set tempradio2 911.3,500,8,7,rxtx,2",f.reply));
|
||||
assert(!strncmp(f.reply,"OK",2));
|
||||
g_mock_millis+=2000; restored.loop();
|
||||
assert(radio.p.secondary_temporary && radio.p.canCross());
|
||||
Radio reboot; mesh::RadioProfileCLI repaired;
|
||||
repaired.begin(&f.fs,&reboot,&f.clock,true);
|
||||
assert(reboot.p.cross==mesh::RadioCrossMode::On);
|
||||
}
|
||||
{
|
||||
Fixture companion;
|
||||
@@ -469,8 +476,51 @@ int main(int argc, char** argv) {
|
||||
Radio corrupt; mesh::RadioProfileCLI invalid;
|
||||
invalid.begin(&f.fs, &corrupt, &f.clock);
|
||||
assert(!corrupt.p.enabled());
|
||||
assert(invalid.handle("set radio2 off", f.reply));
|
||||
assert(strstr(f.reply,"Error")); // newer/corrupt image is not overwritten
|
||||
const auto damaged=f.fs.files["/radio_profiles"];
|
||||
f.fs.fail_remove=true;
|
||||
assert(invalid.handle("set radio2.cross on", f.reply));
|
||||
assert(strstr(f.reply,"Error")); // failed cleanup never overwrites corruption
|
||||
assert(f.fs.files["/radio_profiles"]==damaged);
|
||||
f.fs.fail_remove=false;
|
||||
assert(invalid.handle("set radio2.cross on", f.reply));
|
||||
assert(!strcmp(f.reply,"OK"));
|
||||
assert(corrupt.p.cross==mesh::RadioCrossMode::On);
|
||||
assert(invalid.handle("set tempradio2 911.3,500,8,7,rxtx,2", f.reply));
|
||||
assert(!strncmp(f.reply,"OK",2));
|
||||
g_mock_millis+=2000; invalid.loop();
|
||||
assert(corrupt.p.secondary_temporary && corrupt.p.canCross());
|
||||
}
|
||||
{
|
||||
Fixture f;
|
||||
f.cmd("set radio2 910.5,500,8,5,rxtx,80");
|
||||
const auto saved=f.fs.files["/radio_profiles"];
|
||||
f.fs.fail_read_open=true;
|
||||
Radio radio; mesh::RadioProfileCLI unavailable;
|
||||
unavailable.begin(&f.fs, &radio, &f.clock);
|
||||
f.fs.fail_read_open=false;
|
||||
assert(unavailable.handle("set radio2.cross on", f.reply));
|
||||
assert(strstr(f.reply,"Error")); // an I/O fault is never treated as corruption
|
||||
assert(f.fs.files["/radio_profiles"]==saved);
|
||||
}
|
||||
{
|
||||
Fixture f;
|
||||
f.cmd("set radio2 910.5,500,8,5,rxtx,80");
|
||||
const auto backup=f.fs.files["/radio_profiles"];
|
||||
f.fs.files["/radio_profiles.bak"]=backup;
|
||||
// This versioned image has a valid CRC but an unsupported saved mode.
|
||||
// An intact backup must prevent automatic discard/recreation.
|
||||
f.fs.files["/radio_profiles"][3]=3;
|
||||
uint32_t crc=0xffffffffU;
|
||||
for (size_t i=0;i<20;++i) {
|
||||
crc ^= f.fs.files["/radio_profiles"][i];
|
||||
for (unsigned bit=0;bit<8;++bit) crc=(crc>>1)^((crc&1)?0xedb88320U:0);
|
||||
}
|
||||
memcpy(f.fs.files["/radio_profiles"].data()+20,&crc,4);
|
||||
Radio radio; mesh::RadioProfileCLI protected_backup;
|
||||
protected_backup.begin(&f.fs, &radio, &f.clock);
|
||||
assert(protected_backup.handle("set radio2.cross on", f.reply));
|
||||
assert(strstr(f.reply,"Error"));
|
||||
assert(f.fs.files["/radio_profiles.bak"]==backup);
|
||||
}
|
||||
{
|
||||
Fixture f;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run the actual task wrapper and BLE startup with injected allocation failures."""
|
||||
"""Run the actual task wrapper and BLE startup failure handling."""
|
||||
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
@@ -29,7 +29,6 @@ HARNESS = r'''
|
||||
#include <cstring>
|
||||
#include <helpers/BluetoothMac.h>
|
||||
#include <helpers/BleMotaProtocol.h>
|
||||
#include <helpers/nrf52/BleTaskStartup.h>
|
||||
#ifndef COMPANION_FEATURE_BLE_MOTA_SOURCE
|
||||
#define COMPANION_FEATURE_BLE_MOTA_SOURCE 1
|
||||
#endif
|
||||
@@ -53,7 +52,6 @@ struct Logging { void println(const char*) {} };
|
||||
Logging& usbLoggingPort() { static Logging port; return port; }
|
||||
}
|
||||
|
||||
static const char* fail_task = nullptr;
|
||||
static int fail_service = 0;
|
||||
static bool fail_softdevice = false;
|
||||
static unsigned last_depth;
|
||||
@@ -62,9 +60,8 @@ extern "C" BaseType_t __wrap_xTaskCreate(TaskFunction_t, const char*,
|
||||
extern "C" BaseType_t __real_xTaskCreate(TaskFunction_t, const char* name,
|
||||
configSTACK_DEPTH_TYPE depth, void*, UBaseType_t, TaskHandle_t* out) {
|
||||
last_depth = depth;
|
||||
bool fail = fail_task && name && strcmp(name, fail_task) == 0;
|
||||
if (out) *out = fail ? nullptr : reinterpret_cast<void*>(1);
|
||||
return fail ? 0 : pdPASS;
|
||||
if (out) *out = reinterpret_cast<void*>(1);
|
||||
return pdPASS;
|
||||
}
|
||||
struct Settings {
|
||||
template<class... T> void clearBonds(T...) {}
|
||||
@@ -93,7 +90,8 @@ struct BluefruitStub {
|
||||
++starts;
|
||||
if (fail_softdevice) return false;
|
||||
TaskHandle_t task;
|
||||
// Match the pinned core: it ignores both worker creation return values.
|
||||
// The pinned LTO core resolves these internal calls directly, rather than
|
||||
// through the application's linker wrapper.
|
||||
__wrap_xTaskCreate(nullptr, "BLE", 1280, nullptr, 3, &task);
|
||||
__wrap_xTaskCreate(nullptr, "SOC", 200, nullptr, 3, &task);
|
||||
return true;
|
||||
@@ -142,21 +140,18 @@ struct SerialBLEInterface {
|
||||
static SerialBLEInterface* instance = nullptr;
|
||||
@BEGIN@
|
||||
int main() {
|
||||
mesh::nrf52::resetBleTaskStartup();
|
||||
assert(!mesh::nrf52::bleTasksStarted());
|
||||
__wrap_xTaskCreate(nullptr, "loop", 1024, nullptr, 1, nullptr);
|
||||
assert(last_depth == 2048 && !mesh::nrf52::bleTasksStarted());
|
||||
assert(last_depth == 2048);
|
||||
__wrap_xTaskCreate(nullptr, "callback", 768, nullptr, 1, nullptr);
|
||||
assert(last_depth == 768);
|
||||
__wrap_xTaskCreate(nullptr, nullptr, 100, nullptr, 1, nullptr);
|
||||
assert(last_depth == 100);
|
||||
for (int fault = 0; fault <= 8; ++fault) {
|
||||
for (int fault = 0; fault <= 6; ++fault) {
|
||||
#if !COMPANION_FEATURE_BLE_MOTA_SOURCE
|
||||
if (fault >= 5 && fault <= 7) continue;
|
||||
if (fault >= 3 && fault <= 5) continue;
|
||||
#endif
|
||||
fail_softdevice = fault == 1;
|
||||
fail_task = fault == 2 ? "BLE" : fault == 3 ? "SOC" : nullptr;
|
||||
fail_service = fault >= 4 ? fault - 3 : 0;
|
||||
fail_service = fault >= 2 ? fault - 1 : 0;
|
||||
SerialBLEInterface port;
|
||||
const int starts = Bluefruit.starts;
|
||||
const bool expected = fault == 0;
|
||||
@@ -174,7 +169,7 @@ int main() {
|
||||
|
||||
|
||||
class Nrf52BleStartupTest(unittest.TestCase):
|
||||
def test_all_startup_failures_are_reported_without_reinitializing(self):
|
||||
def test_reported_startup_failures_are_not_reinitialized(self):
|
||||
begin = method((ROOT / "src/helpers/nrf52/SerialBLEInterface.cpp").read_text(),
|
||||
"bool SerialBLEInterface::begin(")
|
||||
header = (ROOT / "src/helpers/nrf52/SerialBLEInterface.h").read_text()
|
||||
|
||||
Reference in new issue
Block a user