Share bounded STM32 Companion preference wire metadata
This commit is contained in:
1 parent
38563a9828
commit
46df653dbc
7 files changed
+614
-21
No files matched your search
@@ -2,6 +2,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <initializer_list>
|
||||
#include "DataStore.h"
|
||||
#include "PrefsStorageLayout.h"
|
||||
#include <helpers/FileRead.h>
|
||||
#include <helpers/AdvertDataHelpers.h>
|
||||
#if defined(ESP32_PLATFORM) || defined(RP2040_PLATFORM)
|
||||
@@ -1094,6 +1095,19 @@ bool DataStore::loadPrefsInt(const char *filename,
|
||||
return false;
|
||||
}
|
||||
bool success = true;
|
||||
#if defined(STM32_PLATFORM) && defined(__GNUC__)
|
||||
using namespace mesh::companion_prefs;
|
||||
uint8_t pad[4];
|
||||
for (size_t i = 0; i < FIELD_COUNT; ++i) {
|
||||
const PrefField& field = PREF_FIELDS[i];
|
||||
if (i >= MANDATORY_FIELDS && file.available() == 0) continue;
|
||||
// Only this normally constructed, mutable snapshot is a destination.
|
||||
void* destination = const_cast<void*>(prefValue(field, loaded_prefs,
|
||||
loaded_lat, loaded_lon, pad));
|
||||
if (success && file.read(static_cast<uint8_t*>(destination), field.length)
|
||||
!= field.length) success = false;
|
||||
}
|
||||
#else
|
||||
auto readField = [&file, &success](void* dest, size_t size) -> bool {
|
||||
if (!success
|
||||
|| file.read(static_cast<uint8_t*>(dest), size) != size) {
|
||||
@@ -1226,6 +1240,7 @@ bool DataStore::loadPrefsInt(const char *filename,
|
||||
sizeof(loaded_prefs.gps_sync_interval_hours));
|
||||
readOptionalField(&loaded_prefs.usb_debug_enabled,
|
||||
sizeof(loaded_prefs.usb_debug_enabled));
|
||||
#endif
|
||||
|
||||
// Any bytes left over form only part of a historically appended field.
|
||||
// Preserve the file and defaults rather than treating that tail as EOF.
|
||||
@@ -1260,6 +1275,24 @@ bool DataStore::savePrefs(const CompanionNodePrefs& _prefs, double node_lat, dou
|
||||
File file = openWrite(_fs, "/new_prefs");
|
||||
#endif
|
||||
if (file) {
|
||||
#if defined(STM32_PLATFORM) && defined(__GNUC__)
|
||||
using namespace mesh::companion_prefs;
|
||||
uint8_t image[235];
|
||||
const uint8_t pad[4] = {};
|
||||
size_t used = 0;
|
||||
for (const PrefField& field : PREF_FIELDS) {
|
||||
if (field.length > sizeof(image) - used) {
|
||||
// Latch failure and stop before any out-of-bounds pointer arithmetic.
|
||||
used = sizeof(image) + 1;
|
||||
break;
|
||||
}
|
||||
memcpy(image + used, prefValue(field, _prefs, node_lat, node_lon, pad),
|
||||
field.length);
|
||||
used += field.length;
|
||||
}
|
||||
bool success = used == sizeof(image)
|
||||
&& file.write(image, sizeof(image)) == sizeof(image);
|
||||
#else
|
||||
#if defined(STM32_PLATFORM)
|
||||
// Keep the explicit wire field list, but make one bounded atomic write.
|
||||
// Other platforms retain direct writes and their existing stack footprint.
|
||||
@@ -1406,6 +1439,7 @@ bool DataStore::savePrefs(const CompanionNodePrefs& _prefs, double node_lat, dou
|
||||
success = success && image_length == sizeof(image)
|
||||
&& file.write(image, sizeof(image)) == sizeof(image);
|
||||
#endif
|
||||
#endif
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) || defined(ESP32_PLATFORM) || defined(RP2040_PLATFORM)
|
||||
success = file.commit(success);
|
||||
if (!success) MESH_DEBUG_PRINTLN("DataStore: atomic preferences write failed");
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(STM32_PLATFORM) && defined(__GNUC__)
|
||||
#include "NodePrefs.h"
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(TBEAM_1W) || defined(RP2040_PLATFORM)
|
||||
#error "The compact STM32 preference table needs an explicit board layout"
|
||||
#endif
|
||||
|
||||
namespace mesh {
|
||||
namespace companion_prefs {
|
||||
|
||||
struct PrefField { uint8_t offset, length; };
|
||||
enum : uint8_t { PrefsLat = 253, PrefsLon = 254, PrefsPad = 255 };
|
||||
static constexpr size_t MANDATORY_FIELDS = 21;
|
||||
|
||||
// GNU supports offsetof for this adapter-bearing, non-standard-layout class.
|
||||
// Use it only on the STM32 GNU ABI, and verify every individual value field;
|
||||
// neither the class padding nor either runtime adapter is a wire image.
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||
template<typename Value, size_t Offset, size_t Length>
|
||||
constexpr PrefField prefField() {
|
||||
static_assert(std::is_trivially_copyable<Value>::value,
|
||||
"only scalar/array preferences are persisted");
|
||||
static_assert(sizeof(Value) == Length, "published preference size");
|
||||
static_assert(Offset < PrefsLat && Length <= UINT8_MAX,
|
||||
"compact preference field bounds");
|
||||
static_assert(Offset + Length <=
|
||||
__builtin_offsetof(CompanionNodePrefs, usb_debug_enabled)
|
||||
+ sizeof(CompanionNodePrefs::usb_debug_enabled),
|
||||
"preference field stays before runtime adapters");
|
||||
static_assert(Offset + Length <= sizeof(CompanionNodePrefs),
|
||||
"preference field stays inside its object");
|
||||
return {uint8_t(Offset), uint8_t(Length)};
|
||||
}
|
||||
template<uint8_t Source, size_t Length>
|
||||
constexpr PrefField prefSpecial() {
|
||||
static_assert(Source == PrefsLat || Source == PrefsLon || Source == PrefsPad,
|
||||
"known auxiliary preference source");
|
||||
static_assert(Source == PrefsPad ? Length <= 4 : Length == sizeof(double),
|
||||
"coordinate/padding source bounds");
|
||||
return {Source, uint8_t(Length)};
|
||||
}
|
||||
#define PREF_FIELD(name, length) prefField<decltype(CompanionNodePrefs::name), \
|
||||
__builtin_offsetof(CompanionNodePrefs, name), length>()
|
||||
// Explicit published wire order, deliberately independent of member order.
|
||||
static constexpr PrefField PREF_FIELDS[] = {
|
||||
PREF_FIELD(airtime_factor, 4),
|
||||
PREF_FIELD(node_name, 32),
|
||||
prefSpecial<PrefsPad, 4>(),
|
||||
prefSpecial<PrefsLat, 8>(),
|
||||
prefSpecial<PrefsLon, 8>(),
|
||||
PREF_FIELD(freq, 4),
|
||||
PREF_FIELD(sf, 1),
|
||||
PREF_FIELD(cr, 1),
|
||||
PREF_FIELD(client_repeat, 1),
|
||||
PREF_FIELD(manual_add_contacts, 1),
|
||||
PREF_FIELD(bw, 4),
|
||||
PREF_FIELD(tx_power_dbm, 1),
|
||||
PREF_FIELD(telemetry_mode_base, 1),
|
||||
PREF_FIELD(telemetry_mode_loc, 1),
|
||||
PREF_FIELD(telemetry_mode_env, 1),
|
||||
PREF_FIELD(rx_delay_base, 4),
|
||||
PREF_FIELD(advert_loc_policy, 1),
|
||||
PREF_FIELD(multi_acks, 1),
|
||||
PREF_FIELD(path_hash_mode, 1),
|
||||
prefSpecial<PrefsPad, 1>(),
|
||||
PREF_FIELD(ble_pin, 4),
|
||||
PREF_FIELD(buzzer_quiet, 1),
|
||||
PREF_FIELD(gps_enabled, 1),
|
||||
PREF_FIELD(gps_interval, 4),
|
||||
PREF_FIELD(autoadd_config, 1),
|
||||
PREF_FIELD(autoadd_max_hops, 1),
|
||||
PREF_FIELD(rx_boosted_gain, 1),
|
||||
PREF_FIELD(default_scope_name, 31),
|
||||
PREF_FIELD(default_scope_key, 16),
|
||||
PREF_FIELD(radio_fem_rxgain, 1),
|
||||
PREF_FIELD(radio_fem_rxgain_override, 1),
|
||||
PREF_FIELD(vibe_quiet, 1),
|
||||
PREF_FIELD(radio_fem_txgain, 1),
|
||||
PREF_FIELD(rx_powersaving_enabled, 1),
|
||||
PREF_FIELD(rx_ps_rx_us, 4),
|
||||
PREF_FIELD(rx_ps_sleep_us, 4),
|
||||
PREF_FIELD(rx_ps_level, 1),
|
||||
PREF_FIELD(rx_ps_preamble, 1),
|
||||
PREF_FIELD(powersaving_enabled, 1),
|
||||
PREF_FIELD(wifi_enabled, 1),
|
||||
PREF_FIELD(powersaving_policy_version, 1),
|
||||
PREF_FIELD(usb_logging_enabled, 1),
|
||||
PREF_FIELD(bluetooth_name, 32),
|
||||
PREF_FIELD(display_rotation_degrees, 2),
|
||||
PREF_FIELD(cad_enabled, 1),
|
||||
PREF_FIELD(cad_scan_timeout_ms, 2),
|
||||
PREF_FIELD(cad_retry_delay_ms, 2),
|
||||
PREF_FIELD(cad_max_duration_ms, 2),
|
||||
PREF_FIELD(bluetooth_mac_mode, 1),
|
||||
PREF_FIELD(bluetooth_mac, 6),
|
||||
PREF_FIELD(bluetooth_stealth_peer_type, 1),
|
||||
PREF_FIELD(bluetooth_stealth_peer, 6),
|
||||
PREF_FIELD(bluetooth_stealth_mode, 1),
|
||||
PREF_FIELD(tx_delay_factor, 4),
|
||||
PREF_FIELD(direct_tx_delay_factor, 4),
|
||||
PREF_FIELD(interference_threshold, 1),
|
||||
PREF_FIELD(agc_reset_interval, 1),
|
||||
PREF_FIELD(tz_offset, 1),
|
||||
PREF_FIELD(flood_retry_attempts, 1),
|
||||
PREF_FIELD(flood_retry_max_path, 1),
|
||||
PREF_FIELD(flood_retry_group_max_path, 1),
|
||||
PREF_FIELD(flood_retry_advert_enabled, 1),
|
||||
PREF_FIELD(one_key_dm_enabled, 1),
|
||||
PREF_FIELD(bluetooth_enabled, 1),
|
||||
PREF_FIELD(gps_sync_interval_hours, 2),
|
||||
PREF_FIELD(usb_debug_enabled, 1),
|
||||
};
|
||||
#undef PREF_FIELD
|
||||
#pragma GCC diagnostic pop
|
||||
static constexpr size_t FIELD_COUNT = sizeof(PREF_FIELDS) / sizeof(PREF_FIELDS[0]);
|
||||
constexpr size_t wireSize(size_t count) {
|
||||
return count == 0 ? 0 : PREF_FIELDS[count - 1].length + wireSize(count - 1);
|
||||
}
|
||||
static_assert(wireSize(MANDATORY_FIELDS) == 84, "published mandatory image size");
|
||||
static_assert(wireSize(FIELD_COUNT) == 235, "published STM32 preference image size");
|
||||
|
||||
static const void* prefValue(const PrefField& field,
|
||||
const CompanionNodePrefs& values,
|
||||
const double& lat, const double& lon,
|
||||
const uint8_t pad[4]) {
|
||||
if (field.offset == PrefsLat) return ⪫
|
||||
if (field.offset == PrefsLon) return &lon;
|
||||
if (field.offset == PrefsPad) return pad;
|
||||
return reinterpret_cast<const uint8_t*>(&values) + field.offset;
|
||||
}
|
||||
|
||||
} // namespace companion_prefs
|
||||
} // namespace mesh
|
||||
#endif
|
||||
Reference in new issue
Block a user