Merge remote-tracking branch 'origin/keymindCascade' into keymindCascade

This commit is contained in:
mikecarper committed 2026-10-16 01:27:24 -07:00
commit 41405ee01b
54 files changed
+3962 -477

No files matched your search

+20 -1
View File
@@ -76,7 +76,26 @@ void formatStorageLayout(MainBoard& board, char* reply, size_t reply_size) {
(unsigned long)internal_fs_start,
(unsigned long)(internal_fs_size / 1024UL));
#if defined(ENABLE_OTA) && defined(OTA_RAK_AUTO_STORE)
#if defined(ENABLE_OTA) && defined(OTA_RAK15002_SD_STORE)
(void)board;
// Optional SD discovery can be slow and takes ownership of the IO-slot
// bus. A layout/status query must not mount it or allocate an OTA context.
const auto* context = mesh::ota::ota_context_if_active();
if (!context) {
appendStorageLayout(reply, reply_size, "; ext:sd=optional ota=not-prepared");
} else {
#if defined(OTA_SEEDER_ONLY)
appendStorageLayout(reply, reply_size, "; ext:sd=optional owner=ota-archive source-only");
#else
const auto& store = context->fetch_store;
appendStorageLayout(reply, reply_size, "; ota=%s medium=%s staged=%lu",
store.selectionReason(),
store.usesSd() ? "sd" : store.usesQspi() ? "qspi" :
store.usesInternal() ? "internal" : "not-prepared",
(unsigned long)store.staged_size());
#endif
}
#elif defined(ENABLE_OTA) && defined(OTA_RAK_AUTO_STORE)
(void)board;
mesh::ota::OtaStoreAdaptiveNrf52& store = mesh::ota::ota_ctx().fetch_store;
if (store.usesExternal()) {
+7
View File
@@ -21,6 +21,13 @@
namespace UserGpioPinPolicy {
inline bool isFirmwareReserved(uint8_t pin) {
#if defined(OTA_RAK15002_SD_STORE)
// The exact RAK15002 recipe claims these WisBlock nets even when no card
// is fitted: user GPIO must not defeat card detection or a later SD/NOR/
// Ethernet handoff. WB_IO6 is Arduino pin 10, not GPIO number 6.
if (pin == PIN_SPI_SCK || pin == WB_IO6 || pin == 26
|| pin == PIN_SPI_MISO || pin == PIN_SPI_MOSI) return true;
#endif
#if defined(STATION_G3_ESP32)
// A module may be present in the inactive slot too. Never offer its pins
// for user GPIO, even when the current boot selected the other slot.
@@ -4,6 +4,9 @@
#include "../../nrf52/EthernetCliTransmit.h"
#include <SPI.h>
#include <EthernetUdp.h>
#if defined(OTA_RAK15002_SD_STORE)
#include "../../ota/OtaSdSpiBusNrf52.h"
#endif
#define PIN_SPI1_MISO (29) // (0 + 29)
#define PIN_SPI1_MOSI (30) // (0 + 30)
@@ -16,6 +19,64 @@ SPIClass ETHERNET_SPI_PORT(NRF_SPIM1, PIN_SPI1_MISO, PIN_SPI1_SCK, PIN_SPI1_MOSI
#define PIN_ETHERNET_SS 26
bool RAK13800EthernetInterface::begin() {
#if defined(OTA_RAK15002_SD_STORE)
// Rare shared-CS Ethernet is explicitly enabled after the SD/NOR context
// has supplied its pin/transfer ownership hooks. Boot remains USB/BLE-only.
return mesh::nrf52::registerTerminalEthernetShutdown(terminalShutdown, this);
#else
return beginHardware();
#endif
}
#if defined(OTA_RAK15002_SD_STORE)
bool RAK13800EthernetInterface::tryEnable() {
if (_terminalStopping) { _spiError = "terminal shutdown"; return false; }
if (_spiStarted) { SerialEthernetInterface::enable(); return true; }
if (_ownsSharedSpi) { _spiError = "release stopped Ethernet with set eth off first"; return false; }
if (!_prepareSpi || !_canReleaseSpi || !_releasedSpi) {
_spiError = "OTA ownership hooks unavailable"; return false;
}
if (mesh::ota::ota_rak15002_sd_card_present()) {
_spiError = "remove SD card: Ethernet and SD share CS26"; return false;
}
if (!_prepareSpi(_spiContext)) { _spiError = "OTA storage busy"; return false; }
if (!mesh::ota::rak4631_try_acquire_spi(mesh::ota::RakSharedSpiOwner::Ethernet)) {
(void)_releasedSpi(_spiContext);
_spiError = "WisBlock SPI busy"; return false;
}
_ownsSharedSpi = true;
if (!beginHardware()) {
(void)shutdownForPowerOff(0);
_terminalStopping = false;
(void)_releasedSpi(_spiContext);
_spiError = "Ethernet startup failed"; return false;
}
SerialEthernetInterface::enable();
_spiError = "on";
return true;
}
bool RAK13800EthernetInterface::tryDisable() {
if (_terminalStopping) { _spiError = "terminal shutdown"; return false; }
if (!_ownsSharedSpi) {
SerialEthernetInterface::disable();
_spiError = "off";
return true;
}
if (_ownsSharedSpi && (!_canReleaseSpi || !_canReleaseSpi(_spiContext))) {
_spiError = "OTA storage busy"; return false;
}
(void)shutdownForPowerOff(0);
_terminalStopping = false;
if (_releasedSpi && !_releasedSpi(_spiContext)) {
_spiError = "OTA storage release pending"; return false;
}
_spiError = "off";
return true;
}
#endif
bool RAK13800EthernetInterface::beginHardware() {
if (_terminalStopping) return false;
if (!mesh::nrf52::registerTerminalEthernetShutdown(terminalShutdown, this)) return false;
// Preserve existing startup/link timing. PoE supplies separate 5V through
@@ -83,29 +144,53 @@ bool RAK13800EthernetInterface::shutdownForPowerOff(uint32_t timeout_ms) {
// disable() cancels framed/session producers, but disconnectClient() must
// not enter the vendor socket-stop/ACK wait during terminal shutdown.
SerialEthernetInterface::disable();
if (!_spiStarted) return true;
for (uint8_t socket = 0; socket < 4; ++socket) {
(void)mesh::nrf52::EthernetCliTransmit::close(socket);
if (_spiStarted) {
#if defined(OTA_RAK15002_SD_STORE)
// A newly inserted card also selects on CS26. Do not clock Ethernet
// socket commands into it during recovery or terminal shutdown.
if (!mesh::ota::ota_rak15002_sd_card_present())
#endif
for (uint8_t socket = 0; socket < 4; ++socket) {
(void)mesh::nrf52::EthernetCliTransmit::close(socket);
}
client = EthernetClient();
ETHERNET_SPI_PORT.end();
_spiStarted = false;
mesh::nrf52::releaseRakEthernetBusPads();
}
client = EthernetClient();
ETHERNET_SPI_PORT.end();
_spiStarted = false;
mesh::nrf52::releaseRakEthernetBusPads();
#if defined(OTA_RAK15002_SD_STORE)
if (_ownsSharedSpi) {
mesh::ota::rak4631_release_spi(mesh::ota::RakSharedSpiOwner::Ethernet);
_ownsSharedSpi = false;
}
#endif
return true;
}
int RAK13800EthernetInterface::available() {
if (!_spiStarted || _terminalStopping) return 0;
#if defined(OTA_RAK15002_SD_STORE)
if (mesh::ota::ota_rak15002_sd_card_present()
|| !mesh::ota::rak4631_ethernet_owns_spi()) return 0;
#endif
return client.available();
}
int RAK13800EthernetInterface::read() {
if (!_spiStarted || _terminalStopping) return -1;
#if defined(OTA_RAK15002_SD_STORE)
if (mesh::ota::ota_rak15002_sd_card_present()
|| !mesh::ota::rak4631_ethernet_owns_spi()) return -1;
#endif
return client.read();
}
size_t RAK13800EthernetInterface::write(const uint8_t *buf, size_t size) {
if (!_spiStarted || _terminalStopping) return 0;
#if defined(OTA_RAK15002_SD_STORE)
if (mesh::ota::ota_rak15002_sd_card_present()
|| !mesh::ota::rak4631_ethernet_owns_spi()) return 0;
#endif
return client.write(buf, size);
}
@@ -115,11 +200,31 @@ bool RAK13800EthernetInterface::isConnected() const {
void RAK13800EthernetInterface::disconnectClient() {
_isConnected = false;
#if defined(OTA_RAK15002_SD_STORE)
if (mesh::ota::ota_rak15002_sd_card_present()
|| !mesh::ota::rak4631_ethernet_owns_spi()) return;
#endif
if (_spiStarted && !_terminalStopping) client.stop();
}
void RAK13800EthernetInterface::loop() {
if (!_spiStarted || _terminalStopping) return;
#if defined(OTA_RAK15002_SD_STORE)
if (mesh::ota::ota_rak15002_sd_card_present()) {
// Fail closed on insertion while Ethernet owns the bus. Do not send a
// socket-close command on shared CS26 or switch a live internal OTA store.
_terminalStopping = true;
SerialEthernetInterface::disable();
_terminalStopping = false;
digitalWrite(PIN_ETHERNET_SS, HIGH);
ETHERNET_SPI_PORT.end();
_spiStarted = false;
mesh::nrf52::releaseRakEthernetBusPads();
_spiError = "SD inserted; set eth off before SD access";
return; // Retain Ethernet's claim until the context permits release.
}
if (!mesh::ota::rak4631_ethernet_owns_spi()) return;
#endif
Ethernet.maintain();
if (!isEnabled()) return;
if (_isConnected && !client.connected()) {
@@ -11,6 +11,15 @@ class RAK13800EthernetInterface : public SerialEthernetInterface {
bool _terminalStopping = false;
EthernetServer server;
EthernetClient client;
bool beginHardware();
#if defined(OTA_RAK15002_SD_STORE)
bool (*_prepareSpi)(void*) = nullptr;
bool (*_canReleaseSpi)(void*) = nullptr;
bool (*_releasedSpi)(void*) = nullptr;
void* _spiContext = nullptr;
bool _ownsSharedSpi = false;
const char* _spiError = "off";
#endif
protected:
void disconnectClient() override;
@@ -24,6 +33,21 @@ class RAK13800EthernetInterface : public SerialEthernetInterface {
bool begin();
void loop() override;
#if defined(OTA_RAK15002_SD_STORE)
void setSpiOwnershipHooks(bool (*prepare)(void*), bool (*can_release)(void*),
bool (*released)(void*), void* context) {
_prepareSpi = prepare; _canReleaseSpi = can_release;
_releasedSpi = released; _spiContext = context;
}
bool tryEnable();
bool tryDisable();
bool hardwareActive() const { return _spiStarted; }
const char* last_error() const { return _spiError; }
// The transport manager enables every registered interface at boot. Do
// not turn that logical operation into a physical shared-bus acquisition.
void enable() override { if (_spiStarted) SerialEthernetInterface::enable(); }
void disable() override { (void)tryDisable(); }
#endif
// BaseSerialInterface methods
bool isConnected() const override;
+23 -10
View File
@@ -39,19 +39,28 @@ static SPIClass ETHERNET_SPI_PORT(NRF_SPIM1, PIN_SPI1_MISO, PIN_SPI1_SCK, PIN_SP
#define ETHERNET_RETRY_INTERVAL_MS 30000
// New optional-SD infrastructure recipes need the same cooperative, OFF by
// default bus handoff as the combined prototype. Never start legacy DHCP on
// the shared IO-slot CS merely because Ethernet was compiled into the image.
#if defined(RAK4631_COMBINED_ETHERNET) || defined(OTA_RAK15002_SD_STORE)
#define MESH_RAK_ETHERNET_CLI_RUNTIME 1
#else
#define MESH_RAK_ETHERNET_CLI_RUNTIME 0
#endif
static EthernetServer ethernet_server(ETHERNET_TCP_PORT);
#if !defined(RAK4631_COMBINED_ETHERNET)
#if !MESH_RAK_ETHERNET_CLI_RUNTIME
static EthernetClient ethernet_client;
#endif
static std::atomic<bool> ethernet_running{false};
#if defined(RAK4631_COMBINED_ETHERNET)
#if MESH_RAK_ETHERNET_CLI_RUNTIME
static std::atomic<bool> ethernet_session_reset{false};
static bool ethernet_line_discarding = false; // Main-thread framing state.
#else
static bool ethernet_session_reset = false;
#endif
static bool ethernet_take_session_reset() {
#if defined(RAK4631_COMBINED_ETHERNET)
#if MESH_RAK_ETHERNET_CLI_RUNTIME
const bool changed = ethernet_session_reset.exchange(false);
if (changed) ethernet_line_discarding = false;
return changed;
@@ -62,7 +71,7 @@ static bool ethernet_take_session_reset() {
#endif
}
#if defined(RAK4631_COMBINED_ETHERNET)
#if MESH_RAK_ETHERNET_CLI_RUNTIME
static void ethernet_check_client();
#include "EthernetCliRuntime.h"
#else
@@ -174,7 +183,7 @@ static void ethernet_start_task() {
// Format ethernet status into reply buffer. Returns true if command was handled.
static bool ethernet_handle_command(const char* command, char* reply) {
#if defined(RAK4631_COMBINED_ETHERNET)
#if MESH_RAK_ETHERNET_CLI_RUNTIME
if (strcmp(command, "eth on") == 0 || strcmp(command, "set eth on") == 0) {
(void)ethernet_request_start(reply, true);
return true;
@@ -193,7 +202,7 @@ static bool ethernet_handle_command(const char* command, char* reply) {
}
#endif
if (strcmp(command, "eth.status") == 0) {
#if defined(RAK4631_COMBINED_ETHERNET)
#if MESH_RAK_ETHERNET_CLI_RUNTIME
const EthernetCliState state = ethernet_state.load();
if (state != EthernetCliState::Online) {
const char* status = state == EthernetCliState::Off ? "off" :
@@ -228,7 +237,8 @@ static bool ethernet_handle_command(const char* command, char* reply) {
// available() also returns existing connected sockets that have data, which
// would force us to disambiguate every inbound packet from a real new client.
static void ethernet_check_client() {
#if defined(RAK4631_COMBINED_ETHERNET)
#if MESH_RAK_ETHERNET_CLI_RUNTIME
if (ethernet_storage_conflict()) return;
// Combined accept/relisten can call vendor controller commands; run only
// in the background worker while the mesh-facing Stream is gated.
auto newClient = ethernet_server.accept();
@@ -272,7 +282,10 @@ static void ethernet_check_client() {
// Call from loop() to maintain DHCP and check for new clients
static void ethernet_loop_maintain() {
#if defined(RAK4631_COMBINED_ETHERNET)
#if MESH_RAK_ETHERNET_CLI_RUNTIME
#if defined(OTA_RAK15002_SD_STORE)
if (ethernet_enabled.load() && ethernet_storage_conflict()) return;
#endif
if (ethernet_state.load() == EthernetCliState::Stopping) {
if (ethernet_worker_active.load() && !ethernet_worker_stopped.load()) {
EthernetSpiAccess expected = EthernetSpiAccess::Idle;
@@ -309,7 +322,7 @@ static void ethernet_loop_maintain() {
// Returns true when a complete line is ready to process (command is null-terminated).
// The caller should process the command and then reset ethernet_command[0] = 0.
static bool ethernet_read_line(char* ethernet_command, size_t buf_size) {
#if defined(RAK4631_COMBINED_ETHERNET)
#if MESH_RAK_ETHERNET_CLI_RUNTIME
if (buf_size < 2) return false;
EthernetClientAccess access;
if (!access || !ethernet_client_attached.load()) return false;
@@ -389,7 +402,7 @@ static bool ethernet_read_line(char* ethernet_command, size_t buf_size) {
// Send a reply to the Ethernet client
static void ethernet_send_reply(const char* reply) {
if (reply[0]) {
#if defined(RAK4631_COMBINED_ETHERNET)
#if MESH_RAK_ETHERNET_CLI_RUNTIME
// One RAM-only enqueue keeps a synchronous command's complete reply even
// if the worker begins renewal between command dispatch and this call.
char frame[170];
+60 -8
View File
@@ -3,13 +3,17 @@
#include <cstdlib>
#include "EthernetCliTransmit.h"
// Included by EthernetCLI.h only for the opt-in combined RAK4631 prototype.
// Legacy Ethernet profiles retain their established implementation.
// Included by EthernetCLI.h for combined or optional-SD RAK4631 recipes.
// Both require explicit startup and a completed storage-owner handoff before
// any worker may touch the IO-slot pins. Legacy-only profiles are unchanged.
struct EthernetCliHooks {
bool (*prepare)(void*, char*, size_t) = nullptr;
bool (*can_release)(void*) = nullptr;
bool (*released)(void*) = nullptr;
bool (*save)(void*, bool) = nullptr;
#if defined(OTA_RAK15002_SD_STORE)
bool (*storage_conflict)(void*) = nullptr; // GPIO-only, safe from either thread.
#endif
void* context = nullptr;
};
@@ -41,6 +45,9 @@ static uint32_t ethernet_line_generation = 0; // Main-thread partial input owner
static std::atomic<bool> ethernet_command_check_requested{false};
static std::atomic<uint32_t> ethernet_command_check_started{0};
static bool ethernet_controller_faulted = false;
#if defined(OTA_RAK15002_SD_STORE)
static std::atomic<bool> ethernet_storage_faulted{false};
#endif
static mesh::nrf52::EthernetCliTransmit ethernet_transmit;
#ifndef ETHERNET_CLI_TX_BUFFER_BYTES
@@ -71,6 +78,25 @@ static void ethernet_notify_worker() {
taskEXIT_CRITICAL();
}
static bool ethernet_storage_conflict() {
#if defined(OTA_RAK15002_SD_STORE)
if (ethernet_storage_faulted.load()) return true;
if (ethernet_bus_reserved && (!ethernet_hooks.storage_conflict ||
ethernet_hooks.storage_conflict(ethernet_hooks.context))) {
// A fitted card shares CS26. Gate new socket/controller transactions
// immediately, but leave the worker to finish any already-entered vendor
// call before ending SPI. Retain ownership until an explicit safe OFF.
ethernet_storage_faulted.store(true);
ethernet_running.store(false);
ethernet_state.store(EthernetCliState::Faulted);
ethernet_session_reset.store(true);
ethernet_notify_worker();
return true;
}
#endif
return false;
}
static void ethernet_queue_clear() {
taskENTER_CRITICAL();
ethernet_tx_head = 0;
@@ -116,10 +142,10 @@ class EthernetClientAccess {
bool acquired = false;
public:
EthernetClientAccess() {
if (!ethernet_running.load()) return;
if (ethernet_storage_conflict() || !ethernet_running.load()) return;
EthernetSpiAccess expected = EthernetSpiAccess::Idle;
acquired = ethernet_spi_access.compare_exchange_strong(expected, EthernetSpiAccess::Client);
if (acquired && !ethernet_running.load()) {
if (acquired && (ethernet_storage_conflict() || !ethernet_running.load())) {
ethernet_spi_access.store(EthernetSpiAccess::Idle);
acquired = false;
}
@@ -210,7 +236,7 @@ static void ethernet_stop_hardware() {
// The W5100S has four sockets; there is no EthernetServer::end() in the
// library. Stop listener, accepted-client and DHCP sockets before SPI.end.
// Never toggle the PHY reset or shared WisBlock supply during shutdown.
if (ethernet_spi_started && ethernet_hardware_found) {
if (!ethernet_storage_conflict() && ethernet_spi_started && ethernet_hardware_found) {
for (uint8_t index = 0; index < 4; ++index) {
(void)mesh::nrf52::EthernetCliTransmit::close(index);
}
@@ -319,6 +345,7 @@ static bool ethernet_publish_online() {
}
static void ethernet_cache_ip() {
if (ethernet_storage_conflict()) return;
const IPAddress ip = Ethernet.localIP();
ethernet_cached_ip.store(static_cast<uint32_t>(ip[0]) |
(static_cast<uint32_t>(ip[1]) << 8) |
@@ -334,6 +361,7 @@ static bool ethernet_has_tx_work() {
}
static bool ethernet_controller_commands_idle() {
if (ethernet_storage_conflict()) return false;
SPIClass* spi = W5100.getSPI();
if (!spi) return false;
spi->beginTransaction(SPI_ETHERNET_SETTINGS);
@@ -359,6 +387,7 @@ static void ethernet_poll_command_check() {
// Worker-only, while SPI access is Worker. No library socketSend/execCmdSn
// calls: stalled TCP acknowledgement must never hold the mesh loop.
static void ethernet_pump_tx() {
if (ethernet_storage_conflict()) return;
ethernet_transmit.poll(millis());
if (ethernet_transmit.failed()) {
if (ethernet_raw_client) {
@@ -395,9 +424,10 @@ static void ethernet_pump_tx() {
}
static void ethernet_drain_before_stop() {
if (!ethernet_spi_started || !ethernet_hardware_found) return;
if (ethernet_storage_conflict() || !ethernet_spi_started || !ethernet_hardware_found) return;
const uint32_t started = millis();
while (ethernet_has_tx_work() && static_cast<uint32_t>(millis() - started) < 250) {
if (ethernet_storage_conflict()) return;
ethernet_pump_tx();
if (!ethernet_has_tx_work()) break;
(void)ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(10));
@@ -409,7 +439,7 @@ static void ethernet_drain_before_stop() {
// OFF interrupts retry waits, then lets an in-progress library call finish;
// never delete another task in the middle of its SPI transaction.
static void ethernet_task(void*) {
if (ethernet_enabled.load()) {
if (ethernet_enabled.load() && !ethernet_storage_conflict()) {
pinMode(PIN_ETHERNET_RESET, OUTPUT);
digitalWrite(PIN_ETHERNET_RESET, HIGH);
ETHERNET_SPI_PORT.begin();
@@ -418,7 +448,9 @@ static void ethernet_task(void*) {
uint8_t mac[6];
generateEthernetMac(mac);
while (ethernet_enabled.load()) {
if (ethernet_storage_conflict()) break;
const bool connected = Ethernet.begin(mac, 10000, 2000) != 0;
if (ethernet_storage_conflict()) break;
ethernet_hardware_found = Ethernet.hardwareStatus() != EthernetNoHardware;
if (!ethernet_enabled.load()) break;
if (!ethernet_hardware_found) {
@@ -427,6 +459,7 @@ static void ethernet_task(void*) {
return;
}
if (connected) {
if (ethernet_storage_conflict()) break;
ethernet_server.begin();
ethernet_cache_ip();
ethernet_last_maintain.store(millis());
@@ -435,10 +468,12 @@ static void ethernet_task(void*) {
// An online OFF is notified by the next mesh-loop maintain call,
// allowing a command received over Ethernet to send its reply first.
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(20));
if (ethernet_storage_conflict()) break;
if (!ethernet_enabled.load()) break;
if (ethernet_controller_faulted) continue;
const bool maintain = ethernet_maintain_requested.exchange(false);
ethernet_worker_take_access();
if (ethernet_storage_conflict()) break;
ethernet_poll_command_check();
if (ethernet_controller_faulted || ethernet_command_check_requested.load()) {
// Preserve the maintenance request while command checking yields.
@@ -446,6 +481,7 @@ static void ethernet_task(void*) {
continue;
}
if (maintain) (void)Ethernet.maintain();
if (ethernet_storage_conflict()) break;
if (!ethernet_enabled.load()) break;
if (maintain) ethernet_last_maintain.store(millis());
// Do not enter vendor accept/relisten while SEND/RECV is pending.
@@ -465,10 +501,17 @@ static void ethernet_task(void*) {
ethernet_worker_take_access();
ethernet_drain_before_stop();
ethernet_stop_hardware();
ethernet_finish_task(EthernetCliState::Stopping);
ethernet_finish_task(ethernet_storage_conflict() && ethernet_enabled.load()
? EthernetCliState::Faulted : EthernetCliState::Stopping);
}
static bool ethernet_request_start(char* reply, bool persist) {
#if defined(OTA_RAK15002_SD_STORE)
if (ethernet_storage_faulted.load() && ethernet_enabled.load()) {
strcpy(reply, "Error: SD insertion stopped Ethernet; turn Ethernet off first");
return false;
}
#endif
if (ethernet_state.load() == EthernetCliState::Stopping) {
strcpy(reply, "Error: Ethernet is still stopping");
return false;
@@ -489,6 +532,12 @@ static bool ethernet_request_start(char* reply, bool persist) {
strcpy(reply, "Error: Ethernet bus safety hooks unavailable");
return false;
}
#if defined(OTA_RAK15002_SD_STORE)
if (!ethernet_hooks.storage_conflict) {
strcpy(reply, "Error: Ethernet storage-conflict hook unavailable");
return false;
}
#endif
reply[0] = 0;
if (!ethernet_hooks.prepare(ethernet_hooks.context, reply, 160)) {
if (!reply[0]) strcpy(reply, "Error: Ethernet bus is busy");
@@ -510,6 +559,9 @@ static bool ethernet_request_start(char* reply, bool persist) {
return false;
}
ethernet_enabled.store(true);
#if defined(OTA_RAK15002_SD_STORE)
ethernet_storage_faulted.store(false);
#endif
ethernet_controller_faulted = false;
ethernet_command_check_requested.store(false);
ethernet_maintain_requested.store(false);
+30 -5
View File
@@ -1,8 +1,9 @@
#pragma once
#if defined(RAK4631_COMBINED_ETHERNET)
#if defined(RAK4631_COMBINED_ETHERNET) || defined(OTA_RAK15002_SD_STORE)
#include <atomic>
#include <stdint.h>
namespace mesh {
namespace ota {
@@ -11,17 +12,41 @@ namespace ota {
// controller at a time. Reserve them before starting Ethernet's DHCP task;
// release them only after that task and every Ethernet socket have stopped.
// NOR discovery and QSPI staging must never touch these pins while reserved.
inline std::atomic<bool>& rak4631_ethernet_spi_owner() {
static std::atomic<bool> owner{false};
enum class RakSharedSpiOwner : uint8_t { None, Ethernet, Sd, Nor };
inline std::atomic<RakSharedSpiOwner>& rak4631_shared_spi_owner() {
static std::atomic<RakSharedSpiOwner> owner{RakSharedSpiOwner::None};
return owner;
}
inline RakSharedSpiOwner rak4631_spi_owner() {
return rak4631_shared_spi_owner().load(std::memory_order_acquire);
}
// Claims are main-loop lifetime reservations, not a recursive transaction
// lock. The same sole backend may confirm its retained claim; another device
// must never overwrite it, including on a failed mount or DHCP startup.
inline bool rak4631_try_acquire_spi(RakSharedSpiOwner wanted) {
if (wanted == RakSharedSpiOwner::None) return false;
auto expected = RakSharedSpiOwner::None;
return rak4631_shared_spi_owner().compare_exchange_strong(
expected, wanted, std::memory_order_acq_rel) || expected == wanted;
}
inline void rak4631_release_spi(RakSharedSpiOwner owner) {
if (owner == RakSharedSpiOwner::None) return;
auto expected = owner;
(void)rak4631_shared_spi_owner().compare_exchange_strong(
expected, RakSharedSpiOwner::None, std::memory_order_acq_rel);
}
inline bool rak4631_ethernet_owns_spi() {
return rak4631_ethernet_spi_owner().load(std::memory_order_acquire);
return rak4631_spi_owner() == RakSharedSpiOwner::Ethernet;
}
inline void rak4631_set_ethernet_spi_owner(bool ethernet) {
rak4631_ethernet_spi_owner().store(ethernet, std::memory_order_release);
if (ethernet) (void)rak4631_try_acquire_spi(RakSharedSpiOwner::Ethernet);
else rak4631_release_spi(RakSharedSpiOwner::Ethernet);
}
} // namespace ota
+37 -8
View File
@@ -466,7 +466,9 @@ bool ota_apply_detools_mota(const uint8_t*, uint32_t, const SignerAllowlist&, Ap
static OtaStoreFlashNrf52* g_nrf52_apply_store = nullptr;
#endif
#if defined(OTA_QSPI_STORE)
static uint8_t g_nrf52_qspi_handoff = GPREGRET2_OTA_STAGE_QSPI;
// Set by the successfully verified external application path, never inferred
// from compile-time store flags: the same RAK image supports NOR and SD.
static uint8_t g_nrf52_external_handoff = GPREGRET2_OTA_STAGE_QSPI;
#endif
static void ota_nrf52_set_reset_handoff(uint8_t request, uint8_t source) {
@@ -514,13 +516,13 @@ void ota_reboot_to_apply() { // public: set the apply magic +
#if defined(OTA_TOWER_AUTO_STORE)
stage_handoff = GPREGRET2_OTA_STAGE_SD;
#else
stage_handoff = g_nrf52_qspi_handoff;
stage_handoff = g_nrf52_external_handoff;
#endif
}
#elif defined(OTA_SD_STORE)
stage_handoff = GPREGRET2_OTA_STAGE_SD;
#elif defined(OTA_QSPI_STORE)
stage_handoff = g_nrf52_qspi_handoff;
stage_handoff = g_nrf52_external_handoff;
#elif defined(OTA_FLASH_STORE)
if (g_nrf52_apply_store && g_nrf52_apply_store->is_hybrid()) {
// The bootloader admits retained SRAM only after a clean software-reset
@@ -1155,9 +1157,21 @@ static bool ota_prepare_bootloader_update_external(Store& store,
#if defined(OTA_SD_STORE)
bool ota_apply_mota_nrf52(OtaStoreSdNrf52& store, const SignerAllowlist& allow,
ApplyState& st, char* msg) {
#if defined(OTA_TOWER_AUTO_STORE)
#if defined(OTA_TOWER_AUTO_STORE) || defined(OTA_RAK_AUTO_STORE)
g_nrf52_apply_store = nullptr;
#endif
#if defined(OTA_RAK15002_SD_STORE)
OtaBootloaderIdentity rak_identity;
const OtaBlCaps rak_caps = ota_bootloader_app_caps();
if (rak_caps.optional_sd_app_storage != OTA_BL_STORAGE_SD ||
!ota_installed_bootloader_identity(rak_identity) || !rak_identity.crc_ok ||
(strcmp(rak_identity.device_name, "4631_DFU") != 0 &&
strcmp(rak_identity.device_name, "4631_AUTO_DFU") != 0)) {
st = ApplyState();
strcpy(msg, "RAK SD application OTA requires the matching all-in-one OTAFIX bootloader");
return false;
}
#endif
#if defined(OTA_SD_BOOTLOADER_UPDATE)
OtaBootloaderIdentity installed;
if (!ota_installed_bootloader_identity(installed) ||
@@ -1170,7 +1184,12 @@ bool ota_apply_mota_nrf52(OtaStoreSdNrf52& store, const SignerAllowlist& allow,
return false;
}
#endif
return ota_apply_mota_nrf52_external(store, allow, OTA_BL_STORAGE_SD, "SD", true, st, msg);
if (!ota_apply_mota_nrf52_external(store, allow, OTA_BL_STORAGE_SD, "SD", true, st, msg))
return false;
#if defined(OTA_RAK15002_SD_STORE) && defined(OTA_RAK_AUTO_STORE)
g_nrf52_external_handoff = GPREGRET2_OTA_STAGE_SD;
#endif
return true;
}
#if defined(OTA_SD_BOOTLOADER_UPDATE)
@@ -1214,13 +1233,13 @@ bool ota_apply_mota_nrf52(OtaStoreQspiNrf52& store, const SignerAllowlist& allow
#if defined(OTA_RAK_AUTO_STORE)
g_nrf52_apply_store = nullptr;
#endif
g_nrf52_qspi_handoff = GPREGRET2_OTA_STAGE_QSPI;
g_nrf52_external_handoff = GPREGRET2_OTA_STAGE_QSPI;
if (!ota_apply_mota_nrf52_external(store, allow, OTA_BL_STORAGE_QSPI, "QSPI", false, st, msg)) {
return false;
}
#if defined(OTA_RAK_AUTO_STORE)
if (OtaStoreQspiNrf52::headerW25Detected()) {
g_nrf52_qspi_handoff = GPREGRET2_OTA_STAGE_HEADER_W25;
g_nrf52_external_handoff = GPREGRET2_OTA_STAGE_HEADER_W25;
}
#endif
#if defined(OTA_RAK_AUTO_STORE) && !defined(RAK_3401)
@@ -1228,7 +1247,7 @@ bool ota_apply_mota_nrf52(OtaStoreQspiNrf52& store, const SignerAllowlist& allow
OtaBootloaderIdentity identity;
if (ota_installed_bootloader_identity(identity) && identity.crc_ok &&
strcmp(identity.device_name, "4631_AUTO_DFU") == 0) {
g_nrf52_qspi_handoff = GPREGRET2_OTA_STAGE_RAK15001;
g_nrf52_external_handoff = GPREGRET2_OTA_STAGE_RAK15001;
}
}
#endif
@@ -1338,6 +1357,16 @@ bool ota_prepare_bootloader_update_nrf52(OtaStoreFlashNrf52& store,
ota_bl_optional_app_storage(image, OTA_BOOT_IMAGE_SIZE) != optional_storage) {
strcpy(msg, "candidate must retain optional RAK application storage"); return false;
}
#if defined(RAK_4631) || defined(OTA_RAK15002_SD_STORE)
const OtaBlCaps installed_app_caps = ota_bootloader_app_caps();
if (installed_app_caps.optional_sd_app_storage != 0 &&
ota_bl_rak_sd_app_storage(image, OTA_BOOT_IMAGE_SIZE,
candidate_caps.storage_flags,
ota_bl_optional_app_storage(image, OTA_BOOT_IMAGE_SIZE)) !=
installed_app_caps.optional_sd_app_storage) {
strcpy(msg, "candidate must retain authenticated RAK SD application storage"); return false;
}
#endif
#endif
const OtaBootloaderContinuityGate continuity = ota_bootloader_continuity_gate(
installed, candidate, m.fw_version, OTA_BOOT_CONTINUITY_FAMILY_S140,
+4
View File
@@ -94,6 +94,10 @@ bool ota_rescue_mota_nrf52(const uint8_t* buf, uint32_t len,
uint32_t local_target_id, ApplyState& st, char* msg);
#if defined(NRF52_PLATFORM) && defined(OTA_SD_STORE)
class OtaStoreSdNrf52;
// Removable-media application apply always requires a trusted signature and
// contiguous staging plus retained authorization. RAK15002 additionally needs
// the exact supplemental all-in-one loader capability; SD never stages its
// bootloader packages, which continue through the internal-store overload.
bool ota_apply_mota_nrf52(OtaStoreSdNrf52& store,
const SignerAllowlist& allow, ApplyState& st, char* msg);
#if defined(OTA_SD_BOOTLOADER_UPDATE)
+40 -2
View File
@@ -29,6 +29,7 @@ struct OtaBlCaps {
uint16_t codec_mask = 0; // bit i set => can apply codec_id i (in-place delta = bit 2)
uint8_t storage_flags = 0; // OTA_BL_STORAGE_* capability bits
uint8_t optional_app_storage = 0; // separate MOTASTOR record, scoped to its qualified primary profile
uint8_t optional_sd_app_storage = 0; // supplemental MOTASDRA; application-only RAK15002 support
};
// Independent marker for the reset-retained hybrid source. Keep this separate
@@ -256,12 +257,37 @@ inline uint8_t ota_bl_profile_optional_app_storage(const uint8_t* bytes,
return 0;
}
// The all-in-one RAK4631 loader deliberately keeps its deployed internal/NOR
// capability bytes unchanged. SD application apply is a separate, exact ABI;
// it never grants SD bootloader updates. Count every aligned magic, including
// malformed/truncated declarations, so a second record cannot hide a conflict.
inline uint8_t ota_bl_rak_sd_app_storage(const uint8_t* bytes, size_t len,
uint8_t primary_storage,
uint8_t optional_storage) {
static const uint8_t marker[16] = {
'M', 'O', 'T', 'A', 'S', 'D', 'R', 'A', 1, 0, 72, 0, 1, 0x53, 0, 0,
};
if (!bytes || primary_storage != OTA_BL_PROFILE_INTERNAL_BOOT_UPDATE ||
optional_storage != (OTA_BL_STORAGE_QSPI | OTA_BL_STORAGE_HEADER_W25)) return 0;
unsigned matches = 0;
for (size_t off = 0; off + 8u <= len; off += 4u) {
if (memcmp(bytes + off, marker, 8u) != 0) continue;
if (++matches != 1u || off + sizeof(marker) > len ||
memcmp(bytes + off, marker, sizeof(marker)) != 0) return 0;
}
return matches == 1u ? OTA_BL_STORAGE_SD : 0;
}
inline OtaBlCaps ota_bl_app_caps_scan(const uint8_t* bytes, size_t len) {
OtaBlCaps aligned = ota_bl_caps_scan_aligned(bytes, len, false);
if (aligned.present && aligned.apply_abi >= 3) {
aligned.optional_app_storage = ota_bl_profile_optional_app_storage(
bytes, len, aligned.storage_flags);
aligned.storage_flags |= aligned.optional_app_storage;
#if !defined(NRF52_PLATFORM) || defined(RAK_4631) || defined(OTA_RAK15002_SD_STORE)
aligned.optional_sd_app_storage = ota_bl_rak_sd_app_storage(
bytes, len, aligned.storage_flags, aligned.optional_app_storage);
#endif
aligned.storage_flags |= aligned.optional_app_storage | aligned.optional_sd_app_storage;
}
if (aligned.present) return aligned;
return ota_bl_legacy_app_caps_scan_halfword(bytes, len);
@@ -276,6 +302,10 @@ inline OtaBlCaps ota_bl_update_caps_scan_aligned(const uint8_t* bytes, size_t le
// require an existing optional application capability in a successor.
caps.optional_app_storage = ota_bl_profile_optional_app_storage(
bytes, len, caps.storage_flags);
#if !defined(NRF52_PLATFORM) || defined(RAK_4631) || defined(OTA_RAK15002_SD_STORE)
caps.optional_sd_app_storage = ota_bl_rak_sd_app_storage(
bytes, len, caps.storage_flags, caps.optional_app_storage);
#endif
}
return caps;
}
@@ -317,7 +347,15 @@ inline OtaBlCaps ota_bootloader_update_caps() {
#if defined(OTA_INTERNAL_BOOTLOADER_UPDATE) && defined(OTA_RAK_AUTO_STORE)
// Unified RAK applications support the existing internal board identity as
// well as the adaptive successor. Both require one privileged marker.
const OtaBlCaps caps = ota_bl_caps_scan_aligned(lo, (size_t)(hi - lo), true);
OtaBlCaps caps = ota_bl_caps_scan_aligned(lo, (size_t)(hi - lo), true);
if (caps.present) {
caps.optional_app_storage = ota_bl_profile_optional_app_storage(
lo, (size_t)(hi - lo), caps.storage_flags);
#if defined(RAK_4631) || defined(OTA_RAK15002_SD_STORE)
caps.optional_sd_app_storage = ota_bl_rak_sd_app_storage(
lo, (size_t)(hi - lo), caps.storage_flags, caps.optional_app_storage);
#endif
}
return ota_bootloader_self_update_caps_valid(caps) ? caps : OtaBlCaps();
#else
return ota_bl_update_caps_scan_aligned(
+2 -1
View File
@@ -40,7 +40,8 @@
#error "OTA_INTERNAL_BOOTLOADER_UPDATE requires nRF52 internal-flash staging"
#endif
#if (defined(OTA_QSPI_STORE) && !defined(OTA_RAK_AUTO_STORE)) || \
defined(OTA_SD_STORE) || defined(QSPIFLASH)
(defined(OTA_SD_STORE) && !(defined(OTA_RAK15002_SD_STORE) && \
defined(OTA_RAK_AUTO_STORE) && defined(RAK_4631))) || defined(QSPIFLASH)
#error "internal bootloader staging cannot share an external OTA/filesystem store"
#endif
#endif
+33 -10
View File
@@ -18,7 +18,7 @@ namespace ota {
const char* const OtaCacheSdNrf52::DIR = "/mota";
const char* const OtaCacheSdNrf52::DISABLED_PATH = "/mota/cache.off";
OtaCacheSdNrf52::OtaCacheSdNrf52() : _file(new (std::nothrow) FsFile()) {}
OtaCacheSdNrf52::OtaCacheSdNrf52() : _file(new (std::nothrow) OtaSdFile()) {}
OtaCacheSdNrf52::~OtaCacheSdNrf52() {
if (_file) { _file->close(); delete _file; }
@@ -58,6 +58,9 @@ bool OtaCacheSdNrf52::ensureDir() {
}
bool OtaCacheSdNrf52::initialize() {
#if defined(OTA_RAK15002_SD_STORE)
if (!ready()) return false;
#endif
if (_initialized) return true;
_error[0] = 0;
if (!ready() || !ensureDir()) return false;
@@ -72,7 +75,7 @@ static int cache_hex_nibble(char c) {
return -1;
}
bool OtaCacheSdNrf52::describeFile(FsFile& file, MotaDesc& out) const {
bool OtaCacheSdNrf52::describeFile(OtaSdFile& file, MotaDesc& out) const {
uint64_t size64 = file.fileSize();
if (size64 < 8 + MOTA_MFL + 5 || size64 > UINT32_MAX) return false;
const uint32_t total = (uint32_t)size64;
@@ -111,9 +114,9 @@ bool OtaCacheSdNrf52::rescan() {
_count = 0;
if (!ready() || !ensureDir()) return false;
FsFile dir = _owner->_sd->open(DIR, O_RDONLY);
OtaSdFile dir = _owner->_sd->open(DIR, O_RDONLY);
if (!dir || !dir.isDir()) { fail("SD archive directory open failed"); return false; }
FsFile file;
OtaSdFile file;
while (_count < OTA_SD_CACHE_MAX && file.openNext(&dir, O_RDONLY)) {
if (!file.isDir()) {
char name[24] = {0};
@@ -156,7 +159,7 @@ bool OtaCacheSdNrf52::contains(const uint8_t mid[4]) {
// never enough: malformed files must remain eligible for quarantine and a clean re-download.
char path[32];
pathFor(mid, "mota", path, sizeof(path));
FsFile file = _owner->_sd->open(path, O_RDONLY);
OtaSdFile file = _owner->_sd->open(path, O_RDONLY);
MotaDesc d;
bool valid = file && describeFile(file, d) && memcmp(d.mid, mid, 4) == 0;
file.close();
@@ -188,7 +191,7 @@ bool OtaCacheSdNrf52::setAutoCaptureEnabled(bool enabled) {
return false;
}
} else {
FsFile marker = _owner->_sd->open(DISABLED_PATH, O_WRONLY | O_CREAT | O_TRUNC);
OtaSdFile marker = _owner->_sd->open(DISABLED_PATH, O_WRONLY | O_CREAT | O_TRUNC);
static const uint8_t off[] = {'o', 'f', 'f', '\n'};
if (!marker || marker.write(off, sizeof(off)) != sizeof(off) || !marker.sync()) {
marker.close();
@@ -295,6 +298,9 @@ bool OtaCacheSdNrf52::set_meta_size(uint32_t meta_bytes) {
}
bool OtaCacheSdNrf52::write(uint32_t off, const uint8_t* data, uint32_t len) {
#if defined(OTA_RAK15002_SD_STORE)
if (!_owner || !_owner->busAccessAllowed()) return false;
#endif
if (!_file || !*_file || (uint64_t)off + len > _total || !_file->seekSet(off) ||
_file->write(data, len) != len) {
fail("SD archive write failed");
@@ -304,17 +310,26 @@ bool OtaCacheSdNrf52::write(uint32_t off, const uint8_t* data, uint32_t len) {
}
bool OtaCacheSdNrf52::read(uint32_t off, uint8_t* buf, uint32_t len) const {
#if defined(OTA_RAK15002_SD_STORE)
if (!_owner || !_owner->busAccessAllowed()) return false;
#endif
return _file && *_file && (uint64_t)off + len <= _total && _file->seekSet(off) &&
_file->read(buf, len) == (int)len;
}
uint32_t OtaCacheSdNrf52::capacity() const {
#if defined(OTA_RAK15002_SD_STORE)
if (!_owner || !_owner->busAccessAllowed()) return 0;
#endif
if (!_owner || !_owner->_mounted || !_owner->_sd || !_owner->_sd->card()) return 0;
uint64_t bytes = (uint64_t)_owner->_sd->card()->sectorCount() * 512;
return bytes > UINT32_MAX ? UINT32_MAX : (uint32_t)bytes;
}
void OtaCacheSdNrf52::checkpoint() {
#if defined(OTA_RAK15002_SD_STORE)
if (!_owner || !_owner->busAccessAllowed()) return;
#endif
if (_file && *_file) {
_file->sync();
if (_owner && _owner->_sd->card()) _owner->_sd->card()->syncDevice();
@@ -322,6 +337,9 @@ void OtaCacheSdNrf52::checkpoint() {
}
bool OtaCacheSdNrf52::finalize() {
#if defined(OTA_RAK15002_SD_STORE)
if (!_owner || !_owner->busAccessAllowed()) return false;
#endif
if (!_file || !*_file || !_total || !_file->sync()) { fail("SD archive sync failed"); return false; }
if (_owner->_sd->card() && !_owner->_sd->card()->syncDevice()) {
fail("SD archive card sync failed");
@@ -332,7 +350,7 @@ bool OtaCacheSdNrf52::finalize() {
pathFor(_mid, "part", part_path, sizeof(part_path));
_file->close();
FsFile check = _owner->_sd->open(part_path, O_RDONLY);
OtaSdFile check = _owner->_sd->open(part_path, O_RDONLY);
MotaDesc d;
bool valid = check && describeFile(check, d) && memcmp(d.mid, _mid, 4) == 0;
check.close();
@@ -396,11 +414,16 @@ bool OtaCacheSdNrf52::describe(uint8_t idx, MotaDesc& out) {
}
bool OtaCacheSdNrf52::read(uint8_t idx, uint32_t off, uint8_t* buf, uint32_t len) {
if (!_initialized || idx >= _count || (uint64_t)off + len > _entries[idx].total_size || !ready())
return false;
if (!_initialized || idx >= _count) return false;
#if defined(OTA_RAK15002_SD_STORE)
const uint32_t total_size = _entries[idx].totalSize();
#else
const uint32_t total_size = _entries[idx].total_size;
#endif
if ((uint64_t)off + len > total_size || !ready()) return false;
char path[32];
pathFor(_entries[idx].mid, "mota", path, sizeof(path));
FsFile file = _owner->_sd->open(path, O_RDONLY);
OtaSdFile file = _owner->_sd->open(path, O_RDONLY);
bool ok = file && file.seekSet(off) && file.read(buf, len) == (int)len;
file.close();
return ok;
+38 -5
View File
@@ -4,8 +4,7 @@
#include "OtaSource.h"
#include "OtaStore.h"
class FsFile;
#include "OtaSdTypesNrf52.h"
namespace mesh {
namespace ota {
@@ -23,7 +22,7 @@ class OtaStoreSdNrf52;
// Persistent capture destination and MotaSource for the SD OTA archive. Completed containers live as
// /mota/<mid>.mota; an in-progress capture uses /mota/<mid>.part and can resume after interruption.
// This object shares the staging store's mounted SdFs volume so FAT metadata never has two independent
// This object shares the staging store's mounted volume so FAT metadata never has two independent
// caches for the same card.
class OtaCacheSdNrf52 : public OtaStore, public MotaSource {
public:
@@ -68,14 +67,48 @@ private:
bool ready();
bool ensureDir();
bool describeFile(FsFile& file, MotaDesc& out) const;
bool describeFile(OtaSdFile& file, MotaDesc& out) const;
bool quarantineFinal(const uint8_t mid[4]);
void pathFor(const uint8_t mid[4], const char* ext, char* out, size_t cap) const;
void fail(const char* message);
OtaStoreSdNrf52* _owner = nullptr;
FsFile* _file = nullptr;
OtaSdFile* _file = nullptr;
#if defined(OTA_RAK15002_SD_STORE)
// Valid archive files all have the fixed manifest followed immediately by
// leaves and payload. Keep every descriptor field without caching three
// redundant offsets/sizes per entry (192 bytes saved at 16 entries).
struct CachedDesc {
uint8_t mid[4];
uint32_t target_id, fw_version, block_count, payload_size;
uint8_t codec_id, flags, block_size_log2, source_caps;
CachedDesc& operator=(const MotaDesc& d) {
for (unsigned i = 0; i < 4; ++i) mid[i] = d.mid[i];
target_id = d.target_id; fw_version = d.fw_version;
block_count = d.block_count; payload_size = d.payload_size;
codec_id = d.codec_id; flags = d.flags;
block_size_log2 = d.block_size_log2; source_caps = d.source_caps;
return *this;
}
uint32_t totalSize() const { return 8u + MOTA_MFL + block_count * 4u + payload_size + 5u; }
operator MotaDesc() const {
MotaDesc d;
for (unsigned i = 0; i < 4; ++i) d.mid[i] = mid[i];
d.target_id = target_id; d.fw_version = fw_version;
d.block_count = block_count; d.payload_size = payload_size;
d.codec_id = codec_id; d.flags = flags;
d.block_size_log2 = block_size_log2; d.source_caps = source_caps;
d.leaves_off = 8u + MOTA_MFL;
d.payload_off = d.leaves_off + block_count * 4u;
d.total_size = totalSize();
return d;
}
};
static_assert(sizeof(CachedDesc) == 24u, "bounded RAK SD index changed");
CachedDesc _entries[OTA_SD_CACHE_MAX];
#else
MotaDesc _entries[OTA_SD_CACHE_MAX];
#endif
uint8_t _count = 0;
uint8_t _mid[4] = {0};
uint32_t _total = 0;
+73 -10
View File
@@ -200,7 +200,7 @@ static __attribute__((noinline)) bool handle_status(char* reply, OtaContext& c)
c.fetch_store.usesInternal() ? "internal" : "storage-ERR";
#elif defined(OTA_RAK_AUTO_STORE)
const char* bl_state = !bl.present ? "NONE" :
c.fetch_store.usesExternal() ? "QSPI" :
c.fetch_store.usesSd() ? "SD" : c.fetch_store.usesQspi() ? "QSPI" :
c.fetch_store.usesInternal() ? "internal" : "storage-ERR";
#elif defined(OTA_QSPI_STORE)
const char* bl_state = !bl.present ? "NONE" :
@@ -267,7 +267,8 @@ static __attribute__((noinline)) bool handle_neighbors(
const OtaBlCaps& list_bl_update = c.bootloaderUpdateCaps();
#endif
#endif
#if defined(NRF52_PLATFORM) && defined(OTA_FLASH_STORE) && !defined(OTA_SD_STORE) && \
#if defined(NRF52_PLATFORM) && defined(OTA_FLASH_STORE) && \
(!defined(OTA_SD_STORE) || defined(OTA_RAK15002_SD_STORE)) && \
(!defined(OTA_QSPI_STORE) || defined(OTA_RAK_AUTO_STORE))
SelfFwInfo list_self;
bool list_has_endf = ota_self_firmware(list_self) && list_self.valid;
@@ -337,11 +338,17 @@ static __attribute__((noinline)) bool handle_neighbors(
installable = installable && c.fetch_store.compatible();
if (h->codec == CODEC_FULL)
installable = installable && c.fetch_store.usesExternal();
#elif defined(OTA_SD_STORE)
#elif defined(OTA_SD_STORE) && !defined(OTA_RAK_AUTO_STORE)
installable = installable && (list_bl.storage_flags & OTA_BL_STORAGE_SD);
#elif defined(OTA_RAK_AUTO_STORE)
if (c.fetch_store.usesExternal())
if (c.fetch_store.usesQspi())
installable = installable && (list_bl.storage_flags & OTA_BL_STORAGE_QSPI);
#if defined(OTA_RAK15002_SD_STORE)
if (c.fetch_store.usesSd())
installable = installable && list_bl.optional_sd_app_storage == OTA_BL_STORAGE_SD &&
(h->flags & MFLAG_SIGNED);
#endif
installable = installable && (c.fetch_store.usesExternal() || c.fetch_store.usesInternal());
if (h->codec == CODEC_FULL)
installable = installable && c.fetch_store.usesExternal();
#elif defined(OTA_QSPI_STORE)
@@ -388,6 +395,11 @@ bool handle_ota_command(const char* command, char* reply, mesh::MainBoard& board
#endif
const char* rest = a;
if (*a == 0 || is_cmd(a, "status|st", &rest)) return handle_status(reply, c);
#if defined(NRF52_PLATFORM) && defined(OTA_RAK15002_SD_STORE) && !defined(OTA_SEEDER_ONLY)
// Radio/USB startup and status remain independent of removable storage.
// Explicit transfer/catalog/cache work may prepare it at this main-loop boundary.
if (!is_cmd(a, "qspi|storage|self|id|stats|help|?|h", &rest)) c.prepareRakStorage();
#endif
if (is_cmd(a, "neighbors|nbrs|updates|ls|n", &rest))
return handle_neighbors(rest, reply, c);
return handle_control_command(a, reply, board, c);
@@ -425,7 +437,11 @@ static __attribute__((noinline)) bool handle_control_command(
#endif
#elif defined(NRF52_PLATFORM) && defined(OTA_RAK_AUTO_STORE)
strcpy(reply,
"OTA: status | stats | ls | get <id> flash | install | bootloader | cancel | announce | self | serve | storage | folder | config | key");
"OTA: status | stats | ls | get <id> flash | install | bootloader | cancel | announce | self | serve | storage | folder | "
#if defined(OTA_RAK15002_SD_STORE)
"cache | "
#endif
"config | key");
#elif defined(NRF52_PLATFORM) && defined(OTA_QSPI_STORE)
#if defined(OTA_QSPI_BOOTLOADER_UPDATE)
strcpy(reply,
@@ -658,7 +674,7 @@ static __attribute__((noinline)) bool handle_control_command(
snprintf(reply, 160, "ERR OTA storage unsafe: %s", c.fetch_store.selectionReason());
return true;
}
#elif defined(OTA_SD_STORE)
#elif defined(OTA_SD_STORE) && !defined(OTA_RAK_AUTO_STORE)
if (!(bl.storage_flags & OTA_BL_STORAGE_SD)) {
strcpy(reply, "ERR bootloader cannot apply an update staged on SD; update it over USB first");
return true;
@@ -667,10 +683,26 @@ static __attribute__((noinline)) bool handle_control_command(
// Bootloader packages passed their separate privileged checks above and
// select internal staging when their manifest arrives. The currently
// selected application backend must not veto that transition.
if (!selboot && c.fetch_store.usesExternal() && !(bl.storage_flags & OTA_BL_STORAGE_QSPI)) {
if (!selboot &&
#if defined(OTA_RAK15002_SD_STORE)
c.fetch_store.usesQspi() &&
#else
c.fetch_store.usesExternal() &&
#endif
!(bl.storage_flags & OTA_BL_STORAGE_QSPI)) {
strcpy(reply, "ERR bootloader cannot apply from detected QSPI storage");
return true;
}
#if defined(OTA_RAK15002_SD_STORE)
if (!selboot && c.fetch_store.usesSd() && bl.optional_sd_app_storage != OTA_BL_STORAGE_SD) {
strcpy(reply, "ERR bootloader lacks authenticated RAK SD application support");
return true;
}
if (!selboot && c.fetch_store.usesSd() && !(selflags & MFLAG_SIGNED)) {
strcpy(reply, "ERR SD application install requires a trusted signed package; use folder to inspect");
return true;
}
#endif
if (!selboot && !c.fetch_store.usesExternal() && !c.fetch_store.usesInternal()) {
snprintf(reply, 160, "ERR OTA storage unsafe: %s", c.fetch_store.selectionReason());
return true;
@@ -682,7 +714,8 @@ static __attribute__((noinline)) bool handle_control_command(
}
#endif
#endif
#if defined(NRF52_PLATFORM) && defined(OTA_FLASH_STORE) && !defined(OTA_SD_STORE) && \
#if defined(NRF52_PLATFORM) && defined(OTA_FLASH_STORE) && \
(!defined(OTA_SD_STORE) || defined(OTA_RAK15002_SD_STORE)) && \
(!defined(OTA_QSPI_STORE) || defined(OTA_RAK_AUTO_STORE))
SelfFwInfo self;
bool has_endf = ota_self_firmware(self) && self.valid;
@@ -767,6 +800,11 @@ static __attribute__((noinline)) bool handle_control_command(
#endif
const bool discarded = !was_folder && !was_sd_archive &&
c.fetch_store.discard();
#if defined(NRF52_PLATFORM) && defined(OTA_RAK15002_SD_STORE) && !defined(OTA_SEEDER_ONLY)
// Keep cache sources/independent serving mounted; only refresh the
// application policy after the selected staged file was invalidated.
c.prepareRakStorage();
#endif
#if defined(NRF52_PLATFORM) && defined(OTA_TOWER_AUTO_STORE)
if (discarded || (!c.fetch_store.usesExternal() && !c.fetch_store.usesInternal())) {
c.fetch_store.resetSelection();
@@ -837,7 +875,13 @@ static __attribute__((noinline)) bool handle_control_command(
snprintf(reply, 160, "OTA storage: %s; capacity=%luK; SD primary, internal application deltas only",
c.fetch_store.selectionReason(), (unsigned long)(c.fetch_store.capacity() / 1024));
#elif defined(NRF52_PLATFORM) && defined(OTA_RAK_AUTO_STORE)
if (c.fetch_store.usesExternal()) {
#if defined(OTA_RAK15002_SD_STORE)
if (c.fetch_store.usesSd()) {
snprintf(reply, 160, "OTA storage: RAK15002 SD; staged=%lu; use get sdcard free for media space",
(unsigned long)c.fetch_store.sdStore().staged_size());
} else
#endif
if (c.fetch_store.usesQspi()) {
const uint32_t qspi_capacity = c.fetch_store.qspiCapacity();
snprintf(reply, 160, "QSPI %s jedec=%06lX size=%luK sr1=%02X stage=%s%s%s",
c.fetch_store.selectionReason(), (unsigned long)c.fetch_store.jedec_id(),
@@ -845,16 +889,26 @@ static __attribute__((noinline)) bool handle_control_command(
c.fetch_store.last_stage(), c.fetch_store.last_error()[0] ? " error=" : "",
c.fetch_store.last_error());
} else if (c.fetch_store.usesInternal()) {
#if defined(OTA_RAK15002_SD_STORE)
snprintf(reply, 160, "OTA storage: %s; internal capacity=%luK",
c.fetch_store.selectionReason(), (unsigned long)(c.fetch_store.capacity() / 1024));
#else
uint32_t rak_id = 0, w25_id = 0;
OtaStoreQspiNrf52::autoProbeIds(rak_id, w25_id);
snprintf(reply, 160, "OTA storage: %s; internal capacity=%luK; probe rak=%06lX w25=%06lX",
c.fetch_store.selectionReason(), (unsigned long)(c.fetch_store.capacity() / 1024),
(unsigned long)rak_id, (unsigned long)w25_id);
#endif
} else {
#if defined(OTA_RAK15002_SD_STORE)
snprintf(reply, 160, "OTA storage: %s; optional storage is prepared by OTA transfer/cache work",
c.fetch_store.selectionReason());
#else
uint32_t rak_id = 0, w25_id = 0;
OtaStoreQspiNrf52::autoProbeIds(rak_id, w25_id);
snprintf(reply, 160, "OTA storage unsafe: %s; probe rak=%06lX w25=%06lX",
c.fetch_store.selectionReason(), (unsigned long)rak_id, (unsigned long)w25_id);
#endif
}
#elif defined(NRF52_PLATFORM) && defined(OTA_QSPI_STORE)
// This probe only reads JEDEC/SR1. capacity() deliberately preserves a
@@ -889,7 +943,13 @@ static __attribute__((noinline)) bool handle_control_command(
c.fetch_store.usesExternal() ? "SD" : c.fetch_store.usesInternal() ? "internal" : "unsafe",
c.fetch_store.compatible() ? "apply OK" : "unsupported");
#elif defined(OTA_RAK_AUTO_STORE)
if (c.fetch_store.usesExternal()) {
#if defined(OTA_RAK15002_SD_STORE)
if (c.fetch_store.usesSd()) {
snprintf(reply + n, 160 - n, " | SD store | bootloader: %s",
bl.optional_sd_app_storage == OTA_BL_STORAGE_SD ? "authenticated SD apply OK" : "NO SD apply");
} else
#endif
if (c.fetch_store.usesQspi()) {
const uint32_t qspi_capacity = c.fetch_store.qspiCapacity();
snprintf(reply + n, 160 - n, " | QSPI store:%s%uK | bootloader: %s",
qspi_capacity ? "" : "ERR ",
@@ -1397,6 +1457,9 @@ static bool handle_dev(const char* d, char* reply, OtaContext& c) {
strcpy(reply, "OK cleared SD archive session; partial retained for resume");
} else {
const bool discarded = c.fetch_store.discard();
#if defined(NRF52_PLATFORM) && defined(OTA_RAK15002_SD_STORE) && !defined(OTA_SEEDER_ONLY)
c.prepareRakStorage();
#endif
#if defined(NRF52_PLATFORM) && defined(OTA_TOWER_AUTO_STORE)
if (discarded || (!c.fetch_store.usesExternal() && !c.fetch_store.usesInternal())) {
c.fetch_store.resetSelection();
+108 -2
View File
@@ -33,6 +33,9 @@
#include "OtaCacheSdNrf52.h"
#elif defined(NRF52_PLATFORM) && defined(OTA_RAK_AUTO_STORE)
#include "OtaStoreAdaptiveNrf52.h"
#if defined(OTA_RAK15002_SD_STORE)
#include "OtaCacheSdNrf52.h"
#endif
#elif defined(NRF52_PLATFORM) && defined(OTA_QSPI_STORE)
#include "OtaStoreQspiNrf52.h"
#elif defined(NRF52_PLATFORM) && defined(OTA_SD_STORE)
@@ -43,6 +46,9 @@
#elif defined(ESP32_PLATFORM) && defined(OTA_FLASH_STORE)
#include "OtaStoreFlashEsp32.h"
#endif
#if defined(OTA_RAK15002_SD_STORE)
#include "../nrf52/Rak4631SharedSpi.h"
#endif
#if defined(OTA_FOLDER_SERIAL)
#include "MotaSourceSerial.h" // relay an external folder served by a host daemon over the USB serial
#include "../UsbLogging.h"
@@ -125,6 +131,9 @@ struct OtaContext {
OtaCacheSdNrf52 sd_cache; // always owns the stable SD store
#elif defined(NRF52_PLATFORM) && defined(OTA_RAK_AUTO_STORE)
OtaStoreAdaptiveNrf52 fetch_store;
#if defined(OTA_RAK15002_SD_STORE)
OtaCacheSdNrf52 sd_cache;
#endif
#elif defined(NRF52_PLATFORM) && defined(OTA_QSPI_STORE)
OtaStoreQspiNrf52 fetch_store; // persistent raw QSPI staging, full + in-place delta
#elif defined(NRF52_PLATFORM) && defined(OTA_SD_STORE)
@@ -336,7 +345,17 @@ struct OtaContext {
}
}
bool ok;
#if defined(NRF52_PLATFORM) && (defined(OTA_RAK_AUTO_STORE) || defined(OTA_TOWER_AUTO_STORE))
#if defined(NRF52_PLATFORM) && defined(OTA_RAK15002_SD_STORE) && defined(OTA_RAK_AUTO_STORE)
ok = fetch_store.usesSd()
? ota_apply_mota_nrf52(fetch_store.sdStore(), allow, apply_st, msg)
: fetch_store.usesQspi()
? ota_apply_mota_nrf52(fetch_store.externalStore(), allow, apply_st, msg)
: fetch_store.usesInternal()
? ota_apply_mota_nrf52(fetch_store.internalStore(), allow, apply_st, msg)
: false;
if (!fetch_store.usesExternal() && !fetch_store.usesInternal())
strncpy(msg, fetch_store.selectionReason(), 96);
#elif defined(NRF52_PLATFORM) && (defined(OTA_RAK_AUTO_STORE) || defined(OTA_TOWER_AUTO_STORE))
ok = fetch_store.usesExternal()
? ota_apply_mota_nrf52(fetch_store.externalStore(), allow, apply_st, msg)
: fetch_store.usesInternal()
@@ -685,12 +704,80 @@ struct OtaContext {
}
#endif
#if defined(OTA_RAK15002_SD_STORE)
bool canReleaseRakSdForEthernet() const {
const bool own_image = manager.servingPrimaryManifest(serve_self_manifest);
return manager.fetchState() == OtaManager::IDLE && fetch_store.staged_size() == 0u &&
!apply_pending && !bootloader_apply_pending && !_sd_cache_fetching &&
!manager.pendingServeJobs() && !manager.pendingManifestJobs() &&
(!serving || own_image) && serve_expected == 0u && !folder_active &&
!_folder_source && !folder_dest && !disconnected_folder_dest && !fetch_to_folder;
}
bool prepareRakSdForEthernet() {
if (!canReleaseRakSdForEthernet()) return false;
if (_sd_cache_source_attached) manager.remove_source(&sd_cache);
_sd_cache_source_attached = false;
sd_cache.resetMedia();
if (!sdStagingStore().releaseBus()) return false;
#if !defined(OTA_SEEDER_ONLY)
if (!fetch_store.resetSelection()) return false;
manager.set_accept_full(false);
#endif
manager.clear_primary();
serving = false;
free(serve_self_leaves); free(serve_self_proof);
serve_self_leaves = serve_self_proof = nullptr;
releaseServeBuffer();
serve_expected = 0;
manager.set_archive_interest(false);
_sd_cache_init_retry_at = 0;
return true;
}
bool releasedRakSdEthernet() {
if (!canReleaseRakSdForEthernet()) return false;
#if !defined(OTA_SEEDER_ONLY)
if (!fetch_store.resetSelection()) return false;
manager.set_accept_full(false);
#endif
manager.set_archive_interest(true);
_sd_cache_init_retry_at = 0;
return true; // The next normal main-loop service may probe, never this callback.
}
#endif
#if defined(OTA_RAK15002_SD_STORE) && !defined(OTA_SEEDER_ONLY)
// Only actual OTA work may probe the optional card. Boot/status paths leave
// the selector untouched, and a selected transfer never changes its medium.
bool prepareRakStorage() {
const bool usable = fetch_store.selectStorage();
const bool external = usable && fetch_store.usesExternal() && !rak4631_ethernet_owns_spi();
manager.set_accept_full(external);
manager.set_accept_bootloader(ota_bootloader_self_update_caps_valid(bootloaderUpdateCaps()));
const bool was_self_serve_supported = self_serve_supported;
self_serve_supported = ota_self_serve_supported(external);
if (was_self_serve_supported && !self_serve_supported &&
manager.servingPrimaryManifest(serve_self_manifest)) {
manager.clear_primary();
serving = false;
}
#if MESHCORE_OTA_DEVICE_DEFLATE
manager.set_transport_deflate_encoder(self_serve_supported ? ota_transport_deflate : nullptr);
#endif
if (!fetch_store.usesSd()) manager.set_archive_interest(false);
return usable;
}
#endif
#if defined(NRF52_PLATFORM) && defined(OTA_SD_STORE)
OtaStoreSdNrf52& sdStagingStore() {
#if defined(OTA_SEEDER_ONLY)
return sd_archive_store;
#elif defined(OTA_TOWER_AUTO_STORE)
return fetch_store.sdStore();
#elif defined(OTA_RAK15002_SD_STORE) && defined(OTA_RAK_AUTO_STORE)
return fetch_store.sdStore();
#else
return fetch_store;
#endif
@@ -723,6 +810,12 @@ struct OtaContext {
// Initialize and attach the persistent source lazily. This lets Mesh::begin finish quickly when no card
// is inserted, while archive interest is already enabled so early OTA advertisements still get queried.
bool ensureSdCache() {
#if defined(OTA_RAK15002_SD_STORE)
if (rak4631_ethernet_owns_spi()) return false;
#if !defined(OTA_SEEDER_ONLY)
if (!prepareRakStorage() || !fetch_store.usesSd()) return false;
#endif
#endif
sd_cache.attach(sdStagingStore());
#if defined(OTA_TOWER_AUTO_STORE)
if (!prepareTowerStorage() || !fetch_store.usesExternal()) return false;
@@ -778,12 +871,20 @@ struct OtaContext {
sd_cache.resetMedia();
#if defined(OTA_TOWER_AUTO_STORE)
fetch_store.resetSelection();
#elif defined(OTA_RAK15002_SD_STORE) && !defined(OTA_SEEDER_ONLY)
if (!fetch_store.resetSelection()) return;
#endif
_sd_cache_init_retry_at = now;
ensureSdCache();
}
void serviceSdCache(uint32_t now) {
#if defined(OTA_RAK15002_SD_STORE)
if (rak4631_ethernet_owns_spi()) return;
#if !defined(OTA_SEEDER_ONLY)
if (!prepareRakStorage() || !fetch_store.usesSd()) return;
#endif
#endif
#if defined(OTA_TOWER_AUTO_STORE)
if (!prepareTowerStorage() || !fetch_store.usesExternal()) {
if (_sd_cache_source_attached) manager.remove_source(&sd_cache);
@@ -897,7 +998,11 @@ struct OtaContext {
manager.set_accept_full(false);
manager.set_apply_codec(CODEC_DETOOLS_INPLACE);
#elif defined(NRF52_PLATFORM) && defined(OTA_RAK_AUTO_STORE)
#if defined(OTA_RAK15002_SD_STORE)
manager.set_accept_full(false); // defer optional SD/NOR probing until startup finishes
#else
manager.set_accept_full(fetch_store.usesExternal());
#endif
manager.set_apply_codec(CODEC_DETOOLS_INPLACE);
#elif defined(NRF52_PLATFORM) && (defined(OTA_SD_STORE) || defined(OTA_QSPI_STORE))
manager.set_accept_full(true);
@@ -925,7 +1030,8 @@ struct OtaContext {
manager.set_accept_bootloader(false);
#endif
manager.set_fetch_store(&fetch_store);
#if defined(NRF52_PLATFORM) && defined(OTA_TOWER_AUTO_STORE)
#if defined(NRF52_PLATFORM) && (defined(OTA_TOWER_AUTO_STORE) || \
(defined(OTA_RAK15002_SD_STORE) && !defined(OTA_SEEDER_ONLY)))
self_serve_supported = false;
#elif defined(NRF52_PLATFORM) && defined(OTA_RAK_AUTO_STORE) && !defined(OTA_SEEDER_ONLY)
self_serve_supported = ota_self_serve_supported(fetch_store.usesExternal());
+27 -2
View File
@@ -2,6 +2,17 @@
#include <SPI.h>
#if defined(OTA_RAK15002_SD_CACHE) && defined(OTA_RAK15002_SD_STORE)
#error "RAK15002 seeder-only cache and adaptive SD receiver profiles are distinct"
#endif
#if defined(OTA_RAK15002_SD_STORE)
#if !defined(NRF52_PLATFORM) || !defined(RAK_4631) || !defined(OTA_SD_STORE)
#error "RAK15002 adaptive SD requires the exact nRF52 RAK4631 SD profile"
#endif
#include "../nrf52/Rak4631SharedSpi.h"
#endif
#if defined(OTA_RAK15002_SD_CACHE)
#if !defined(NRF52_PLATFORM) || !defined(RAK_4631) || !defined(OTA_SD_STORE) || !defined(OTA_SEEDER_ONLY)
#error "RAK15002 SD cache requires the exact nRF52 RAK4631 seeder-only profile"
@@ -9,10 +20,12 @@
#if defined(ETHERNET_ENABLED) || defined(OTA_QSPI_STORE) || defined(OTA_RAK_AUTO_STORE) || defined(OTA_TOWER_AUTO_STORE)
#error "RAK15002 SD cache cannot share the WisBlock SPI/CS with Ethernet or OTA NOR"
#endif
#endif
#if defined(OTA_RAK15002_SD_CACHE) || defined(OTA_RAK15002_SD_STORE)
#if SPI_INTERFACES_COUNT != 1 || (defined(SPI_32MHZ_INTERFACE) && SPI_32MHZ_INTERFACE != 0)
#error "RAK15002 SD cache requires radio SPI on SPIM3 and reserves SPIM2 for the SD card"
#endif
#if defined(OTA_SD_BOOTLOADER_UPDATE) || defined(OTA_INTERNAL_BOOTLOADER_UPDATE) || defined(OTA_QSPI_BOOTLOADER_UPDATE)
#if defined(OTA_RAK15002_SD_CACHE) && (defined(OTA_SD_BOOTLOADER_UPDATE) || defined(OTA_INTERNAL_BOOTLOADER_UPDATE) || defined(OTA_QSPI_BOOTLOADER_UPDATE))
#error "RAK15002 cache profile has no qualified bootloader-update handoff"
#endif
#ifndef OTA_SD_CS_PIN
@@ -20,14 +33,26 @@
#endif
static_assert(OTA_SD_CS_PIN == 26 && PIN_SPI_MISO == 29 && PIN_SPI_SCK == 3 && PIN_SPI_MOSI == 30,
"RAK15002 exact WisBlock SPI pin map changed; requalify the profile");
#if defined(OTA_RAK15002_SD_STORE)
static_assert(WB_IO6 == 10, "RAK15002 card detect must use the mapped WisBlock IO6");
#endif
#elif !defined(OTA_SD_CS_PIN)
#define OTA_SD_CS_PIN PIN_SPI1_NSS
#endif
namespace mesh { namespace ota {
#if defined(OTA_RAK15002_SD_STORE)
inline bool ota_rak15002_sd_card_present() {
// RAK15002 CD is active-low WisBlock IO6 (Arduino 10 / P0.10), not pin 6.
// GPIO-only detection must precede any SD or NOR command on shared CS26.
pinMode(WB_IO6, INPUT_PULLUP);
return digitalRead(WB_IO6) == LOW;
}
#endif
inline SPIClass& ota_sd_spi_bus() {
#if defined(OTA_RAK15002_SD_CACHE)
#if defined(OTA_RAK15002_SD_CACHE) || defined(OTA_RAK15002_SD_STORE)
// The RAK radio reassigns global SPI/SPIM3 to its private pins. Never use
// that object for the external WisBlock bus, or reconfigure it on SD mount.
static SPIClass sd_spi(NRF_SPIM2, 29, 3, 30);
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#if defined(NRF52_PLATFORM) && defined(OTA_SD_STORE)
// Keep SdFat implementation headers out of application headers: its global
// File typedef conflicts with Adafruit LittleFS's File. Only the two SD
// implementation translation units need complete volume/file definitions.
#if defined(OTA_RAK15002_SD_STORE)
class SdFat32;
class FatFile;
#else
class SdFs;
class FsFile;
#endif
namespace mesh {
namespace ota {
#if defined(OTA_RAK15002_SD_STORE)
using OtaSdFs = SdFat32;
using OtaSdFile = FatFile;
#else
using OtaSdFs = SdFs;
using OtaSdFile = FsFile;
#endif
} // namespace ota
} // namespace mesh
#endif
+104 -19
View File
@@ -8,7 +8,11 @@
#include "OtaStoreFlashNrf52.h"
#include "OtaStoreQspiNrf52.h"
#include "OtaRakStoragePolicy.h"
#if defined(RAK4631_COMBINED_ETHERNET)
#if defined(OTA_RAK15002_SD_STORE)
#include "OtaStoreSdNrf52.h"
#include "OtaSdSpiBusNrf52.h"
#endif
#if defined(RAK4631_COMBINED_ETHERNET) || defined(OTA_RAK15002_SD_STORE)
#include "../nrf52/Rak4631SharedSpi.h"
#endif
#include <new>
@@ -17,14 +21,21 @@ namespace mesh {
namespace ota {
// One RAK application image supports the board's internal OTA store and its
// explicitly matched external NOR. Application placement follows the detected
// explicitly matched external NOR or RAK15002 SD. Application placement follows the detected
// hardware. Bootloader packages always use internal flash; select the backend
// before begin(), never move a partially fetched container between stores.
class OtaStoreAdaptiveNrf52 : public OtaStore {
enum Mode : uint8_t { UNSELECTED, INTERNAL_MODE, QSPI_MODE, UNSAFE };
enum Mode : uint8_t { UNSELECTED, INTERNAL_MODE, QSPI_MODE, SD_MODE, UNSAFE };
mutable Mode _mode = UNSELECTED;
mutable const char* _reason = "not probed";
bool _bootloader_store = false;
#if defined(OTA_RAK15002_SD_STORE)
// The SD cache retains a stable owner even while a bootloader package uses
// internal flash. Card discovery is deferred until an explicit main-loop
// prepare; status/capability getters must not clock an optional empty slot.
mutable OtaStoreSdNrf52 _sd;
bool _prepared = false;
#endif
// Only one backend is usable for a given board/bootloader pairing. Sharing
// their two flash-page buffers saves 8 KiB of scarce nRF52840 runtime RAM.
union StoreStorage {
@@ -46,6 +57,9 @@ class OtaStoreAdaptiveNrf52 : public OtaStore {
bool selectBootloader() {
if (!ota_bootloader_self_update_caps_valid(ota_bootloader_update_caps())) return false;
#if defined(OTA_RAK15002_SD_STORE)
if (_sd.staged_size() != 0u) return false;
#endif
if (_mode != INTERNAL_MODE) {
if (_mode == QSPI_MODE) _storage.external.~OtaStoreQspiNrf52();
activateInternal();
@@ -61,18 +75,43 @@ class OtaStoreAdaptiveNrf52 : public OtaStore {
_mode = UNSELECTED;
_bootloader_store = false;
}
#if defined(OTA_RAK15002_SD_STORE)
selectStorage();
#else
select();
#endif
}
void select() const {
if (_mode != UNSELECTED) return;
#if defined(RAK4631_COMBINED_ETHERNET)
#if defined(OTA_RAK15002_SD_STORE)
if (!_prepared) return;
#endif
#if defined(RAK4631_COMBINED_ETHERNET) || defined(OTA_RAK15002_SD_STORE)
const bool ethernet = rak4631_ethernet_owns_spi();
const uint8_t detected = ethernet ? 0u : OtaStoreQspiNrf52::autoDetect();
#else
const uint8_t detected = OtaStoreQspiNrf52::autoDetect();
const bool ethernet = false;
#endif
const OtaBlCaps caps = ota_bootloader_app_caps();
#if defined(OTA_RAK15002_SD_STORE)
// Card detect is GPIO-only. If a card is fitted, never issue JEDEC clocks
// to the same CS as a fallback, even when its media/loader is unsuitable.
const bool sd_present = !ethernet && ota_rak15002_sd_card_present();
if (sd_present && caps.optional_sd_app_storage == OTA_BL_STORAGE_SD) {
OtaBootloaderIdentity sd_identity;
if (ota_installed_bootloader_identity(sd_identity) && sd_identity.crc_ok &&
(strcmp(sd_identity.device_name, "4631_DFU") == 0 ||
strcmp(sd_identity.device_name, "4631_AUTO_DFU") == 0) &&
_sd.probeMedia()) {
_mode = SD_MODE;
_reason = "RAK15002 SD";
return;
}
}
const uint8_t detected = (ethernet || sd_present) ? 0u : OtaStoreQspiNrf52::autoDetect();
#else
const uint8_t detected = ethernet ? 0u : OtaStoreQspiNrf52::autoDetect();
#endif
if (detected == 3u) {
_mode = UNSAFE;
_reason = "both external NOR types detected";
@@ -100,7 +139,7 @@ class OtaStoreAdaptiveNrf52 : public OtaStore {
caps.optional_app_storage == (OTA_BL_STORAGE_QSPI | OTA_BL_STORAGE_HEADER_W25));
if (choice == RakStorageChoice::Internal) {
activateInternal();
#if defined(RAK4631_COMBINED_ETHERNET)
#if defined(RAK4631_COMBINED_ETHERNET) || defined(OTA_RAK15002_SD_STORE)
if (ethernet) {
_reason = "Ethernet active; internal application deltas only";
return;
@@ -108,6 +147,13 @@ class OtaStoreAdaptiveNrf52 : public OtaStore {
#endif
_reason = detected == 0u ? "no external NOR" :
"external NOR fitted; internal bootloader";
#if defined(OTA_RAK15002_SD_STORE)
if (sd_present) {
_reason = caps.optional_sd_app_storage == OTA_BL_STORAGE_SD
? "SD unavailable; internal application deltas only"
: "SD needs newer OTAFIX; internal application deltas only";
}
#endif
return;
}
if (choice == RakStorageChoice::Qspi) {
@@ -117,7 +163,7 @@ class OtaStoreAdaptiveNrf52 : public OtaStore {
return;
}
_mode = UNSAFE;
#if defined(RAK4631_COMBINED_ETHERNET)
#if defined(RAK4631_COMBINED_ETHERNET) || defined(OTA_RAK15002_SD_STORE)
if (ethernet) {
_reason = "Ethernet active; bootloader lacks internal OTA";
return;
@@ -134,27 +180,55 @@ class OtaStoreAdaptiveNrf52 : public OtaStore {
OtaStore* active() {
select();
#if defined(RAK4631_COMBINED_ETHERNET)
if (_mode == QSPI_MODE && rak4631_ethernet_owns_spi()) return nullptr;
#if defined(RAK4631_COMBINED_ETHERNET) || defined(OTA_RAK15002_SD_STORE)
if ((_mode == QSPI_MODE || _mode == SD_MODE) && rak4631_ethernet_owns_spi()) return nullptr;
#endif
#if defined(OTA_RAK15002_SD_STORE)
if (_mode == SD_MODE) return &_sd;
#endif
return _mode == QSPI_MODE ? static_cast<OtaStore*>(&_storage.external) :
_mode == INTERNAL_MODE ? static_cast<OtaStore*>(&_storage.internal) : nullptr;
}
const OtaStore* active() const {
select();
#if defined(RAK4631_COMBINED_ETHERNET)
if (_mode == QSPI_MODE && rak4631_ethernet_owns_spi()) return nullptr;
#if defined(RAK4631_COMBINED_ETHERNET) || defined(OTA_RAK15002_SD_STORE)
if ((_mode == QSPI_MODE || _mode == SD_MODE) && rak4631_ethernet_owns_spi()) return nullptr;
#endif
#if defined(OTA_RAK15002_SD_STORE)
if (_mode == SD_MODE) return &_sd;
#endif
return _mode == QSPI_MODE ? static_cast<const OtaStore*>(&_storage.external) :
_mode == INTERNAL_MODE ? static_cast<const OtaStore*>(&_storage.internal) : nullptr;
}
public:
#if defined(RAK4631_COMBINED_ETHERNET)
bool selectStorage() {
#if defined(OTA_RAK15002_SD_STORE)
// A cancelled/cleared privileged fetch may return to its application
// backend without tearing down the independent SD cache owner. Never
// reselect while internal bootloader staging still contains data.
if (_bootloader_store && _mode == INTERNAL_MODE &&
_storage.internal.staged_size() == 0u) {
_storage.internal.~OtaStoreFlashNrf52();
_mode = UNSELECTED;
_bootloader_store = false;
}
_prepared = true;
#endif
select();
return _mode != UNSELECTED && _mode != UNSAFE;
}
#if defined(RAK4631_COMBINED_ETHERNET) || defined(OTA_RAK15002_SD_STORE)
// Only the main-loop handoff may reset the selection, after OtaContext has
// proved that no receive/apply session is active. Never discard staged data
// as a side effect of changing a network setting.
bool resetSelection() {
#if defined(OTA_RAK15002_SD_STORE)
if (_sd.staged_size() != 0u) return false;
if ((_mode == QSPI_MODE && _storage.external.staged_size() != 0u) ||
(_mode == INTERNAL_MODE && _storage.internal.staged_size() != 0u) ||
!_sd.releaseBus()) return false;
#endif
if (_mode == QSPI_MODE) {
if (_storage.external.staged_size() != 0u) return false;
_storage.external.~OtaStoreQspiNrf52();
@@ -162,6 +236,9 @@ public:
if (_storage.internal.staged_size() != 0u) return false;
_storage.internal.~OtaStoreFlashNrf52();
}
#if defined(OTA_RAK15002_SD_STORE)
_prepared = false;
#endif
_mode = UNSELECTED;
_reason = "not probed";
_bootloader_store = false;
@@ -172,17 +249,25 @@ public:
if (_mode == QSPI_MODE) _storage.external.~OtaStoreQspiNrf52();
else if (_mode == INTERNAL_MODE) _storage.internal.~OtaStoreFlashNrf52();
}
bool usesExternal() const { select(); return _mode == QSPI_MODE; }
bool usesExternal() const { select(); return _mode == QSPI_MODE || _mode == SD_MODE; }
bool usesQspi() const { select(); return _mode == QSPI_MODE; }
bool usesSd() const { select(); return _mode == SD_MODE; }
bool usesInternal() const { select(); return _mode == INTERNAL_MODE; }
const char* selectionReason() const { select(); return _reason; }
OtaStoreFlashNrf52& internalStore() { select(); return _storage.internal; }
OtaStoreQspiNrf52& externalStore() { select(); return _storage.external; }
uint32_t qspiCapacity() const { return usesExternal() ? _storage.external.capacity() : 0u; }
uint32_t jedec_id() const { return usesExternal() ? _storage.external.jedec_id() : 0u; }
uint8_t status1() const { return usesExternal() ? _storage.external.status1() : 0xFFu; }
const char* last_stage() const { return usesExternal() ? _storage.external.last_stage() : "inactive"; }
#if defined(OTA_RAK15002_SD_STORE)
OtaStoreSdNrf52& sdStore() { return _sd; }
#endif
uint32_t qspiCapacity() const { return usesQspi() ? _storage.external.capacity() : 0u; }
uint32_t jedec_id() const { return usesQspi() ? _storage.external.jedec_id() : 0u; }
uint8_t status1() const { return usesQspi() ? _storage.external.status1() : 0xFFu; }
const char* last_stage() const { return usesQspi() ? _storage.external.last_stage() : "inactive"; }
const char* last_error() const {
return usesExternal() ? _storage.external.last_error() : selectionReason();
#if defined(OTA_RAK15002_SD_STORE)
if (usesSd()) return _sd.last_error();
#endif
return usesQspi() ? _storage.external.last_error() : selectionReason();
}
bool begin(uint32_t n) override { OtaStore* s = active(); return s && s->begin(n); }
+33 -3
View File
@@ -13,9 +13,12 @@
#include <SPI.h>
#endif
#include <string.h>
#if defined(RAK4631_COMBINED_ETHERNET)
#if defined(RAK4631_COMBINED_ETHERNET) || defined(OTA_RAK15002_SD_STORE)
#include "../nrf52/Rak4631SharedSpi.h"
#endif
#if defined(OTA_RAK15002_SD_STORE)
#include "OtaSdSpiBusNrf52.h"
#endif
namespace mesh {
namespace ota {
@@ -327,12 +330,20 @@ static uint32_t probe_nor(uint8_t cs) {
#if defined(OTA_RAK_AUTO_STORE)
uint8_t OtaStoreQspiNrf52::autoDetect() {
#if defined(OTA_RAK15002_SD_STORE)
// SD and the RAK15001 use the same CS26. Even an unmounted/unsupported
// inserted card must never see the NOR wake or JEDEC command sequence.
if (ota_rak15002_sd_card_present()) return 0u;
#endif
#if defined(RAK4631_COMBINED_ETHERNET)
// Do not even bit-bang a JEDEC probe while the Ethernet task owns SPI.
// Leave the cached physical discovery unchanged for the next idle handoff.
if (rak4631_ethernet_owns_spi()) return 0u;
#endif
if (auto_detection != 0xFFu) return auto_detection;
#if defined(OTA_RAK15002_SD_STORE)
if (!rak4631_try_acquire_spi(RakSharedSpiOwner::Nor)) return 0u;
#endif
const uint8_t w25_cs = arduino_to_physical(31);
nrf_gpio_pin_set(w25_cs);
@@ -382,6 +393,9 @@ uint8_t OtaStoreQspiNrf52::autoDetect() {
auto_cs_pin = w25_cs;
auto_jedec_id = 0xEF4015UL;
}
#if defined(OTA_RAK15002_SD_STORE)
rak4631_release_spi(RakSharedSpiOwner::Nor);
#endif
return auto_detection;
}
@@ -487,6 +501,12 @@ bool OtaStoreQspiNrf52::waitMemoryReady(uint32_t timeout_ms) {
}
bool OtaStoreQspiNrf52::ensureFlash() {
#if defined(OTA_RAK15002_SD_STORE)
if (ota_rak15002_sd_card_present() || !rak4631_try_acquire_spi(RakSharedSpiOwner::Nor)) {
fail("SD card present or WisBlock SPI owned by SD/Ethernet");
return false;
}
#endif
#if defined(RAK4631_COMBINED_ETHERNET)
if (rak4631_ethernet_owns_spi()) {
fail("Ethernet owns WisBlock SPI; external OTA unavailable");
@@ -627,8 +647,13 @@ bool OtaStoreQspiNrf52::ensureFlash() {
}
void OtaStoreQspiNrf52::releaseFlash() {
#if defined(OTA_RAK15002_SD_STORE)
// The failed-acquire path must not touch somebody else's live controller,
// chip select or supply. No callback may forcibly overwrite this claim.
if (rak4631_spi_owner() != RakSharedSpiOwner::Nor) return;
#endif
bool send_power_down = _qspi_awake && !_memory_operation_pending;
#if defined(RAK4631_COMBINED_ETHERNET)
#if defined(RAK4631_COMBINED_ETHERNET) || defined(OTA_RAK15002_SD_STORE)
send_power_down = send_power_down && !rak4631_ethernet_owns_spi();
#endif
if (send_power_down) {
@@ -650,7 +675,7 @@ void OtaStoreQspiNrf52::releaseFlash() {
// disabling the peripheral. Trigger it after every successful ENABLE, even
// when wake or JEDEC identification failed before _qspi_ready was set.
if (_qspi_active) {
#if defined(RAK4631_COMBINED_ETHERNET)
#if defined(RAK4631_COMBINED_ETHERNET) || defined(OTA_RAK15002_SD_STORE)
// Also release the QSPI pin assignments after a successful operation so
// the Ethernet SPIM can safely acquire the same pads at an idle handoff.
const bool disconnect_pins = true;
@@ -699,6 +724,11 @@ void OtaStoreQspiNrf52::releaseFlash() {
_shared_radio_bus_acquired = false;
}
#endif
#if defined(OTA_RAK15002_SD_STORE)
// An uncertain program/erase retains exclusive ownership until a later
// successful status poll proves the NOR idle; never hand its nets to SD.
if (!_memory_operation_pending) rak4631_release_spi(RakSharedSpiOwner::Nor);
#endif
}
bool OtaStoreQspiNrf52::dmaReadAligned(uint32_t address, uint32_t length) {
+5 -2
View File
@@ -7,7 +7,9 @@
#endif
#if defined(OTA_QSPI_SHARED_WISBLOCK_SPI) && defined(ETHERNET_ENABLED) && \
!defined(RAK4631_COMBINED_ETHERNET)
!defined(RAK4631_COMBINED_ETHERNET) && \
!(defined(OTA_RAK15002_SD_STORE) && defined(NRF52_PLATFORM) && \
defined(RAK_4631) && defined(OTA_RAK_AUTO_STORE))
#error "WisBlock SPI OTA staging cannot share the bus/chip-select with Ethernet"
#endif
@@ -17,7 +19,8 @@
#error "Combined RAK4631 Ethernet requires its adaptive OTA and shared-SPI ownership contract"
#endif
#if defined(OTA_QSPI_SHARED_WISBLOCK_SPI) && defined(OTA_SD_STORE)
#if defined(OTA_QSPI_SHARED_WISBLOCK_SPI) && defined(OTA_SD_STORE) && \
!(defined(OTA_RAK15002_SD_STORE) && defined(RAK_4631) && defined(OTA_RAK_AUTO_STORE))
#error "WisBlock SPI OTA staging cannot share the bus/chip-select with SD staging"
#endif
+131 -18
View File
@@ -22,6 +22,14 @@
#define OTA_SD_SCK_MHZ 8
#endif
#if defined(OTA_RAK15002_SD_STORE)
// The mounted FAT/exFAT volume and both retained file handles (staging and
// archive) are lazy heap objects, not linked globals. Bind their total plus
// allocator metadata to the runtime RAM policy's separate 2 KiB allowance.
static_assert(sizeof(mesh::ota::OtaSdFs) + 2u * sizeof(mesh::ota::OtaSdFile) + 3u * 32u <= 2048u,
"RAK15002 FAT volume/file allocations exceed the RAM allowance");
#endif
namespace mesh {
namespace ota {
@@ -35,6 +43,7 @@ static void clear_sd_auth_record() {
__DSB();
}
#if !defined(OTA_SEEDER_ONLY)
static bool publish_sd_auth_record(const uint8_t record[MOTA_SD_AUTH_LEN]) {
volatile uint8_t* const retained =
reinterpret_cast<volatile uint8_t*>(MOTA_SD_AUTH_ADDR);
@@ -52,13 +61,17 @@ static bool publish_sd_auth_record(const uint8_t record[MOTA_SD_AUTH_LEN]) {
if (retained[i] != record[i]) return false;
return true;
}
#endif
} // namespace
const char* const OtaStoreSdNrf52::PATH = "/meshcore-ota.mota";
OtaStoreSdNrf52::OtaStoreSdNrf52()
: _sd(new (std::nothrow) SdFs()), _file(new (std::nothrow) FsFile()) {
#if !defined(OTA_RAK15002_SD_STORE)
: _sd(new (std::nothrow) OtaSdFs()), _file(new (std::nothrow) OtaSdFile())
#endif
{
// If an earlier reset did not enter OTAFIX, stale authorization must not arm a
// later unrelated reboot. OTAFIX runs before application constructors and
// consumes a valid record on the intended apply boot.
@@ -66,8 +79,59 @@ OtaStoreSdNrf52::OtaStoreSdNrf52()
}
OtaStoreSdNrf52::~OtaStoreSdNrf52() {
if (_file) { _file->close(); delete _file; }
if (_sd) { _sd->end(); delete _sd; }
resetStoreState();
if (_file) delete _file;
if (_sd) delete _sd;
releaseBusClaim();
}
bool OtaStoreSdNrf52::acquireBus() {
#if defined(OTA_RAK15002_SD_STORE)
if (!ota_rak15002_sd_card_present()) { fail("SD card absent"); return false; }
if (!rak4631_try_acquire_spi(RakSharedSpiOwner::Sd)) {
fail("WisBlock SPI owned by Ethernet or NOR"); return false;
}
_bus_owned = true;
if (!_sd) _sd = new (std::nothrow) OtaSdFs();
if (!_file) _file = new (std::nothrow) OtaSdFile();
if (!_sd || !_file) {
fail("SD allocation failed"); releaseBusClaim(); return false;
}
#endif
return true;
}
bool OtaStoreSdNrf52::busAccessAllowed() const {
#if defined(OTA_RAK15002_SD_STORE)
return _bus_owned && rak4631_spi_owner() == RakSharedSpiOwner::Sd
&& ota_rak15002_sd_card_present();
#else
return true;
#endif
}
void OtaStoreSdNrf52::releaseBusClaim() {
#if defined(OTA_RAK15002_SD_STORE)
if (!_bus_owned) return;
if (_bus_started) {
ota_sd_spi_bus().end();
// Leave the powered, unswitched card deselected, and stop driving the
// shared clock/data pads before another peripheral claims them.
digitalWrite(OTA_SD_CS_PIN, HIGH);
pinMode(OTA_SD_CS_PIN, OUTPUT);
pinMode(3, INPUT); pinMode(29, INPUT); pinMode(30, INPUT);
_bus_started = false;
}
rak4631_release_spi(RakSharedSpiOwner::Sd);
_bus_owned = false;
#endif
}
bool OtaStoreSdNrf52::releaseBus() {
if (_total) { fail("SD staged update is pinned"); return false; }
resetStoreState();
releaseBusClaim();
return true;
}
void OtaStoreSdNrf52::fail(const char* message) {
@@ -80,31 +144,48 @@ void OtaStoreSdNrf52::resetStoreState() {
_first_sector = 0;
_allocated_sectors = 0;
_planned_bootloader = false;
if (_file && *_file) _file->close();
if (_sd) _sd->end();
#if defined(OTA_RAK15002_SD_STORE)
// Ownership persists if a card is physically removed. Cleanup still owns
// the controller and can release it, but no other device may steal it.
const bool access = _bus_owned && rak4631_spi_owner() == RakSharedSpiOwner::Sd;
#else
const bool access = true;
#endif
if (access && _file && *_file) _file->close();
if (access && _sd) _sd->end();
_mounted = false;
}
bool OtaStoreSdNrf52::beginCardOnly() {
_error[0] = 0;
if (!acquireBus()) return false;
resetStoreState();
ota_sd_prepare_chip_select();
#if defined(OTA_RAK15002_SD_STORE)
_bus_started = true;
#endif
if (!_sd || !_file ||
!_sd->cardBegin(SdSpiConfig(OTA_SD_CS_PIN, DEDICATED_SPI,
SD_SCK_MHZ(OTA_SD_SCK_MHZ), &ota_sd_spi_bus()))) {
fail("SD card init failed");
resetStoreState(); releaseBusClaim();
return false;
}
return true;
}
bool OtaStoreSdNrf52::mount() {
if (_mounted) return true;
if (_mounted) return busAccessAllowed();
_error[0] = 0;
if (!acquireBus()) return false;
ota_sd_prepare_chip_select();
#if defined(OTA_RAK15002_SD_STORE)
_bus_started = true;
#endif
if (!_sd || !_file || !_sd->begin(SdSpiConfig(OTA_SD_CS_PIN, DEDICATED_SPI,
SD_SCK_MHZ(OTA_SD_SCK_MHZ), &ota_sd_spi_bus()))) {
fail("SD mount failed");
resetStoreState(); releaseBusClaim();
return false;
}
_mounted = true;
@@ -119,12 +200,14 @@ bool OtaStoreSdNrf52::formatCard(MainBoard& board) {
if (!_sd->format()) {
fail("SD format failed");
_sd->end();
releaseBusClaim();
return false;
}
board.serviceWatchdog();
if (!_sd->volumeBegin()) {
fail("SD remount after format failed");
_sd->end();
releaseBusClaim();
return false;
}
@@ -142,6 +225,7 @@ bool OtaStoreSdNrf52::eraseCard(MainBoard& board) {
if (!card || sector_count == 0) {
fail("SD sector count failed");
_sd->end();
releaseBusClaim();
return false;
}
@@ -159,6 +243,7 @@ bool OtaStoreSdNrf52::eraseCard(MainBoard& board) {
if (!card->erase(first_sector, last_sector)) {
fail("SD erase failed");
_sd->end();
releaseBusClaim();
return false;
}
first_sector += count;
@@ -168,9 +253,11 @@ bool OtaStoreSdNrf52::eraseCard(MainBoard& board) {
if (!card->syncDevice()) {
fail("SD erase sync failed");
_sd->end();
releaseBusClaim();
return false;
}
_sd->end();
releaseBusClaim();
return true;
}
@@ -257,8 +344,8 @@ static bool append_sd_list_entry(SdListState& state, const char* path, uint64_t
return true;
}
static bool scan_sd_files(FsFile& dir, SdListState& state) {
FsFile file;
static bool scan_sd_files(OtaSdFile& dir, SdListState& state) {
OtaSdFile file;
while (file.openNext(&dir, O_RDONLY)) {
state.board.serviceWatchdog();
const size_t parent_len = state.path_len;
@@ -309,7 +396,7 @@ bool OtaStoreSdNrf52::listFiles(MainBoard& board, uint16_t page,
_error[0] = 0;
if (!mount()) return false;
FsFile root = _sd->open("/", O_RDONLY);
OtaSdFile root = _sd->open("/", O_RDONLY);
if (!root || !root.isDir()) { fail("SD root directory open failed"); return false; }
SdListState state(board, (uint32_t)(page - 1) * SD_LIST_PAGE_SIZE);
bool scanned = ensure_list_path(state, 2) && scan_sd_files(root, state);
@@ -331,7 +418,37 @@ bool OtaStoreSdNrf52::listFiles(MainBoard& board, uint16_t page,
return true;
}
uint32_t OtaStoreSdNrf52::capacity() const {
if (!busAccessAllowed() || !_mounted || !_sd || !_sd->card()) return 0;
uint64_t bytes = (uint64_t)_sd->card()->sectorCount() * MOTA_SD_AUTH_SECTOR_SIZE;
return bytes > UINT32_MAX ? UINT32_MAX : (uint32_t)bytes;
}
#if defined(OTA_SEEDER_ONLY)
// The source-only context uses OtaStoreRam<1> for install fetches and this
// object only owns the FAT volume shared with OtaCacheSdNrf52. Its archive
// has its own file/read/write methods. Do not retain unreachable installer
// vtable code (or permit accidental staging/approval) in a Companion image.
bool OtaStoreSdNrf52::plan_layout(bool, uint32_t, uint32_t, uint32_t, bool) {
fail("source-only SD archive cannot stage/apply"); return false;
}
bool OtaStoreSdNrf52::begin(uint32_t) {
fail("source-only SD archive cannot stage/apply"); return false;
}
bool OtaStoreSdNrf52::set_meta_size(uint32_t) { return false; }
bool OtaStoreSdNrf52::write(uint32_t, const uint8_t*, uint32_t) { return false; }
bool OtaStoreSdNrf52::read(uint32_t, uint8_t*, uint32_t) const { return false; }
bool OtaStoreSdNrf52::finalize() { return false; }
void OtaStoreSdNrf52::checkpoint() {}
bool OtaStoreSdNrf52::reopen() { return false; }
bool OtaStoreSdNrf52::discard() { return false; }
void OtaStoreSdNrf52::clear() {}
bool OtaStoreSdNrf52::approve_for_bootloader(const uint8_t[32], const uint8_t[32]) {
fail("source-only SD archive cannot stage/apply"); return false;
}
#else
bool OtaStoreSdNrf52::locate_file() {
if (!busAccessAllowed()) return false;
Sector_t first = 0, last = 0;
if (!_file || !_file->contiguousRange(&first, &last) || last < first) {
fail("OTA file is not contiguous");
@@ -418,7 +535,7 @@ bool OtaStoreSdNrf52::set_meta_size(uint32_t meta_bytes) {
}
bool OtaStoreSdNrf52::write(uint32_t offset, const uint8_t* data, uint32_t len) {
if (!_file || !*_file || (uint64_t)offset + len > _total || !_file->seekSet(offset) ||
if (!busAccessAllowed() || !_file || !*_file || (uint64_t)offset + len > _total || !_file->seekSet(offset) ||
_file->write(data, len) != len) {
fail("SD OTA write failed");
return false;
@@ -427,20 +544,15 @@ bool OtaStoreSdNrf52::write(uint32_t offset, const uint8_t* data, uint32_t len)
}
bool OtaStoreSdNrf52::read(uint32_t offset, uint8_t* buf, uint32_t len) const {
if (!_file || !*_file || (uint64_t)offset + len > _total || !_file->seekSet(offset) ||
if (!busAccessAllowed() || !_file || !*_file || (uint64_t)offset + len > _total || !_file->seekSet(offset) ||
_file->read(buf, len) != (int)len) {
return false;
}
return true;
}
uint32_t OtaStoreSdNrf52::capacity() const {
if (!_mounted || !_sd || !_sd->card()) return 0;
uint64_t bytes = (uint64_t)_sd->card()->sectorCount() * MOTA_SD_AUTH_SECTOR_SIZE;
return bytes > UINT32_MAX ? UINT32_MAX : (uint32_t)bytes;
}
bool OtaStoreSdNrf52::finalize() {
if (!busAccessAllowed()) return false;
SdCard* const card = _sd ? _sd->card() : nullptr;
if (!_file || !*_file || !card || !_file->sync() || !card->syncDevice()) {
fail("SD OTA sync failed");
@@ -450,7 +562,7 @@ bool OtaStoreSdNrf52::finalize() {
}
void OtaStoreSdNrf52::checkpoint() {
if (_file && *_file) {
if (busAccessAllowed() && _file && *_file) {
_file->sync();
if (_sd->card()) _sd->card()->syncDevice();
}
@@ -604,6 +716,7 @@ bool OtaStoreSdNrf52::approve_for_bootloader(
}
return true;
}
#endif // OTA_SEEDER_ONLY
} // namespace ota
} // namespace mesh
+13 -5
View File
@@ -3,9 +3,7 @@
#if defined(NRF52_PLATFORM) && defined(OTA_SD_STORE)
#include "OtaStore.h"
class SdFs;
class FsFile;
#include "OtaSdTypesNrf52.h"
namespace mesh {
@@ -55,6 +53,9 @@ public:
// Mount-only probe for the MeshTower selector. Never opens/removes staging
// files or changes their live session state.
bool probeMedia() { return mount() && capacity() != 0u; }
// Caller first detaches the archive and proves no queued/pinned SD source
// remains. Never unmount an installable or partially received container.
bool releaseBus();
const char* last_error() const { return _error; }
private:
@@ -62,14 +63,21 @@ private:
static const char* const PATH;
bool mount();
bool acquireBus();
bool busAccessAllowed() const;
void releaseBusClaim();
bool beginCardOnly();
bool locate_file();
void resetStoreState();
void fail(const char* message);
SdFs* _sd = nullptr;
mutable FsFile* _file = nullptr;
OtaSdFs* _sd = nullptr;
mutable OtaSdFile* _file = nullptr;
bool _mounted = false;
#if defined(OTA_RAK15002_SD_STORE)
bool _bus_owned = false;
bool _bus_started = false;
#endif
bool _planned_bootloader = false;
uint32_t _total = 0;
uint32_t _first_sector = 0;
+532 -287
View File
@@ -3,7 +3,7 @@
#include <stddef.h>
// AUTO-GENERATED by tools/mota/gen_targets.py - do not edit by hand.
// 659 OTA-capable build targets. Maps target_id (= sha2-256:4 of the target name, LE uint32)
// 683 OTA-capable build targets. Maps target_id (= sha2-256:4 of the target name, LE uint32)
// to the human-readable env name, so a node/tool can name a target seen over the air WITHOUT
// transmitting the string in the .mota / LoRa protocol. Regenerate when the OTA env set changes.
// Size-constrained receivers can set OTA_TARGET_NAME_TABLE=0. They still match targets by ID;
@@ -19,6 +19,9 @@
#if OTA_TARGET_NAME_TABLE && OTA_TARGET_NAME_FRONT_CODED
#include "tinf/tinf.h"
#if defined(OTA_RAK15002_SD_STORE) && defined(COMPANION_RADIO_FULL)
#include <stdlib.h>
#endif
#endif
namespace mesh { namespace ota {
@@ -47,68 +50,71 @@ inline const char* ota_target_env_name(uint32_t target_id, char* buffer, size_t
0x2f9f8e7d, 0xc9dc5d8c, 0x34b0e5f3, 0xb4d0ad97, 0xcfa0e233, 0x17d5e1ff, 0xc99313c6, 0xffb3854f,
0x21a73973, 0x3067f656, 0xfdb25057, 0x836e5931, 0x0f1767cd, 0x57172fcc, 0xa529b4f5, 0x85a8c944,
0x3c60586d, 0x0a9dbbf0, 0xb519a89f, 0xa3e8d8ce, 0xb16009a0, 0xdc780e80, 0x28b8007c, 0x4aa63180,
0x6fddcbf1, 0xf4fb5484, 0x2e5384e2, 0xb62a1e75, 0x9e64845d, 0x19232e8b, 0xfaf164f4, 0x38b948b4,
0x462ddb9b, 0xed21ca4f, 0x9f5f39a7, 0x081d2219, 0x7c498bc4, 0xc283ad60, 0x4fbdc1e1, 0xc797fd73,
0xab9871c8, 0x2075687c, 0x5d726067, 0x9969bbc9, 0x804f7a3a, 0x0823a21a, 0x22f900de, 0xaca0e153,
0x516c9c13, 0xcb1c2e65, 0xd1b29b18, 0x644bb68d, 0xd19a9759, 0xde40f56f, 0x6dc15ab3, 0xc59d294e,
0xceeddfaf, 0x21519537, 0x382bb181, 0xad1bcd63, 0x5a2980e6, 0x90581c83, 0x90a6a854, 0x356df17c,
0x99a084fc, 0x64c9dfc9, 0x2a2ffc92, 0x2c13b8e9, 0x1ee7d867, 0xb016979a, 0xb49b6b55, 0x01d8f124,
0x86fb85f0, 0xd33b0986, 0x1a4d0096, 0xa8f5940f, 0x1cf5e14c, 0x2707d3d1, 0x86ac3b1b, 0x7af848da,
0xd522a14f, 0x76657d76, 0x21ee03bd, 0x5977d941, 0xe2a1907b, 0xbb0ca8db, 0xf75feb3e, 0xb0a1e71f,
0xbc9cdc70, 0x19e31fc6, 0xa01b330e, 0x36c78d86, 0xdf722326, 0xfa901c87, 0xeebe3469, 0xb0f5aff6,
0x5729ad07, 0xaa2aef42, 0xb4cb035a, 0xbb429dde, 0x883e3edc, 0x4a9d1c6c, 0xa0965a6b, 0x59ea25fa,
0x1758bdb9, 0x1313f16d, 0x0372b0f1, 0xa2fb4192, 0xdd39457b, 0x7964461c, 0x1d1c0d92, 0x5fc52bcb,
0x0dacb683, 0xe792a051, 0x2d5ea842, 0x30c59b1d, 0xab74db9e, 0xeed78ce4, 0x7778b7e1, 0x0b6847ed,
0xab3e1ad3, 0x04a8b763, 0xc456437e, 0xb3ecafca, 0xf3afcde6, 0x87513d56, 0xca812b41, 0x18594231,
0x188f3ac1, 0x402055ec, 0x12393f1f, 0x16fbab07, 0xed3ef5b8, 0x8ea86381, 0x09f57289, 0x148aeaab,
0xcbb2bf38, 0x6e9f7fec, 0x9a340cd3, 0x6a25d024, 0xc5a2cb41, 0xc4a11710, 0x753947a4, 0xe6fa98b4,
0xf8c27cf4, 0xa139c6ae, 0x90e227d9, 0x89e8d957, 0x812f5e52, 0x61f961c5, 0x1ddd15c8, 0x49d67d0d,
0xbc003322, 0x835b70c0, 0xd7460575, 0x36acced5, 0x33e2c171, 0x5be91148, 0xa165ae99, 0x04c5c2b7,
0x6c1ca659, 0xa295fb04, 0x61c405ca, 0xe893fe91, 0x2b599d31, 0xf1442d25, 0x15082ede, 0x6ff350d1,
0xe03d0cd9, 0x82e1eb2c, 0x0ee19b12, 0x6c67ed89, 0xd1c05d0d, 0xb90150b0, 0x7285bdb3, 0x77cadc99,
0xc177e6cd, 0xf7a38993, 0x83762f33, 0x86cadda9, 0x7e49da49, 0x305d6f92, 0xbc6b870b, 0x78845fdd,
0x6d35f434, 0xa52d76a8, 0xd8de9639, 0x8ab5c51b, 0x80097676, 0xffe70df5, 0x3da80417, 0x47ca3c13,
0x28b499c7, 0xf1ab4c28, 0xe5331f02, 0xfba7177b, 0xde7dc472, 0x3625bb80, 0x69df7765, 0x988cc4e1,
0xe9fbc9be, 0x40818863, 0x65deac33, 0xd86cf67a, 0xed73ac03, 0xab0d550a, 0x1bd2a266, 0x3655c748,
0x9a5ae8fc, 0x1e9eb97a, 0x139536f4, 0x0716c724, 0x8de23fa7, 0xb92ec96b, 0x420ebc63, 0x967ccaee,
0x8021afa2, 0x5f7c7688, 0xbd04e6e7, 0x1aeac884, 0x07849209, 0x0c79b100, 0xbfb60e7c, 0x8a63de3a,
0x96036a68, 0xb76a745c, 0x43023193, 0xa7d97db7, 0x468a133e, 0xb59ea2ed, 0xcb75495b, 0xbe372c6d,
0xb762985a, 0x6215e9eb, 0x07907102, 0x3898ea56, 0x2fb7e218, 0x8c61d4c8, 0x4f722e70, 0x694eed25,
0x85b8b9d7, 0xe8421e36, 0x82eaaf9e, 0x9f9caba4, 0xe4a2de74, 0x582c7826, 0xaeb637f1, 0x86813271,
0x67b2ff84, 0xd0e2e616, 0x3f9edfe3, 0xc23f8082, 0x0e74ae16, 0x8f256e56, 0x6915c6e1, 0xf1407f39,
0x1b4dc5ab, 0xe90a3181, 0x3b1b4237, 0x21c6d08d, 0x34302c14, 0x268d6fc1, 0xeabe3b40, 0x3c172d27,
0xf7c5d584, 0x81960cc0, 0x504020ea, 0x9d8b60ae, 0xa2ce002f, 0xba4f47f3, 0x6f16479e, 0x1018e5d1,
0xc0ab5040, 0xa2bf90bd, 0xe6bff8f9, 0xb9cd26cb, 0x9bc34bd5, 0x144504b0, 0xdbfe2675, 0xb00241d0,
0x88e77fee, 0xae2d9caa, 0xa0043e00, 0x26bbf4ed, 0xdcc0f158, 0xe3320463, 0x852b928a, 0x0ec870e9,
0x8f00c1f2, 0xcea95e20, 0xd45277ff, 0x2cad9a9d, 0x02a4a8c1, 0x4a709348, 0x35d960bd, 0x62977023,
0x17a7ed8c, 0x07f5f5b5, 0xfd0d4b8f, 0x8d88f33c, 0xf5afb7dd, 0x3a0ea830, 0x6ebf28cb, 0x2d589527,
0x77d0810c, 0x11b131e7, 0x54850763, 0x0fa61805, 0x8039574f, 0xa65555d6, 0x22b1804c, 0xa9f631b1,
0x49d11eca, 0xd41f0638, 0xe3105eb8, 0xf8259880, 0x063f1f00, 0x70d71f92, 0xa8a79da5, 0xc0d3815b,
0x966ac88e, 0x104b3f8b, 0x23b49348, 0xc42cc90d, 0xc24791c3, 0x15e11067, 0x2dbb2669, 0x90799c32,
0x881a6a48, 0xf26af51c, 0x8174cbdc, 0x0bf0f061, 0x2509dd80, 0x56293b24, 0x8c81078c, 0xbd6a1c97,
0xf4d98c12, 0xccac4283, 0xa68c09e9, 0x5f606274, 0x84923735, 0x2c0b8b05, 0xdba69401, 0xcb6555ee,
0xd60daf25, 0x410bd800, 0x9aca380e, 0xd8641710, 0x4d0b1601, 0xab2ac09e, 0xa17fc4f7, 0xe4b2bd22,
0x90c4eb3f, 0x19d9ce91, 0x121cc72f, 0xfa8510fa, 0xd34d8461, 0x98077550, 0x11628e29, 0x2ed959aa,
0xf1e7cadb, 0xced7e127, 0x6925aaf0, 0x2fa509c1, 0xa52ba3c8, 0xaf0b81dd, 0x9f4d58ac, 0x7a55dec3,
0x5e69738a, 0x48d3b5d4, 0x14e6965d, 0x76f3c984, 0x41e33a97, 0x29a0da19, 0x29bc38dc, 0x05f5ffae,
0xc62da371, 0xf1d3c5a8, 0xc6d55752, 0x60683191, 0x8a7cb79b, 0x4fa4ae55, 0xd1d61a8b, 0x9cb0b232,
0x51fa1a49, 0xdb73638b, 0xd904c2d5, 0x3a14aac7, 0x620569f4, 0x08483f6b, 0x0db48f35, 0x20532f6c,
0x293ced32, 0xb38ebf22, 0x7c89c73d, 0x2d7c11e8, 0x5bb567cd, 0x61b32037, 0xad1cd53d, 0x0e18b360,
0x3caf966e, 0xcb59ea4e, 0xe3a28284, 0xa8cb8e8b, 0x05747eb6, 0x75587f24, 0xf2128c81, 0x1b34e7b4,
0x8e549407, 0x6ca142f4, 0x1deac038, 0x2a2f303b, 0x0ba4453d, 0xf8d1958f, 0x75e85141, 0xd62ed92e,
0x7abaf0d5, 0x87edc08b, 0x3c49caf8, 0x58689ef9, 0x91878dd8, 0x72a93c45, 0x4dbf035e, 0xff4102d6,
0x2f9a9185, 0x000fc519, 0x7b071fa0, 0x3e55e34d, 0x54606a4f, 0x16482630, 0xce0f07ad, 0x01c43d49,
0x51fe3801, 0x857149e5, 0xd9d64938, 0xcb7da3f5, 0x9952fbe3, 0x3729cd74, 0xdc27c37c, 0x9b9f8775,
0xb27514a3, 0xe9ce038c, 0xe295a7a4, 0x189660b3, 0x99907475, 0x2082078d, 0x24a94c81, 0x3750e80d,
0xe4b49973, 0xe1372b52, 0x5a79bac1, 0x6f339514, 0x8eaa289e, 0x70e91bd9, 0x5a7e9c2d, 0x1211b151,
0x0a32044b, 0xa13fe853, 0x79b9ff01, 0xc3decea2, 0x3c012018, 0x6afd6f75, 0x7517d400, 0xd7598668,
0x61bdb52a, 0x413287d8, 0xe8cd2377, 0x3309bdaf, 0x7330afbb, 0x9df48ce6, 0xa5cb783a, 0x4b9f8283,
0x311846c9, 0x8b6b29b7, 0x37928233, 0x88ac45da, 0x087f99c0, 0xdf41affe, 0x2afe6c63, 0xf2d61a1c,
0x14b1b097, 0xec334f3f, 0x1da30414, 0xae8fe1c9, 0x67acd73f, 0xfb118ed8, 0xe4213729, 0x1298038d,
0x2c47acff, 0x7d641646, 0x51efdbfe, 0xf2aada5b, 0xe59c9373, 0x236f605c, 0x93994fed, 0x66c47cfd,
0x48061a01, 0x7ed7137d, 0xa01688a7, 0xa5f4a562, 0xef83222b, 0xc36ec867, 0xb932238e, 0x92f5bbea,
0x26d91ab2, 0x708ac4dc, 0xdc51bf13, 0xc27f8304, 0x7190df40, 0x609d50b3, 0x5718c7ea, 0x37443a5b,
0x603384a7, 0xce3ef40f, 0x475a2a6a, 0xf4175d2c, 0x3d831006, 0x556eb1bc, 0xb67370b3, 0x0c689b90,
0x6fddcbf1, 0xf4fb5484, 0x2e5384e2, 0xb62a1e75, 0x9e64845d, 0x19232e8b, 0x10f41878, 0xfaf164f4,
0x38b948b4, 0x462ddb9b, 0xed21ca4f, 0x9f5f39a7, 0x081d2219, 0x7c498bc4, 0xc283ad60, 0x4fbdc1e1,
0xc797fd73, 0xab9871c8, 0x2075687c, 0x5d726067, 0x9969bbc9, 0x804f7a3a, 0x0823a21a, 0x22f900de,
0xaca0e153, 0x516c9c13, 0xcb1c2e65, 0xd1b29b18, 0x644bb68d, 0xd19a9759, 0xde40f56f, 0x6dc15ab3,
0xc59d294e, 0xceeddfaf, 0x21519537, 0x382bb181, 0xad1bcd63, 0x5a2980e6, 0x90581c83, 0x90a6a854,
0x356df17c, 0x99a084fc, 0x64c9dfc9, 0x2a2ffc92, 0x2c13b8e9, 0x1ee7d867, 0xb016979a, 0xb49b6b55,
0x01d8f124, 0x64140fd9, 0x86fb85f0, 0xd33b0986, 0x1a4d0096, 0xa8f5940f, 0x1cf5e14c, 0x2707d3d1,
0x86ac3b1b, 0x7af848da, 0xd522a14f, 0x76657d76, 0x21ee03bd, 0x5977d941, 0xe2a1907b, 0xbb0ca8db,
0xf75feb3e, 0xb0a1e71f, 0xbc9cdc70, 0x19e31fc6, 0xa01b330e, 0x36c78d86, 0xdf722326, 0xfa901c87,
0xeebe3469, 0xb0f5aff6, 0x5729ad07, 0xaa2aef42, 0xb4cb035a, 0xbb429dde, 0x883e3edc, 0x4a9d1c6c,
0xa0965a6b, 0x59ea25fa, 0x1758bdb9, 0x1313f16d, 0x0372b0f1, 0xa2fb4192, 0xdd39457b, 0x7964461c,
0x1d1c0d92, 0x5fc52bcb, 0x0dacb683, 0xe792a051, 0x2d5ea842, 0x30c59b1d, 0xab74db9e, 0xeed78ce4,
0x7778b7e1, 0x0b6847ed, 0xab3e1ad3, 0x04a8b763, 0xc456437e, 0xb3ecafca, 0xf3afcde6, 0x87513d56,
0xca812b41, 0x18594231, 0x188f3ac1, 0x402055ec, 0x12393f1f, 0x16fbab07, 0xed3ef5b8, 0x8ea86381,
0x09f57289, 0x148aeaab, 0xcbb2bf38, 0x6e9f7fec, 0x9a340cd3, 0x6a25d024, 0xc5a2cb41, 0xc4a11710,
0x753947a4, 0xe6fa98b4, 0xf8c27cf4, 0xa139c6ae, 0x90e227d9, 0x89e8d957, 0x812f5e52, 0x61f961c5,
0x1ddd15c8, 0x49d67d0d, 0xbc003322, 0x835b70c0, 0xd7460575, 0x36acced5, 0x33e2c171, 0x5be91148,
0xa165ae99, 0x04c5c2b7, 0x6c1ca659, 0xa295fb04, 0x61c405ca, 0xe893fe91, 0x2b599d31, 0xf1442d25,
0x15082ede, 0x6ff350d1, 0xe03d0cd9, 0x82e1eb2c, 0x0ee19b12, 0x6c67ed89, 0xd1c05d0d, 0xb90150b0,
0x7285bdb3, 0x77cadc99, 0xc177e6cd, 0xf7a38993, 0x83762f33, 0x86cadda9, 0x7e49da49, 0x305d6f92,
0xbc6b870b, 0x78845fdd, 0x6d35f434, 0xa52d76a8, 0xd8de9639, 0x8ab5c51b, 0x80097676, 0xffe70df5,
0x3da80417, 0x47ca3c13, 0x28b499c7, 0xf1ab4c28, 0xe5331f02, 0xfba7177b, 0xde7dc472, 0x3625bb80,
0x69df7765, 0x988cc4e1, 0xe9fbc9be, 0x40818863, 0x65deac33, 0xd86cf67a, 0xed73ac03, 0xab0d550a,
0x1bd2a266, 0x3655c748, 0x9a5ae8fc, 0x1e9eb97a, 0x139536f4, 0x0716c724, 0x8de23fa7, 0xb92ec96b,
0x420ebc63, 0x967ccaee, 0x8021afa2, 0x5f7c7688, 0xbd04e6e7, 0x1aeac884, 0x07849209, 0x0c79b100,
0xbfb60e7c, 0x8a63de3a, 0x96036a68, 0xb76a745c, 0x43023193, 0xa7d97db7, 0x468a133e, 0xb59ea2ed,
0xcb75495b, 0xbe372c6d, 0xb762985a, 0x6215e9eb, 0x07907102, 0x3898ea56, 0x2fb7e218, 0x8c61d4c8,
0x4f722e70, 0x694eed25, 0x85b8b9d7, 0xe8421e36, 0x82eaaf9e, 0x9f9caba4, 0xe4a2de74, 0x582c7826,
0xaeb637f1, 0x86813271, 0x67b2ff84, 0xd0e2e616, 0x3f9edfe3, 0xc23f8082, 0x0e74ae16, 0x8f256e56,
0x6915c6e1, 0xf1407f39, 0x1b4dc5ab, 0xe90a3181, 0x3b1b4237, 0x21c6d08d, 0x34302c14, 0x268d6fc1,
0xeabe3b40, 0x3c172d27, 0xf7c5d584, 0x81960cc0, 0x504020ea, 0x9d8b60ae, 0xa2ce002f, 0xba4f47f3,
0x6f16479e, 0x1018e5d1, 0xc0ab5040, 0xa2bf90bd, 0xe6bff8f9, 0xb9cd26cb, 0x9bc34bd5, 0x144504b0,
0xdbfe2675, 0xb00241d0, 0x88e77fee, 0xae2d9caa, 0xa0043e00, 0x26bbf4ed, 0xdcc0f158, 0xe3320463,
0x852b928a, 0x0ec870e9, 0x8f00c1f2, 0xcea95e20, 0xd45277ff, 0x2cad9a9d, 0x02a4a8c1, 0x4a709348,
0x35d960bd, 0x62977023, 0x17a7ed8c, 0x07f5f5b5, 0xfd0d4b8f, 0x8d88f33c, 0xf5afb7dd, 0x3a0ea830,
0x6ebf28cb, 0x2d589527, 0x77d0810c, 0x11b131e7, 0x54850763, 0x0fa61805, 0x8039574f, 0xa65555d6,
0x22b1804c, 0xa9f631b1, 0x49d11eca, 0xd41f0638, 0xe3105eb8, 0xba76cc33, 0x944263f8, 0xc7a8c754,
0xf8259880, 0x063f1f00, 0x70d71f92, 0xa8a79da5, 0xc0d3815b, 0x966ac88e, 0x104b3f8b, 0x23b49348,
0xc42cc90d, 0xc24791c3, 0x15e11067, 0x2dbb2669, 0x90799c32, 0x881a6a48, 0xf26af51c, 0x8174cbdc,
0x0bf0f061, 0x2509dd80, 0x56293b24, 0x8c81078c, 0xbd6a1c97, 0xf4d98c12, 0xccac4283, 0xa68c09e9,
0x5f606274, 0x84923735, 0x2c0b8b05, 0xdba69401, 0xcb6555ee, 0xd60daf25, 0x410bd800, 0x9aca380e,
0xd8641710, 0x4d0b1601, 0xab2ac09e, 0xa17fc4f7, 0xe4b2bd22, 0x90c4eb3f, 0x19d9ce91, 0x121cc72f,
0xfa8510fa, 0xd34d8461, 0x98077550, 0x11628e29, 0x2ed959aa, 0xf1e7cadb, 0xced7e127, 0x6925aaf0,
0x2fa509c1, 0xa52ba3c8, 0xaf0b81dd, 0x9f4d58ac, 0x7a55dec3, 0x5e69738a, 0x48d3b5d4, 0x14e6965d,
0x76f3c984, 0x41e33a97, 0x29a0da19, 0x29bc38dc, 0xd5fc71d4, 0x05f5ffae, 0xc62da371, 0x96fc4c26,
0x8a6dc63b, 0xb48950bb, 0xf1d3c5a8, 0xc6d55752, 0x60683191, 0x8a7cb79b, 0x4fa4ae55, 0xd1d61a8b,
0x9cb0b232, 0x51fa1a49, 0xdb73638b, 0xd904c2d5, 0x3a14aac7, 0x620569f4, 0x08483f6b, 0x0db48f35,
0x20532f6c, 0x293ced32, 0xb38ebf22, 0x7c89c73d, 0x2d7c11e8, 0x5bb567cd, 0x61b32037, 0xad1cd53d,
0x0e18b360, 0x3caf966e, 0xcb59ea4e, 0xe3a28284, 0xa8cb8e8b, 0x05747eb6, 0x75587f24, 0xf2128c81,
0x1b34e7b4, 0x8e549407, 0x6ca142f4, 0x1deac038, 0x2a2f303b, 0x0ba4453d, 0xf8d1958f, 0x75e85141,
0xd62ed92e, 0x7abaf0d5, 0x87edc08b, 0xc50b0e48, 0x3c49caf8, 0x58689ef9, 0x91878dd8, 0x72a93c45,
0x347e087a, 0x4dbf035e, 0xff4102d6, 0x2f9a9185, 0x000fc519, 0x7b071fa0, 0x3e55e34d, 0x54606a4f,
0x16482630, 0xce0f07ad, 0x01c43d49, 0x51fe3801, 0x857149e5, 0xd9d64938, 0xcb7da3f5, 0x9952fbe3,
0x3729cd74, 0xdc27c37c, 0x9b9f8775, 0xb27514a3, 0xe9ce038c, 0xe295a7a4, 0x189660b3, 0x99907475,
0x2082078d, 0x24a94c81, 0x3750e80d, 0xe4b49973, 0xe1372b52, 0x5a79bac1, 0x6f339514, 0x8eaa289e,
0x70e91bd9, 0x5a7e9c2d, 0x1211b151, 0x0a32044b, 0xa13fe853, 0x79b9ff01, 0xc3decea2, 0x3c012018,
0x6afd6f75, 0x7517d400, 0xd7598668, 0x61bdb52a, 0x413287d8, 0xe8cd2377, 0x3309bdaf, 0x7330afbb,
0x9df48ce6, 0xa5cb783a, 0x4b9f8283, 0x311846c9, 0x8b6b29b7, 0x37928233, 0x88ac45da, 0x087f99c0,
0xdf41affe, 0x2afe6c63, 0xf2d61a1c, 0x14b1b097, 0xec334f3f, 0x1da30414, 0xae8fe1c9, 0x67acd73f,
0xfb118ed8, 0xe4213729, 0x1298038d, 0x2c47acff, 0x7d641646, 0x51efdbfe, 0xf2aada5b, 0xe59c9373,
0x236f605c, 0x93994fed, 0x66c47cfd, 0x48061a01, 0x7ed7137d, 0xa01688a7, 0xa5f4a562, 0xef83222b,
0xc36ec867, 0xb932238e, 0x92f5bbea, 0x26d91ab2, 0x708ac4dc, 0xdc51bf13, 0xc27f8304, 0x7190df40,
0x609d50b3, 0x5718c7ea, 0x7bbf1081, 0xb151c2aa, 0x3b0174fd, 0xc1fd2578, 0xeb440352, 0xa297f85a,
0xcbe54778, 0x0343bdf0, 0x3de413b1, 0x37443a5b, 0x603384a7, 0x76456c88, 0x28c4948b, 0xb34a83c5,
0x63fe9591, 0xce3ef40f, 0x475a2a6a, 0xf4175d2c, 0x3d831006, 0x556eb1bc, 0xb67370b3, 0x0c689b90,
0x91eac424, 0x7a84a8c4, 0x67843a5f, 0x0e83fd51, 0xfad357ab, 0x16af5c67, 0x8c105104, 0xa80042a0,
0x4bff0748, 0x77c8f2b5, 0x71ee0e15, 0x8824cd0c, 0xe5439198, 0xe90a6e74, 0x833e5cdc, 0x93145fbb,
0x8d3dd6d5, 0x33a154ff, 0x6658b418, 0x0cfb9c00, 0x964f4f5c, 0x0a5dc760, 0xd08496ce, 0x293ac2c8,
@@ -116,19 +122,205 @@ inline const char* ota_target_env_name(uint32_t target_id, char* buffer, size_t
0x396fecc7, 0xf94e3407, 0xe1930103, 0xa4740508, 0x9f19bcd1, 0xfcebff61, 0xac5c1405, 0xccb734e2,
0x634ff632, 0xf967a300, 0xa0cc1c36,
};
#if defined(OTA_RAK15002_SD_STORE) && defined(COMPANION_RADIO_FULL)
enum { DECODE_CHUNK_BYTES = 4096 };
static const uint16_t OFFSETS[] = {
0, 227, 448, 665, 896, 1112, 1319, 1536, 1768, 1937, 2118, 2349,
2591, 2847, 3082, 3330, 3535, 3739, 3912, 4128, 4330,
0, 1066, 2165, 2715,
};
static const uint16_t RAW_LENGTHS[] = {
510, 512, 505, 509, 505, 512, 494, 506, 512, 499, 511, 507,
507, 509, 481, 501, 498, 511, 488, 388,
4087, 4061, 2042,
};
static const uint16_t FIRSTS[] = {
0, 27, 64, 95, 121, 158, 192, 226, 262, 295, 330, 366,
398, 431, 463, 486, 522, 554, 596, 628, 659,
0, 274, 536, 683,
};
// 4330 encoded bytes; 20 independent chunks, at most 512 decoded bytes.
// 2715 encoded bytes; 3 independent chunks, at most 4096 decoded bytes.
static const uint8_t NAMES[] = {
0xad, 0x57, 0x57, 0x9b, 0xa3, 0x38, 0x10, 0xd4, 0xe5, 0xbb, 0xc9, 0x39, 0x3a, 0x6f, 0xce, 0x36,
0xde, 0xf8, 0x76, 0x69, 0x73, 0xce, 0x8f, 0xfd, 0xc9, 0x20, 0xaf, 0xf9, 0x0c, 0x88, 0x93, 0x34,
0xe9, 0xdf, 0xdf, 0x08, 0x31, 0xb8, 0xd1, 0xca, 0x6c, 0xde, 0x09, 0xc2, 0x1a, 0xba, 0xaa, 0xab,
0xab, 0x1b, 0x44, 0xee, 0x0e, 0x8e, 0x14, 0x83, 0xbb, 0xfc, 0x15, 0xbd, 0xf2, 0xba, 0x0f, 0x3e,
0x8f, 0x53, 0x9a, 0x84, 0x3c, 0x01, 0x41, 0x83, 0x90, 0xc3, 0x20, 0x62, 0xa4, 0xb1, 0x27, 0x07,
0x64, 0x61, 0x1c, 0x4a, 0x09, 0x31, 0x0f, 0x58, 0x4c, 0x16, 0x5e, 0xb1, 0x94, 0x51, 0xc5, 0x04,
0x59, 0x10, 0xf9, 0x0a, 0xf8, 0x40, 0x32, 0xb1, 0xcf, 0x04, 0xc4, 0xff, 0x29, 0x45, 0x16, 0x39,
0x8f, 0xc1, 0x7c, 0x40, 0xb6, 0xad, 0xbd, 0x05, 0xc5, 0x44, 0x1c, 0x26, 0x34, 0x02, 0x7f, 0x44,
0x15, 0x21, 0xf7, 0xff, 0x7a, 0x73, 0xe3, 0xa6, 0x07, 0xfd, 0xee, 0x6b, 0x78, 0xca, 0xe4, 0x08,
0x1e, 0x87, 0xca, 0x49, 0xe3, 0xb4, 0xa6, 0xb1, 0x86, 0x68, 0xac, 0x15, 0xe0, 0x11, 0x17, 0x14,
0xb8, 0xa2, 0x90, 0x70, 0x60, 0x87, 0x8a, 0x09, 0x1d, 0x5d, 0xb2, 0x44, 0x72, 0x21, 0xc9, 0x3a,
0xe2, 0xf2, 0x5b, 0x86, 0x70, 0xf7, 0xdd, 0xdf, 0xf0, 0x42, 0x70, 0x70, 0xc5, 0x22, 0xb5, 0xcf,
0x8f, 0x36, 0xf7, 0x46, 0x50, 0x7f, 0xcc, 0x84, 0x0e, 0xe6, 0xa4, 0x7c, 0x5e, 0x53, 0xde, 0x42,
0x30, 0x5b, 0x9f, 0x47, 0x79, 0x1b, 0x83, 0xbc, 0xa7, 0xca, 0x1f, 0xf5, 0xdc, 0xa5, 0xc1, 0x72,
0xfc, 0xc0, 0x12, 0x26, 0x42, 0x1f, 0xee, 0x7a, 0x1e, 0xce, 0x6c, 0x4e, 0x1e, 0xf6, 0xbc, 0x63,
0x7d, 0x8b, 0x04, 0x97, 0x6f, 0x4f, 0xd6, 0x7f, 0xbc, 0x7e, 0xf1, 0xec, 0xf9, 0xfb, 0x2c, 0x74,
0x1e, 0x55, 0x33, 0x5e, 0x34, 0xfb, 0x82, 0x2c, 0x65, 0x3c, 0xf6, 0x05, 0x59, 0x2c, 0xd7, 0xec,
0x87, 0xbb, 0x2f, 0x5e, 0xfd, 0x03, 0x4f, 0xc2, 0x64, 0x3c, 0x8c, 0xf8, 0x01, 0xdc, 0xe9, 0x76,
0x27, 0x21, 0xc9, 0x03, 0x16, 0x29, 0xe6, 0x83, 0xaf, 0x6e, 0x7a, 0x4e, 0xce, 0x4d, 0x48, 0x25,
0xa9, 0x69, 0x9c, 0x39, 0x4c, 0xb3, 0x08, 0xb0, 0x06, 0x03, 0x11, 0x06, 0x1f, 0x18, 0x30, 0x99,
0x26, 0xfc, 0x80, 0xcc, 0x19, 0x55, 0xc8, 0x6f, 0x77, 0x3d, 0xb7, 0x0a, 0x5f, 0x12, 0x6c, 0xbd,
0xec, 0xc5, 0x79, 0x24, 0xf4, 0xa6, 0xe5, 0xd3, 0x3f, 0xee, 0x74, 0x11, 0x9a, 0xc6, 0xd9, 0xd0,
0x38, 0xdb, 0x7a, 0xf9, 0xfd, 0xd1, 0x7e, 0x8b, 0xb5, 0x33, 0x25, 0x8f, 0xa8, 0x40, 0x5a, 0x8e,
0x8c, 0x96, 0xc2, 0xef, 0xbb, 0xb5, 0xac, 0x0d, 0xf7, 0xa2, 0xc8, 0xe4, 0x5f, 0x3b, 0x08, 0x87,
0xe1, 0x77, 0xe7, 0x55, 0xa8, 0x3f, 0x57, 0x76, 0xc0, 0xdc, 0x41, 0xa8, 0x46, 0x7c, 0x4f, 0x41,
0x10, 0xca, 0x34, 0xa2, 0x47, 0x4e, 0x76, 0x97, 0x33, 0x76, 0x97, 0x35, 0xbb, 0xcb, 0x19, 0xbb,
0x5a, 0xc1, 0xe8, 0xb4, 0xc5, 0xe8, 0x4c, 0x19, 0xb5, 0x8e, 0x18, 0x9d, 0xb3, 0x18, 0xd5, 0x0c,
0xa3, 0xc2, 0x68, 0xaa, 0x7b, 0xe7, 0xa6, 0x0b, 0x1e, 0x86, 0x2c, 0xe6, 0xc3, 0x21, 0x39, 0x9d,
0x64, 0x02, 0xa1, 0x4b, 0xa7, 0x48, 0x27, 0x84, 0x84, 0xf4, 0xfa, 0xde, 0x27, 0x66, 0x40, 0xd5,
0x2e, 0x56, 0xb3, 0x50, 0xef, 0xf7, 0x5e, 0xef, 0x7a, 0xb5, 0x75, 0xab, 0xf9, 0x7c, 0x05, 0xe4,
0xe7, 0x55, 0x48, 0xc3, 0xdb, 0x55, 0x29, 0x40, 0xcf, 0x54, 0x82, 0xe2, 0x1a, 0xfd, 0xf6, 0xa6,
0x77, 0xa7, 0xeb, 0x2c, 0x82, 0xd1, 0x9e, 0xb4, 0xb2, 0xb5, 0x3b, 0x51, 0xb2, 0x5e, 0xf2, 0x02,
0x54, 0xd8, 0x13, 0xc8, 0x96, 0xd5, 0x36, 0xaa, 0x07, 0x9f, 0x37, 0x53, 0x7f, 0xe7, 0x07, 0x4c,
0xc0, 0x3e, 0x1a, 0x84, 0x5b, 0xd5, 0x37, 0x2c, 0xc9, 0xc0, 0xa7, 0x22, 0xf8, 0xbc, 0xf0, 0x45,
0xab, 0xaa, 0xfc, 0x91, 0xb0, 0xdf, 0x03, 0xc4, 0xac, 0x44, 0x1a, 0x3d, 0x43, 0xec, 0x9d, 0x25,
0xaf, 0xca, 0xc9, 0x17, 0x13, 0xd2, 0xd1, 0x2d, 0x95, 0x5d, 0x1f, 0xaf, 0xb5, 0xb2, 0x68, 0x4b,
0xb7, 0xd8, 0xc9, 0xf5, 0xa5, 0x84, 0xac, 0x20, 0xb1, 0x57, 0x8a, 0xa4, 0x77, 0xac, 0xc6, 0xeb,
0x98, 0x52, 0xef, 0x96, 0x79, 0xac, 0xa2, 0xda, 0xd6, 0x2d, 0x8e, 0x2b, 0xb9, 0xc1, 0x56, 0x4a,
0x13, 0xa1, 0x68, 0xc7, 0x7d, 0xf7, 0xa4, 0xaa, 0xeb, 0xa9, 0xbf, 0xa3, 0xed, 0xb6, 0xa3, 0x79,
0x92, 0x19, 0xc4, 0x6e, 0xa6, 0x60, 0x37, 0x8b, 0x70, 0x67, 0xca, 0xf1, 0x7f, 0xef, 0x57, 0xc4,
0xd5, 0xaa, 0xe0, 0xe8, 0x0d, 0x43, 0xd5, 0x0d, 0xb2, 0x62, 0x49, 0xb0, 0x63, 0x24, 0x58, 0x2d,
0xa7, 0xd9, 0x02, 0x19, 0xc6, 0x25, 0x42, 0xeb, 0x96, 0x10, 0x33, 0x46, 0x08, 0x8b, 0x68, 0xe1,
0x84, 0xfd, 0xeb, 0xe0, 0xe9, 0x1f, 0x1f, 0xf3, 0x46, 0x35, 0x9c, 0xe9, 0x57, 0x15, 0xbc, 0x95,
0xca, 0x7c, 0x69, 0x26, 0x7d, 0x71, 0x81, 0x2a, 0x5f, 0xc3, 0x65, 0x6f, 0x6b, 0x62, 0x27, 0x17,
0x73, 0xec, 0xf0, 0x38, 0xb0, 0x3c, 0x8e, 0x3c, 0x0e, 0x15, 0xa8, 0xa1, 0xaa, 0x82, 0x9a, 0xfb,
0xc4, 0x7e, 0x0b, 0xe3, 0xb7, 0x10, 0x7e, 0x0b, 0xe3, 0xcf, 0xb8, 0x4b, 0x94, 0xef, 0x76, 0x12,
0xd2, 0xc8, 0x33, 0x4a, 0x4c, 0xb5, 0xcc, 0x96, 0xfe, 0xb8, 0x48, 0xf4, 0x4c, 0x62, 0x6a, 0x98,
0xef, 0x34, 0x4d, 0x46, 0x46, 0x2c, 0x9c, 0xd0, 0xa4, 0x8f, 0x9b, 0x56, 0x5d, 0xb6, 0xa6, 0xb6,
0xd7, 0x66, 0x55, 0x92, 0x09, 0xb9, 0x96, 0x4a, 0x72, 0x09, 0x17, 0x07, 0xdb, 0x27, 0xa5, 0x42,
0x85, 0x4a, 0xdf, 0x96, 0xb1, 0x08, 0xb4, 0x43, 0xc5, 0x6d, 0xa7, 0x27, 0x5b, 0xda, 0x93, 0xf5,
0xcc, 0x93, 0xf5, 0x2c, 0xcb, 0xcc, 0x93, 0xf3, 0x28, 0xda, 0x7c, 0x41, 0xde, 0x36, 0xd5, 0x02,
0x32, 0xdc, 0x96, 0xb5, 0x37, 0x9f, 0x1b, 0x6e, 0xbe, 0x6c, 0xb8, 0x05, 0x77, 0x52, 0xa4, 0x9d,
0x31, 0x68, 0x6b, 0x06, 0xed, 0x8c, 0xc1, 0x32, 0x62, 0xb0, 0x9c, 0x72, 0xa1, 0x04, 0x45, 0x3a,
0x5a, 0x60, 0xbb, 0x53, 0x55, 0x5c, 0x2e, 0xc8, 0x6f, 0xdb, 0xa3, 0x01, 0x91, 0xaf, 0xd9, 0x77,
0xe5, 0xe4, 0x97, 0xcb, 0xe4, 0x67, 0xa7, 0x75, 0xe5, 0x6a, 0xc4, 0x3e, 0x50, 0xff, 0x08, 0x26,
0xba, 0x2b, 0x26, 0x95, 0xdd, 0xa5, 0x5f, 0xd1, 0x9f, 0xb3, 0x95, 0x1e, 0x68, 0xa0, 0xfa, 0xeb,
0x83, 0xcf, 0xc9, 0x12, 0x59, 0x72, 0xa1, 0xc8, 0x7f, 0xc3, 0xe2, 0x8c, 0x8f, 0x3d, 0x0b, 0x39,
0xfa, 0x82, 0x7b, 0x4c, 0xbe, 0x0f, 0x05, 0x8b, 0x98, 0x94, 0xf0, 0x82, 0xa6, 0x4c, 0x38, 0xeb,
0x77, 0x41, 0xdb, 0xe8, 0xac, 0x2e, 0xdf, 0x06, 0x2a, 0xdc, 0x46, 0x81, 0xde, 0xb0, 0xd0, 0x9b,
0x96, 0xd9, 0x11, 0x99, 0xb6, 0xfd, 0x9c, 0x31, 0x27, 0x17, 0x37, 0xee, 0x25, 0x8d, 0x3b, 0xfd,
0xe8, 0x42, 0x5a, 0x16, 0x6e, 0xbb, 0x1c, 0x1b, 0x1f, 0x5e, 0x4e, 0x59, 0xb8, 0xbf, 0xbf, 0x7e,
0xd2, 0xaf, 0x3e, 0x14, 0x98, 0x79, 0x56, 0xf1, 0xee, 0x5a, 0x33, 0xd3, 0xfa, 0x2b, 0x5e, 0x61,
0x49, 0x38, 0xe6, 0x63, 0x0a, 0x23, 0x9a, 0x04, 0x23, 0x16, 0x05, 0x90, 0x88, 0x21, 0x30, 0x4f,
0x9f, 0x3c, 0x83, 0x41, 0x0c, 0x53, 0xde, 0x22, 0x49, 0x2f, 0xeb, 0x1f, 0xc1, 0x15, 0x55, 0x2c,
0x70, 0xfe, 0xc5, 0x9d, 0xac, 0xc7, 0x27, 0x65, 0x41, 0x54, 0x56, 0x91, 0x80, 0xbf, 0x26, 0x34,
0xe1, 0x19, 0xa8, 0xe7, 0x69, 0x40, 0x57, 0xa8, 0x33, 0x3a, 0xd4, 0x3a, 0xba, 0x69, 0x7d, 0xe2,
0x35, 0x14, 0x76, 0x51, 0x53, 0xfe, 0xa6, 0x08, 0xff, 0x03, 0xbd, 0x96, 0x05, 0x93, 0xe3, 0x38,
0x10, 0x85, 0xb5, 0x98, 0xcc, 0x62, 0x38, 0xcb, 0x70, 0xcc, 0x67, 0x58, 0x2c, 0x3c, 0x5a, 0xe6,
0xc1, 0xe2, 0x2e, 0xc5, 0xe9, 0x49, 0x54, 0xb1, 0xa5, 0xac, 0xa4, 0x0c, 0xfd, 0xfa, 0x1b, 0x85,
0x2c, 0xab, 0x14, 0x1f, 0x5f, 0x71, 0x62, 0xab, 0xdf, 0xd7, 0xfd, 0xba, 0xe5, 0x26, 0x6c, 0x24,
0x46, 0x14, 0x38, 0xe5, 0x02, 0xb8, 0xdc, 0x85, 0x38, 0xee, 0xf7, 0x32, 0x48, 0x44, 0x36, 0xa6,
0x9c, 0x09, 0x0e, 0x92, 0xf6, 0x99, 0x80, 0x5e, 0x8a, 0xe4, 0xf3, 0x89, 0xea, 0x91, 0xf6, 0x88,
0x29, 0x05, 0x99, 0xe8, 0x63, 0x46, 0xda, 0x12, 0xc7, 0x48, 0x35, 0x4a, 0xd2, 0x11, 0x22, 0x03,
0x85, 0x72, 0x0f, 0x25, 0x39, 0xab, 0x34, 0x4b, 0x46, 0xd3, 0x58, 0x51, 0xb4, 0x2a, 0xd6, 0x17,
0x26, 0x56, 0xc7, 0x8a, 0xd5, 0x59, 0xc6, 0xea, 0x5a, 0xb1, 0x6a, 0x71, 0xf0, 0x0f, 0x23, 0xd4,
0xe3, 0x7f, 0x18, 0x80, 0xbc, 0x42, 0x1c, 0x6b, 0x44, 0xfe, 0x7a, 0x33, 0x84, 0xe5, 0x1b, 0x2d,
0x48, 0x85, 0xa4, 0x20, 0x34, 0x05, 0x2e, 0x00, 0x0f, 0x34, 0x4a, 0x4e, 0x53, 0x50, 0xc8, 0x95,
0x90, 0x8a, 0x90, 0xd7, 0x2c, 0x3d, 0x7c, 0x26, 0x60, 0xf3, 0xfb, 0x27, 0xc9, 0x50, 0x7c, 0xff,
0x9a, 0x69, 0xf4, 0x32, 0x7c, 0x6a, 0x18, 0x9a, 0x16, 0x43, 0x93, 0x0b, 0x0e, 0x6a, 0x88, 0x69,
0xea, 0x7d, 0xff, 0xfb, 0xe9, 0xfb, 0x39, 0x85, 0xc5, 0x79, 0x11, 0x7e, 0xa5, 0xb2, 0xff, 0x27,
0x55, 0xbc, 0x11, 0x2e, 0xf9, 0xce, 0xde, 0x32, 0x67, 0x2f, 0x59, 0x67, 0x2f, 0x2d, 0xcf, 0x5e,
0xb6, 0xce, 0x56, 0xe3, 0x8d, 0x18, 0xd4, 0x41, 0x18, 0x3d, 0x88, 0xfc, 0xc5, 0x86, 0xb1, 0x2a,
0xe3, 0xb8, 0x0e, 0x3d, 0xc9, 0xfa, 0x03, 0x04, 0x54, 0x63, 0x2e, 0xf6, 0xc9, 0x0d, 0xd1, 0x9b,
0x45, 0x86, 0xec, 0xa3, 0xd6, 0x05, 0xcc, 0x5b, 0x50, 0x7c, 0xd6, 0xd4, 0x28, 0x33, 0x66, 0xaa,
0x9f, 0x0c, 0xa9, 0x26, 0xb5, 0x87, 0x0f, 0xbc, 0x04, 0xff, 0x8b, 0x78, 0x15, 0x5e, 0x64, 0xe3,
0x49, 0xaa, 0x10, 0xde, 0xa7, 0x13, 0x95, 0xb7, 0x4b, 0xf5, 0x17, 0xa4, 0x19, 0x84, 0x3b, 0x7e,
0x32, 0x53, 0x9b, 0x3b, 0xbb, 0x93, 0x34, 0x25, 0x77, 0x0c, 0xe4, 0x9d, 0x7d, 0xb6, 0xcb, 0x48,
0xcd, 0x42, 0xad, 0x2d, 0x03, 0x5d, 0x71, 0x50, 0xaf, 0x16, 0x71, 0xea, 0x16, 0xea, 0x75, 0x07,
0xb5, 0xfa, 0x1b, 0x26, 0x23, 0x2f, 0xc0, 0x4d, 0xa3, 0x7a, 0xd1, 0xd2, 0xbb, 0x98, 0x83, 0x3f,
0xd9, 0x7c, 0x0e, 0x4f, 0x52, 0xa6, 0xb1, 0xcc, 0xdd, 0xaf, 0x4d, 0x80, 0xab, 0x56, 0x80, 0xab,
0xcb, 0x00, 0x77, 0x1d, 0x8a, 0x6b, 0x16, 0xe1, 0x67, 0x2e, 0xe1, 0x74, 0xa6, 0x7e, 0xf5, 0xfa,
0x07, 0xfe, 0x82, 0x80, 0x9d, 0x32, 0x90, 0xea, 0x6b, 0x13, 0x61, 0x3b, 0x82, 0x10, 0x42, 0x78,
0xe0, 0x67, 0x35, 0xd5, 0x9e, 0xce, 0xfe, 0x17, 0xa6, 0xce, 0xfe, 0x1b, 0xc0, 0xf5, 0x19, 0x0a,
0x57, 0xc2, 0x5d, 0xf7, 0x61, 0xa7, 0xd8, 0x05, 0xe4, 0xcd, 0xfd, 0x0d, 0x4d, 0x93, 0x11, 0x6c,
0x71, 0xa6, 0x8f, 0xf3, 0x79, 0xed, 0x05, 0xb9, 0x6b, 0x18, 0xea, 0x96, 0x7a, 0x7d, 0xa9, 0xde,
0xb0, 0xc4, 0x4e, 0xa0, 0x1a, 0xc2, 0x58, 0x24, 0x23, 0xd4, 0x7f, 0xfa, 0xfa, 0x39, 0x4d, 0xfb,
0x7b, 0xc8, 0xf5, 0x44, 0xa2, 0x2c, 0xb1, 0xed, 0x4f, 0xde, 0x80, 0x6e, 0x76, 0xad, 0x47, 0xff,
0x62, 0xb0, 0xd3, 0x2c, 0x63, 0x7e, 0xbb, 0xab, 0xb9, 0xc5, 0x24, 0x43, 0x35, 0xe4, 0x22, 0x15,
0x83, 0x43, 0xd8, 0x0f, 0xfd, 0xb9, 0xdc, 0x9e, 0xce, 0xcf, 0x6d, 0x03, 0x71, 0x7b, 0xea, 0xeb,
0x65, 0x0b, 0xe5, 0x72, 0x8e, 0xe2, 0xcc, 0xcf, 0x95, 0x62, 0x07, 0xd6, 0x2c, 0xd2, 0x6b, 0x4e,
0x77, 0x5e, 0x9e, 0x55, 0x97, 0x5c, 0x76, 0xcd, 0x66, 0x1c, 0xf7, 0x15, 0x66, 0x0c, 0x32, 0x8c,
0xee, 0xa7, 0x2a, 0x08, 0x21, 0xbf, 0x59, 0xca, 0x7d, 0x3a, 0xf1, 0xea, 0x09, 0xa8, 0xd8, 0x9b,
0x4f, 0xd7, 0x64, 0xd2, 0x9d, 0x66, 0x52, 0xc9, 0x6f, 0x5c, 0x87, 0xbe, 0x2b, 0x55, 0x14, 0x47,
0xa4, 0x6a, 0x51, 0x57, 0xe6, 0x94, 0x15, 0x87, 0x32, 0x13, 0x52, 0x21, 0xf4, 0x90, 0x27, 0x43,
0x90, 0x74, 0x74, 0xef, 0x41, 0x1c, 0xe6, 0xba, 0xe4, 0x82, 0x0e, 0x1e, 0x3f, 0xb0, 0x7e, 0x9f,
0x1d, 0x4b, 0xd1, 0x9f, 0x24, 0x7a, 0xc6, 0xe4, 0xbe, 0x4d, 0xde, 0x9a, 0x65, 0xe1, 0x59, 0x04,
0x5b, 0xa9, 0x96, 0x34, 0xcf, 0x96, 0x70, 0xd6, 0x33, 0xde, 0xa9, 0x44, 0x22, 0x72, 0x48, 0x04,
0xe7, 0x98, 0x68, 0xbf, 0xbd, 0x5f, 0x18, 0xbf, 0x60, 0xda, 0x35, 0x30, 0x9b, 0x44, 0xb0, 0xbb,
0x07, 0xf2, 0xf6, 0x01, 0x72, 0xbb, 0x90, 0x75, 0x61, 0x16, 0xdd, 0xd9, 0x03, 0x52, 0x39, 0x42,
0x29, 0xca, 0xb5, 0x3f, 0x9b, 0x69, 0x7f, 0x36, 0xd5, 0xfe, 0x6c, 0xa6, 0xdd, 0xca, 0xe5, 0x6e,
0x3a, 0x72, 0x6d, 0x5b, 0xae, 0xe5, 0xc8, 0x91, 0xf7, 0x52, 0xbc, 0x61, 0x89, 0x14, 0x5e, 0x17,
0xaf, 0x1a, 0x17, 0xd7, 0xac, 0x4e, 0x5c, 0x5b, 0x16, 0xab, 0xbe, 0xf0, 0x72, 0xea, 0xa1, 0x89,
0xcf, 0x68, 0x1a, 0x92, 0x46, 0x69, 0xc3, 0x9c, 0xb3, 0x9c, 0x5e, 0x9b, 0x3b, 0xbd, 0xe6, 0x38,
0xbd, 0x1e, 0xbe, 0x45, 0x3f, 0x4d, 0xc7, 0xd0, 0x9c, 0xb5, 0x68, 0xce, 0x2e, 0x69, 0x2e, 0x95,
0x37, 0x6a, 0xa5, 0xb0, 0xdf, 0xcd, 0x74, 0xcf, 0x16, 0x75, 0x4f, 0xfc, 0xfc, 0x0a, 0xe2, 0x70,
0xc5, 0x74, 0x5e, 0x35, 0x15, 0x9f, 0x55, 0xe3, 0xaa, 0xa9, 0x77, 0x79, 0x4d, 0xf2, 0xaf, 0xdb,
0xac, 0xbf, 0x1b, 0xc5, 0x41, 0xb4, 0x8b, 0xd0, 0x72, 0x86, 0x74, 0x45, 0x51, 0xce, 0xdc, 0xb3,
0xa6, 0xb2, 0x3c, 0xd5, 0x86, 0xa4, 0xa3, 0x30, 0x8e, 0x83, 0x08, 0xf6, 0xa3, 0xfb, 0x1f, 0xc3,
0x07, 0xcb, 0xb7, 0x49, 0x63, 0xc2, 0x8f, 0xd1, 0xb1, 0x9f, 0xff, 0x73, 0xda, 0x0c, 0x86, 0x3f,
0x61, 0xd4, 0x43, 0x94, 0x1c, 0xb5, 0x95, 0xba, 0x3f, 0x69, 0x6f, 0x1b, 0x94, 0x23, 0x7e, 0x1e,
0x41, 0x79, 0x0a, 0x7f, 0x9c, 0xe0, 0xfd, 0x20, 0x08, 0x41, 0xa5, 0x42, 0x43, 0x92, 0xa7, 0x73,
0x35, 0x02, 0xd5, 0x07, 0x85, 0xd8, 0x47, 0x09, 0x82, 0xa7, 0x87, 0x9e, 0x94, 0x1b, 0x6e, 0x55,
0x2c, 0x37, 0x60, 0x91, 0x74, 0x39, 0x5e, 0xbb, 0xec, 0xe9, 0xc2, 0xc1, 0xf2, 0x10, 0x8e, 0xbd,
0xa7, 0x77, 0x98, 0x7a, 0x63, 0xbe, 0x93, 0x9b, 0x74, 0xe0, 0xb5, 0xc3, 0xec, 0x55, 0x2b, 0x37,
0xaa, 0x52, 0x25, 0x7b, 0xa3, 0xaa, 0xcd, 0x9b, 0x8b, 0x6c, 0x20, 0x57, 0xf8, 0x2b, 0x1d, 0xc3,
0x86, 0x48, 0xa9, 0xb4, 0xd6, 0xbc, 0x17, 0xbc, 0xcf, 0x12, 0xaa, 0x85, 0xfc, 0xfe, 0xc9, 0xc6,
0xfb, 0xb7, 0x62, 0x7f, 0x0a, 0x33, 0xe7, 0x30, 0x08, 0x8d, 0xd7, 0x62, 0x9d, 0x7e, 0xff, 0x36,
0x7c, 0xb0, 0x1e, 0x39, 0x8f, 0x60, 0xb6, 0x8c, 0xb8, 0x07, 0x3e, 0x9f, 0xfd, 0x4f, 0x94, 0x11,
0x3a, 0x60, 0x54, 0x40, 0x1c, 0x6c, 0xc0, 0x5f, 0x5c, 0xd4, 0x3b, 0x85, 0x1e, 0x2b, 0xec, 0xed,
0x57, 0x73, 0x23, 0xcf, 0xc7, 0xff, 0x51, 0x60, 0xb2, 0xa1, 0xa9, 0x36, 0x31, 0x9f, 0x99, 0xbe,
0x1d, 0x0c, 0x18, 0x1f, 0xc0, 0xca, 0xdd, 0xfb, 0xfc, 0xf2, 0x49, 0xd3, 0x79, 0xb2, 0x16, 0xc3,
0x71, 0x51, 0xe3, 0xc8, 0xbf, 0x4c, 0x99, 0x29, 0x9b, 0xad, 0x54, 0x77, 0x4d, 0xc5, 0x8a, 0x8b,
0x95, 0xab, 0x4a, 0xea, 0x32, 0xca, 0x7f, 0xdc, 0x70, 0xee, 0x90, 0xb6, 0x95, 0xc6, 0x6d, 0xe7,
0x59, 0x6b, 0xde, 0x02, 0x8d, 0xc5, 0xe1, 0xab, 0xce, 0x0b, 0xf6, 0xea, 0xe6, 0x06, 0xae, 0x2f,
0xfa, 0x47, 0x87, 0x41, 0x10, 0xe0, 0xea, 0xef, 0x7e, 0x25, 0x87, 0xb7, 0xbf, 0xfe, 0xe5, 0x9d,
0xea, 0xdb, 0x02, 0x7e, 0x07, 0xb5, 0x54, 0x55, 0xb7, 0xdb, 0x30, 0x0c, 0xd6, 0x18, 0x0a, 0x63,
0x66, 0x66, 0x48, 0x7a, 0xf1, 0x71, 0xcc, 0xcc, 0x4f, 0x3a, 0x4e, 0xab, 0xae, 0x3e, 0x4d, 0xec,
0xcc, 0x4e, 0xe1, 0xe7, 0x2f, 0x2e, 0x78, 0x8e, 0xe7, 0xf1, 0xee, 0xab, 0x2d, 0x7d, 0x20, 0x82,
0xb7, 0x78, 0x9b, 0x58, 0x86, 0x6f, 0x5a, 0xf8, 0x66, 0x90, 0x2b, 0xca, 0x08, 0xdf, 0x7c, 0x8c,
0xe2, 0xa5, 0x18, 0xdb, 0x32, 0xcb, 0x99, 0xe0, 0x52, 0xa0, 0x62, 0x1d, 0x2e, 0x31, 0x49, 0x09,
0xae, 0x62, 0xae, 0xe1, 0xd2, 0x88, 0x77, 0x39, 0x1c, 0xea, 0x73, 0xad, 0x31, 0x93, 0x1d, 0xca,
0xe0, 0x90, 0xa2, 0x9c, 0x58, 0x41, 0x0a, 0x4e, 0x61, 0xa2, 0x78, 0xe7, 0x33, 0x21, 0xe9, 0x5c,
0xc8, 0x11, 0x9c, 0x96, 0x89, 0x26, 0x35, 0x24, 0x85, 0xd9, 0x97, 0xa2, 0x80, 0xc3, 0x52, 0x66,
0x38, 0x7d, 0x80, 0xb3, 0x58, 0xfd, 0x5b, 0x97, 0x18, 0x1d, 0x3f, 0x21, 0x3f, 0x69, 0xc8, 0x1b,
0x0e, 0x6d, 0xc3, 0xd2, 0xee, 0xf3, 0xb0, 0x9a, 0x0e, 0xcf, 0x41, 0xef, 0x6f, 0xfb, 0xf2, 0xd2,
0x5a, 0xc2, 0xaf, 0x23, 0xa1, 0x0b, 0xa6, 0xf0, 0x4e, 0x0b, 0xf5, 0x78, 0xe2, 0xc5, 0x81, 0xdc,
0x6d, 0x21, 0x0f, 0x7b, 0x85, 0xda, 0xb9, 0xf2, 0x7b, 0x71, 0xeb, 0x7a, 0x5c, 0xf4, 0x9f, 0x97,
0x41, 0xf8, 0x2c, 0x0a, 0xda, 0x38, 0x36, 0xd0, 0x49, 0xd8, 0x46, 0x45, 0x76, 0x2d, 0x0e, 0x67,
0x77, 0x07, 0x69, 0x0a, 0xc7, 0xca, 0x20, 0xce, 0xd2, 0x29, 0xd6, 0x31, 0xd3, 0xee, 0x2a, 0xe2,
0x6b, 0x5b, 0x18, 0x4f, 0x5d, 0x43, 0x39, 0x14, 0x8d, 0x32, 0x22, 0xe3, 0x82, 0xa5, 0xd8, 0xee,
0xb1, 0x02, 0x6a, 0xad, 0x3f, 0xd5, 0xbb, 0x0f, 0x53, 0xa9, 0x18, 0xca, 0x82, 0xa1, 0x90, 0x48,
0xe3, 0xf2, 0xcd, 0xa0, 0x69, 0x12, 0x5a, 0x2a, 0x5d, 0xb5, 0xb3, 0xf8, 0xe3, 0x9e, 0xae, 0xa5,
0xa7, 0xa5, 0x7f, 0xea, 0xc1, 0x72, 0x38, 0x9b, 0x8a, 0x1e, 0x29, 0x41, 0xc5, 0x5c, 0xb9, 0xab,
0xf8, 0x5f, 0xc7, 0xb3, 0xb6, 0x1a, 0xe2, 0xc4, 0x09, 0x09, 0x4e, 0x59, 0x30, 0x4c, 0x83, 0x2e,
0x2e, 0x02, 0x7c, 0x60, 0x43, 0xd2, 0x3d, 0xa6, 0xc8, 0xdc, 0x8e, 0xa7, 0xaf, 0xa3, 0x28, 0x0e,
0x0f, 0xe4, 0x59, 0x23, 0x7f, 0xaf, 0x83, 0xb8, 0x37, 0x67, 0xaa, 0xe0, 0x85, 0x89, 0xa2, 0x71,
0x19, 0xde, 0x21, 0x85, 0x73, 0x12, 0x4f, 0xed, 0x45, 0xcb, 0xe8, 0xff, 0xec, 0xb5, 0xee, 0x8f,
0x7a, 0x3f, 0xfb, 0x1c, 0xf7, 0x27, 0xfc, 0xe5, 0x7c, 0xf8, 0x29, 0xbe, 0x19, 0x2f, 0x62, 0xc2,
0x4c, 0x83, 0x83, 0x85, 0x68, 0x7e, 0xf3, 0xbb, 0xae, 0x7c, 0x7b, 0xab, 0x58, 0xbb, 0x4f, 0x0a,
0x9f, 0x86, 0xf7, 0xe5, 0x84, 0x31, 0xb7, 0xc3, 0x6a, 0xd9, 0xe9, 0x70, 0xc3, 0xa8, 0x8c, 0x19,
0x65, 0x51, 0x14, 0xdd, 0x44, 0x1b, 0x00, 0x1f, 0xb8, 0x9c, 0x41, 0x3e, 0x8d, 0xd6, 0x70, 0x1f,
0xea, 0xf7, 0xb8, 0xe8, 0x07, 0xf1, 0x77, 0x39, 0xd0, 0xf0, 0x91, 0x33, 0x69, 0x2e, 0x55, 0x28,
0xf0, 0x88, 0xd9, 0x9d, 0x83, 0x46, 0xcd, 0xc1, 0xc9, 0xec, 0x6d, 0x75, 0x12, 0xb7, 0x5a, 0x4d,
0xdb, 0x1c, 0xd2, 0xcd, 0xc1, 0x6d, 0xc0, 0x70, 0x26, 0xc2, 0x26, 0xa1, 0xba, 0x8b, 0x71, 0x98,
0xdb, 0xd0, 0xd6, 0x9c, 0xb4, 0x9a, 0x25, 0xdc, 0x33, 0x5f, 0x4f, 0xa5, 0xe3, 0x56, 0x0c, 0x75,
0x87, 0x7f, 0xd3, 0x9b, 0x5f, 0x39, 0x09, 0x7b, 0xd8, 0xe9, 0x6d, 0xfc, 0xae, 0xea, 0xd0, 0xb8,
0x16, 0xf7, 0x78, 0x03, 0xb5, 0x75, 0x5a, 0x7c, 0xd8, 0xfa, 0xe1, 0xd1, 0x8b, 0xf0, 0x80, 0x18,
0xee, 0xa3, 0x93, 0x3d, 0x3e, 0x3a, 0xbb, 0x40, 0x47, 0x8d, 0x92, 0xa3, 0x93, 0x9a, 0xd6, 0x1d,
0x3d, 0xf5, 0xef, 0xd7, 0x02, 0xea, 0x56, 0xe3, 0x5e, 0x4f, 0xe3, 0xbe, 0xaa, 0x8e, 0x86, 0xa3,
0xf1, 0x80, 0xa7, 0xb1, 0x3e, 0xd3, 0x58, 0xaf, 0xde, 0xae, 0xaf,
};
#else
enum { DECODE_CHUNK_BYTES = 512 };
static const uint16_t OFFSETS[] = {
0, 227, 448, 665, 896, 1116, 1323, 1537, 1779, 1926, 2134, 2376,
2609, 2878, 3114, 3328, 3569, 3806, 3999, 4200, 4442, 4662,
};
static const uint16_t RAW_LENGTHS[] = {
510, 512, 505, 509, 512, 500, 502, 511, 489, 512, 510, 504,
505, 501, 486, 504, 499, 504, 504, 509, 460,
};
static const uint16_t FIRSTS[] = {
0, 27, 64, 95, 121, 159, 193, 227, 262, 295, 329, 364,
396, 426, 461, 481, 510, 544, 578, 618, 647, 683,
};
// 4662 encoded bytes; 21 independent chunks, at most 512 decoded bytes.
static const uint8_t NAMES[] = {
0x8c, 0x51, 0x03, 0x6f, 0x40, 0x41, 0x0c, 0xee, 0x82, 0xd9, 0x76, 0x30, 0xdb, 0x88, 0x67, 0x9b,
0x71, 0x73, 0x68, 0xf2, 0xdc, 0x77, 0x98, 0x7e, 0xfd, 0x6c, 0x47, 0x77, 0xf5, 0x07, 0x58, 0x95,
@@ -186,228 +378,257 @@ inline const char* ota_target_env_name(uint32_t target_id, char* buffer, size_t
0x0a, 0xa6, 0x65, 0xce, 0xf7, 0xe0, 0x37, 0xad, 0xd7, 0x4c, 0x44, 0x35, 0xa0, 0x57, 0xec, 0x62,
0xd6, 0x4b, 0x7a, 0xb3, 0x23, 0xa8, 0x6f, 0xb2, 0xf2, 0x36, 0xd4, 0x44, 0xd7, 0x49, 0xc8, 0x75,
0xe1, 0x2e, 0x85, 0x33, 0xb7, 0x10, 0xc7, 0xf5, 0xba, 0x9a, 0xd7, 0x9c, 0xed, 0x8c, 0xfe, 0x01,
0x7d, 0x51, 0x35, 0x76, 0x03, 0x31, 0x10, 0xfd, 0x5d, 0xa8, 0x0a, 0x33, 0xf4, 0xa9, 0x0c, 0x87,
0xc8, 0x2d, 0x66, 0x69, 0x14, 0xeb, 0x59, 0x2c, 0x79, 0x7d, 0x7c, 0x33, 0x7b, 0x59, 0xac, 0x3f,
0x1f, 0x30, 0x62, 0x95, 0xb8, 0xa4, 0x14, 0xf2, 0x72, 0xcc, 0x81, 0xea, 0x3e, 0x95, 0x56, 0xbb,
0xdc, 0x48, 0x6b, 0x28, 0xe4, 0x95, 0xb4, 0x34, 0x95, 0x42, 0x92, 0x60, 0x6d, 0x85, 0xc0, 0xaf,
0xc1, 0xdd, 0x58, 0xc6, 0x48, 0xda, 0x56, 0xac, 0x71, 0x17, 0xd8, 0x71, 0x9e, 0x38, 0xe0, 0x8d,
0x8a, 0x20, 0xab, 0x7f, 0x26, 0x8e, 0xce, 0xd8, 0x29, 0xde, 0x6d, 0x11, 0x39, 0xd4, 0x1c, 0x48,
0xfb, 0x94, 0x70, 0x6f, 0xad, 0xa6, 0xf5, 0x02, 0x3e, 0xe9, 0x78, 0xef, 0x2e, 0xb2, 0x89, 0x36,
0xe0, 0x2e, 0x71, 0xd0, 0xd2, 0xe4, 0x8a, 0xca, 0x51, 0x9e, 0x80, 0xbf, 0x35, 0xb3, 0x06, 0x46,
0x85, 0xe2, 0x05, 0x8a, 0x8b, 0x78, 0x9b, 0xc4, 0x02, 0x6f, 0x4b, 0x82, 0xb8, 0x3e, 0xa0, 0x75,
0xbd, 0xa3, 0x75, 0x73, 0x50, 0xf7, 0xfa, 0x18, 0xff, 0x72, 0xd0, 0x81, 0x2b, 0x26, 0x4a, 0x1d,
0xa2, 0x7f, 0xad, 0xa9, 0x36, 0x17, 0xb9, 0x3b, 0xd1, 0xfe, 0x16, 0x62, 0x7f, 0xd0, 0xc7, 0xfd,
0xb1, 0xcc, 0x1f, 0x8a, 0x52, 0x1f, 0x11, 0x7a, 0x3c, 0x31, 0xe2, 0x7a, 0x6d, 0xc4, 0x29, 0xd1,
0x6d, 0x44, 0xf5, 0x90, 0xfa, 0xcb, 0xe6, 0x9c, 0xf7, 0x92, 0xed, 0x2a, 0x21, 0x83, 0xeb, 0x46,
0x55, 0xdb, 0xf8, 0x7e, 0x5c, 0xdc, 0x0e, 0xe7, 0x85, 0x4f, 0x83, 0x6e, 0x44, 0x51, 0x10, 0x3d,
0x71, 0x56, 0x61, 0x6d, 0x06, 0xb5, 0xdb, 0xbf, 0x99, 0xa7, 0xb9, 0xe9, 0xd3, 0xf5, 0xe2, 0xef,
0xd7, 0x66, 0x34, 0x3c, 0xc2, 0x3f, 0x17, 0x9e, 0x63, 0x6a, 0x7d, 0xd1, 0x27, 0xc5, 0xaa, 0xd4,
0xa1, 0x4c, 0x95, 0x24, 0x1b, 0x26, 0xa9, 0x22, 0xd1, 0x2c, 0x0a, 0x12, 0x5c, 0x2a, 0x21, 0x70,
0xd6, 0x74, 0xd1, 0xb4, 0x6f, 0xa7, 0x22, 0x9d, 0x0c, 0xd7, 0xa5, 0xf1, 0x7e, 0x32, 0xd4, 0xb9,
0xa3, 0x43, 0xe9, 0xfa, 0x14, 0x79, 0xea, 0xc9, 0x0b, 0xbf, 0xc2, 0x19, 0x15, 0x3c, 0x7d, 0xde,
0x71, 0xbf, 0x9a, 0xd7, 0xbf, 0x9a, 0xd3, 0xbf, 0x9a, 0xd7, 0xaf, 0xae, 0x61, 0xc0, 0xf9, 0xf0,
0x2a, 0x71, 0xa1, 0xdd, 0xb8, 0x3b, 0xe9, 0xe3, 0x97, 0xd7, 0x03, 0xf8, 0x7d, 0xff, 0x34, 0xa5,
0x93, 0xb8, 0x9c, 0xa6, 0x91, 0xa8, 0x2e, 0x84, 0xb1, 0xac, 0x39, 0xf4, 0x6c, 0x71, 0x49, 0x2a,
0x72, 0x6c, 0x5b, 0x6c, 0x69, 0xf0, 0x8c, 0x23, 0xa5, 0x4a, 0x1a, 0x2f, 0x16, 0x2f, 0x87, 0xdb,
0x02, 0x4a, 0xbc, 0x6a, 0x87, 0xc7, 0x69, 0x46, 0x89, 0x6a, 0x9e, 0x3a, 0x47, 0xa5, 0x4a, 0xb8,
0x44, 0x55, 0x87, 0xd6, 0xa7, 0x7e, 0x00, 0x1b, 0xba, 0x48, 0xd8, 0xa2, 0x6a, 0xff, 0xd6, 0xb1,
0xe1, 0x8a, 0xb4, 0xc3, 0xf9, 0x80, 0x08, 0xe7, 0xc3, 0x94, 0x83, 0x3c, 0x68, 0xcc, 0xb1, 0x35,
0xa6, 0xe6, 0xf7, 0x97, 0x2c, 0xf6, 0x00, 0x6c, 0x50, 0x45, 0x72, 0x05, 0x41, 0x08, 0x65, 0x1b,
0x77, 0xf7, 0x33, 0x44, 0x6f, 0x43, 0xb5, 0x30, 0x7f, 0x18, 0xa3, 0x03, 0xc4, 0x4e, 0x1f, 0xd7,
0xae, 0xac, 0x70, 0x9e, 0x40, 0x4b, 0x83, 0x53, 0xc2, 0xfb, 0x0b, 0xd4, 0x1b, 0x54, 0x91, 0x11,
0x8d, 0xf4, 0x9e, 0x14, 0x76, 0x51, 0xe2, 0x47, 0x8a, 0xe3, 0xad, 0x3b, 0x2c, 0x19, 0x4d, 0x26,
0x0a, 0x4b, 0x4e, 0x3a, 0xf2, 0x14, 0x06, 0x4c, 0x6d, 0x70, 0x58, 0x6e, 0x1c, 0x93, 0x8c, 0x25,
0x4c, 0x2c, 0x13, 0x6a, 0xc8, 0x2c, 0x18, 0x07, 0x82, 0xb3, 0xe6, 0x6e, 0x18, 0xe0, 0xec, 0xce,
0x22, 0x9c, 0x3d, 0x70, 0xc3, 0xb0, 0xd6, 0xb3, 0x19, 0x8e, 0x92, 0x69, 0x84, 0xb5, 0x22, 0xea,
0x1a, 0xd8, 0x51, 0xa9, 0x50, 0x78, 0xfd, 0x58, 0x81, 0x1d, 0xfc, 0x10, 0xa9, 0x26, 0x6b, 0x5f,
0x17, 0xb0, 0x57, 0x4d, 0xd6, 0x7f, 0x91, 0x3f, 0xac, 0xaf, 0x3e, 0xc9, 0xaf, 0xfd, 0x25, 0xbf,
0xf0, 0xf5, 0x6b, 0x1d, 0xa3, 0x72, 0x9e, 0x11, 0x92, 0x95, 0x49, 0x1e, 0x60, 0x63, 0xa0, 0x59,
0x48, 0x4f, 0x58, 0x82, 0x3a, 0xfb, 0x9b, 0x32, 0x27, 0x73, 0xd8, 0xf8, 0xfb, 0x75, 0xe1, 0x17,
0xe2, 0x56, 0x85, 0x38, 0xff, 0x89, 0x38, 0x5f, 0x21, 0xfe, 0x6f, 0x17, 0x36, 0x34, 0xca, 0x04,
0xc7, 0x6f, 0xa6, 0x7d, 0xe5, 0xaf, 0xd6, 0xbd, 0x0c, 0xc6, 0x04, 0x05, 0x20, 0x94, 0xcd, 0x07,
0xf7, 0xbf, 0x18, 0x9a, 0x9b, 0xf9, 0x91, 0x5c, 0xc3, 0x07, 0xb5, 0x9d, 0x0f, 0xd5, 0x76, 0x00,
0x74, 0xce, 0x83, 0x72, 0x6c, 0x41, 0x10, 0x06, 0xe0, 0x2e, 0x5e, 0xc4, 0xb6, 0x6d, 0xeb, 0x09,
0x62, 0xab, 0xdc, 0x35, 0xe8, 0xcd, 0xce, 0xd1, 0x9c, 0xf4, 0x4c, 0xb2, 0xaf, 0x9f, 0xac, 0x5d,
0x6e, 0x7e, 0x3f, 0x5c, 0x50, 0xe4, 0x49, 0xe1, 0xbb, 0x61, 0x8a, 0xc8, 0x39, 0x7c, 0x10, 0x29,
0x31, 0x2a, 0x1b, 0xa7, 0x22, 0x31, 0x36, 0x41, 0x16, 0xda, 0x58, 0x94, 0x11, 0xc1, 0x1a, 0xa6,
0x0e, 0x96, 0xbf, 0x9c, 0x84, 0xe1, 0xd0, 0xfc, 0x6e, 0xc6, 0x56, 0x53, 0x0c, 0xc3, 0x4c, 0x29,
0x09, 0x4f, 0x0c, 0xd3, 0x28, 0xd9, 0xe8, 0x0f, 0x42, 0x72, 0x69, 0x62, 0x73, 0x30, 0x63, 0xa5,
0x23, 0xfe, 0x26, 0xc6, 0xf8, 0xd3, 0x7b, 0x18, 0xb1, 0x36, 0xc6, 0x62, 0x03, 0xe6, 0xb0, 0x7e,
0xd6, 0xfb, 0xc2, 0x42, 0x85, 0x6d, 0xdc, 0x8d, 0xbc, 0xbb, 0x9a, 0x77, 0x47, 0x6b, 0xdc, 0xd1,
0x8a, 0x3b, 0xdb, 0xe0, 0xce, 0xd5, 0xff, 0x1e, 0xab, 0x71, 0x17, 0x1a, 0xdc, 0xbf, 0xcf, 0x37,
0x07, 0x2d, 0xcd, 0x99, 0xbc, 0x39, 0x99, 0x37, 0x27, 0x73, 0x26, 0x63, 0xa0, 0xb3, 0xa2, 0x0d,
0x36, 0x68, 0x93, 0xec, 0xf6, 0x0f, 0xf6, 0x61, 0xa8, 0xfe, 0x71, 0x57, 0x0d, 0x3a, 0xd2, 0x80,
0x76, 0x3a, 0x4a, 0x9c, 0x65, 0x00, 0x13, 0xda, 0x50, 0x60, 0x56, 0x24, 0x3a, 0x4b, 0x91, 0xc6,
0x84, 0x33, 0x48, 0xfb, 0xfb, 0x78, 0xb0, 0xab, 0x65, 0x8c, 0xbb, 0x67, 0xc7, 0x2d, 0x93, 0xed,
0xe5, 0x43, 0xcd, 0xb1, 0xf5, 0xc2, 0x93, 0x6e, 0xb9, 0x71, 0x96, 0xdf, 0xf8, 0x99, 0x2c, 0x22,
0x5a, 0x90, 0x9c, 0x22, 0x8c, 0x14, 0x80, 0x00, 0xa5, 0xd1, 0xb1, 0x4a, 0xc0, 0x40, 0x0c, 0x80,
0xe1, 0x4c, 0xae, 0x2a, 0xd8, 0xfa, 0x04, 0xba, 0x15, 0x6a, 0xfb, 0x0c, 0xe2, 0x60, 0xc7, 0xee,
0x21, 0x6d, 0x23, 0x1e, 0x77, 0x97, 0x94, 0xe4, 0x14, 0x7d, 0x7b, 0xeb, 0x52, 0x14, 0x04, 0x87,
0x5b, 0x43, 0xf8, 0xf8, 0x43, 0x20, 0x44, 0x8d, 0x84, 0x42, 0xa2, 0x28, 0xf6, 0x82, 0xc3, 0xb0,
0x2d, 0x19, 0x57, 0xcd, 0x3b, 0x49, 0x50, 0x41, 0xa3, 0x2d, 0x28, 0x2e, 0x89, 0xe1, 0xee, 0xcd,
0x17, 0x68, 0x62, 0x70, 0xc7, 0xac, 0x1b, 0x67, 0x68, 0x8c, 0x77, 0xa6, 0xc2, 0x06, 0xad, 0x6a,
0x46, 0x67, 0x7b, 0x67, 0x83, 0xcb, 0xb1, 0xaf, 0x14, 0xae, 0xc6, 0x4a, 0xe0, 0xc2, 0x4b, 0x58,
0xe3, 0x3f, 0xe7, 0xdc, 0x7f, 0x5b, 0xed, 0x0f, 0xab, 0x3d, 0xad, 0xdb, 0x5f, 0x31, 0x7d, 0xa5,
0x70, 0x3d, 0x56, 0x02, 0xf0, 0xcc, 0xbc, 0x17, 0x66, 0x99, 0xe6, 0x07, 0x3c, 0x37, 0x6e, 0x30,
0xa9, 0x11, 0x6a, 0x21, 0x3c, 0x5e, 0xc7, 0x1f, 0xc7, 0x4c, 0x28, 0xa1, 0xb3, 0xb8, 0x9a, 0x03,
0x4c, 0x21, 0x7d, 0x3e, 0x29, 0xce, 0xdd, 0xe3, 0xfa, 0xaa, 0xdd, 0x14, 0x0a, 0xff, 0xd9, 0xf0,
0x05, 0x7c, 0x50, 0xb5, 0x5a, 0x06, 0x41, 0x0c, 0x4c, 0x87, 0x3b, 0x15, 0xee, 0x15, 0xae, 0x3d,
0x6e, 0x1d, 0xf4, 0x39, 0xcb, 0xc7, 0xad, 0x66, 0x0d, 0x7b, 0x7a, 0x5c, 0x7f, 0xab, 0xc7, 0x07,
0xae, 0x85, 0x7e, 0x3e, 0x63, 0xbc, 0x5d, 0x3b, 0x29, 0x6b, 0x5e, 0xbb, 0x16, 0x89, 0xb0, 0x64,
0xe3, 0x72, 0x2b, 0xd8, 0x62, 0xc8, 0x2b, 0xc1, 0x78, 0x1f, 0x0b, 0x18, 0x53, 0x22, 0x46, 0x34,
0x5c, 0x91, 0x81, 0x31, 0xfb, 0x06, 0xc5, 0x9a, 0xb4, 0x6e, 0xa2, 0x16, 0x9a, 0x60, 0xed, 0x83,
0x1f, 0xc8, 0x51, 0x9e, 0x28, 0xc0, 0x38, 0xb3, 0xc1, 0x48, 0xe1, 0x81, 0x02, 0x0c, 0xe0, 0x51,
0x1e, 0xaa, 0x96, 0xaa, 0xc5, 0xc6, 0x94, 0xd6, 0x0e, 0x83, 0xad, 0xb4, 0x33, 0xef, 0xda, 0xc1,
0x3f, 0xda, 0xc1, 0x1f, 0xed, 0xd0, 0x1f, 0x6d, 0xf7, 0xce, 0xcd, 0x0e, 0xc6, 0xa7, 0xad, 0xed,
0xfd, 0xed, 0x96, 0x15, 0x56, 0xd0, 0xc5, 0x4e, 0x3d, 0x26, 0xb1, 0x08, 0xa2, 0xba, 0x23, 0xa4,
0xe8, 0x2c, 0x3f, 0xc2, 0x14, 0x17, 0x9f, 0xce, 0x68, 0x7c, 0x4a, 0xff, 0x6a, 0xce, 0xe0, 0x7f,
0x6c, 0xec, 0x4d, 0x6f, 0x84, 0xcd, 0xdf, 0x1e, 0xab, 0xf3, 0x04, 0xc3, 0x07, 0xaf, 0x33, 0xc3,
0xe6, 0x02, 0xfa, 0x58, 0x0e, 0x00, 0x6d, 0x90, 0xb5, 0x72, 0x04, 0x31, 0x10, 0x44, 0x27, 0x33,
0x33, 0x33, 0x53, 0x66, 0xce, 0xcd, 0xcc, 0x14, 0xf6, 0xd2, 0x9c, 0x4b, 0x75, 0xd2, 0xce, 0x5a,
0xda, 0x35, 0xfc, 0xbd, 0xeb, 0x18, 0x33, 0xe1, 0xeb, 0x37, 0x4d, 0xb7, 0x4a, 0xff, 0x5d, 0x08,
0x5e, 0x71, 0x65, 0x92, 0x4c, 0x3b, 0xc6, 0xa3, 0xce, 0x1c, 0x2c, 0x27, 0xec, 0xa7, 0x6c, 0xa9,
0xf3, 0x98, 0x7d, 0x83, 0x9d, 0x0f, 0x84, 0x62, 0x12, 0x3f, 0x56, 0x12, 0xc3, 0xfa, 0x91, 0x12,
0x04, 0x9a, 0x69, 0x05, 0x89, 0xa3, 0xc5, 0x5c, 0xa6, 0x35, 0x2d, 0x66, 0x2e, 0xa0, 0xc5, 0x1f,
0x95, 0x53, 0x34, 0x98, 0x57, 0xce, 0xc1, 0x48, 0xc4, 0x86, 0x06, 0xab, 0xa0, 0x49, 0x04, 0x56,
0x45, 0x9f, 0x0c, 0x76, 0x49, 0x2c, 0x3f, 0x34, 0x25, 0x81, 0x63, 0xfb, 0xcd, 0x16, 0xe6, 0x2b,
0x4d, 0x69, 0x48, 0xc4, 0xa0, 0x74, 0x40, 0x33, 0x68, 0xbc, 0xeb, 0x3c, 0xe5, 0x30, 0xdf, 0x56,
0x60, 0xae, 0x90, 0xda, 0x57, 0x97, 0xd7, 0x57, 0x13, 0x3f, 0x7b, 0xbd, 0xc4, 0x99, 0x56, 0x29,
0xc3, 0xfd, 0xee, 0xec, 0x1e, 0xee, 0xb6, 0x05, 0x6c, 0x15, 0x00, 0x53, 0x75, 0x80, 0xa9, 0x2a,
0x60, 0xa9, 0xc9, 0x62, 0xba, 0xce, 0x70, 0xb5, 0xd9, 0x50, 0x8b, 0xf5, 0x71, 0x72, 0xd8, 0x2e,
0x03, 0xed, 0x0b, 0x41, 0x6d, 0xe4, 0xc2, 0xa6, 0xf3, 0xb6, 0x40, 0x78, 0xdf, 0xc5, 0x0e, 0x76,
0x70, 0xd8, 0xde, 0xb5, 0xd0, 0xf6, 0x7a, 0x41, 0x78, 0xbd, 0xd8, 0xf3, 0x78, 0x1d, 0x76, 0xbc,
0xaa, 0x3d, 0xdf, 0xa8, 0x06, 0x9a, 0xa8, 0xf3, 0x5e, 0x6a, 0xbe, 0xfc, 0x07, 0xad, 0xcf, 0xb5,
0x5a, 0x2c, 0x41, 0x10, 0x05, 0xe0, 0xba, 0x17, 0x8d, 0x70, 0x77, 0x8f, 0x71, 0x88, 0x71, 0x4d,
0xb0, 0xf4, 0x7c, 0x23, 0xb5, 0x3b, 0x3d, 0xd2, 0x35, 0x2d, 0x6b, 0x4f, 0x8f, 0x3b, 0x84, 0xa4,
0xa5, 0xff, 0xa1, 0x0b, 0x95, 0xb7, 0x8e, 0x05, 0x37, 0x17, 0x62, 0x03, 0xdc, 0xad, 0x63, 0x0d,
0x6b, 0xd8, 0x86, 0x67, 0x5b, 0x28, 0x1d, 0xe4, 0x88, 0x92, 0xc0, 0x13, 0x5d, 0x6e, 0x5d, 0xfb,
0x20, 0xca, 0x70, 0xab, 0x95, 0xc7, 0xfe, 0xf6, 0x05, 0x22, 0x29, 0xca, 0x40, 0x2b, 0xd1, 0xb0,
0x41, 0xac, 0x04, 0x61, 0xce, 0x34, 0x5f, 0x73, 0x21, 0xf5, 0x67, 0xca, 0x39, 0x14, 0x12, 0x73,
0x41, 0xfd, 0x96, 0x4b, 0x0e, 0x3c, 0x5b, 0x1a, 0x10, 0x29, 0xe0, 0xd8, 0xd6, 0xd9, 0xd2, 0x3f,
0x76, 0x09, 0x4a, 0x89, 0x32, 0xf6, 0x78, 0x1f, 0x18, 0x42, 0xfe, 0xf4, 0x5f, 0x7c, 0x00, 0x2d,
0xe0, 0xa6, 0x67, 0xfb, 0xf4, 0xdd, 0xb1, 0x76, 0x62, 0x1d, 0xb5, 0x07, 0x71, 0x9d, 0xb5, 0xaf,
0x59, 0xb6, 0x70, 0xcd, 0xb5, 0xf5, 0xed, 0xf5, 0x5f, 0x05, 0x2b, 0x4f, 0x82, 0x91, 0x4f, 0x82,
0x91, 0xf7, 0x07, 0xa3, 0x9f, 0x04, 0x23, 0x5f, 0xd3, 0x0d, 0xed, 0xfe, 0xe1, 0xb1, 0x76, 0x55,
0xa8, 0xdf, 0xce, 0x81, 0xba, 0xdf, 0xd6, 0x41, 0x54, 0xb0, 0x4b, 0xb4, 0xe4, 0x52, 0x6d, 0xa1,
0xb1, 0xf6, 0x7b, 0x96, 0xd9, 0x4a, 0x2d, 0xcf, 0x69, 0xf6, 0x09, 0x31, 0xdb, 0x50, 0x15, 0x45,
0xbd, 0x9f, 0x28, 0xbd, 0x1f, 0x14, 0x84, 0x56, 0xc5, 0x55, 0x06, 0xbb, 0x52, 0x4b, 0x83, 0xc6,
0x24, 0x7c, 0x81, 0xa1, 0x30, 0xde, 0x53, 0xdf, 0x27, 0xe9, 0x04, 0xbe, 0xf6, 0x1e, 0x00, 0x7c,
0x8e, 0xc5, 0x72, 0x03, 0x31, 0x0c, 0x86, 0xd5, 0xd3, 0xf6, 0x54, 0x66, 0xe6, 0x6b, 0xf8, 0x0d,
0xca, 0x8c, 0x67, 0xcd, 0x82, 0x92, 0x78, 0x6a, 0x5b, 0x19, 0x6b, 0x83, 0x4f, 0x5f, 0xa5, 0xcc,
0x66, 0xfc, 0xbf, 0x0f, 0x1c, 0x49, 0xd3, 0xb3, 0xe5, 0x46, 0x1f, 0xbb, 0xa5, 0x32, 0x0a, 0x79,
0xe1, 0x00, 0xe3, 0x39, 0x05, 0x67, 0x7c, 0x6c, 0x31, 0x6d, 0xc6, 0x39, 0xc0, 0x85, 0xf1, 0xd4,
0x15, 0x72, 0x06, 0x1d, 0x95, 0x6b, 0x56, 0x8a, 0x25, 0x0c, 0xd4, 0xa2, 0x58, 0x9f, 0xc1, 0x32,
0x5a, 0x0e, 0x31, 0x72, 0x1e, 0xa3, 0x67, 0xa4, 0x9e, 0x9e, 0xe9, 0xc7, 0xd7, 0x24, 0x81, 0x91,
0xb3, 0x03, 0x94, 0x0a, 0xa6, 0xec, 0x5a, 0xb1, 0x37, 0xec, 0x31, 0xc4, 0x99, 0x61, 0x4c, 0x2c,
0xc1, 0x7c, 0x5b, 0x12, 0x98, 0xef, 0x9a, 0xba, 0x81, 0xe8, 0x3d, 0x6e, 0x1c, 0x93, 0x60, 0xb2,
0x06, 0x21, 0x49, 0xcb, 0x73, 0x17, 0xe6, 0x83, 0x94, 0x2b, 0x65, 0x18, 0x65, 0x76, 0x9a, 0x19,
0x3a, 0x14, 0x20, 0x7a, 0xb5, 0x8c, 0xbe, 0x59, 0x5e, 0xc6, 0x2a, 0x70, 0x54, 0xc6, 0x07, 0x9b,
0x87, 0xf8, 0x43, 0x10, 0xbc, 0x49, 0x14, 0x87, 0x92, 0x06, 0x22, 0xaf, 0x26, 0xde, 0x53, 0x9a,
0xff, 0x66, 0x84, 0xb0, 0x57, 0x6f, 0x5b, 0xab, 0x93, 0x9a, 0xe9, 0x38, 0x54, 0x43, 0x98, 0x7b,
0x34, 0x22, 0xe8, 0x38, 0x23, 0xa7, 0x9b, 0xb7, 0x58, 0x84, 0xf5, 0x2f, 0xa2, 0x08, 0xf3, 0x1f,
0x8a, 0xfa, 0xee, 0x8b, 0x1b, 0x42, 0x34, 0xa0, 0xc0, 0xff, 0xb3, 0x77, 0x86, 0x6c, 0x9d, 0x94,
0xad, 0xe3, 0x0b, 0x7b, 0xe6, 0x03, 0xb7, 0xfa, 0x0d, 0x37, 0xfb, 0x19, 0x37, 0xf3, 0x0d, 0x07,
0xd7, 0x81, 0x2f, 0x4c, 0xfa, 0xb4, 0xa2, 0x7c, 0xac, 0x01, 0x2f, 0x09, 0x0a, 0x78, 0x00, 0x7c,
0x90, 0x45, 0x76, 0xc4, 0x40, 0x0c, 0x44, 0x15, 0x66, 0xe6, 0x53, 0x98, 0xbd, 0xcf, 0x36, 0x9c,
0xb9, 0x80, 0x9e, 0xd9, 0x6d, 0x92, 0x47, 0xdd, 0x03, 0xb7, 0x1f, 0x66, 0x5a, 0x0a, 0xab, 0xea,
0xc3, 0x1f, 0xd3, 0xb7, 0x08, 0x98, 0x30, 0x17, 0x52, 0x62, 0x49, 0x61, 0x54, 0xc2, 0x19, 0x47,
0x75, 0xe4, 0xa9, 0x88, 0xe1, 0x0e, 0x7d, 0x16, 0x61, 0x12, 0x21, 0x4b, 0xd3, 0x32, 0x51, 0x46,
0x2c, 0xbc, 0xc2, 0x80, 0xfb, 0x82, 0xd8, 0x43, 0x52, 0x1e, 0x56, 0x84, 0x51, 0x77, 0xb0, 0x59,
0x79, 0xc5, 0x60, 0x5a, 0x49, 0x62, 0x09, 0xe7, 0x44, 0xe5, 0x70, 0xb5, 0x1d, 0x31, 0x9c, 0x8d,
0x9b, 0x70, 0x36, 0xd8, 0x29, 0xc5, 0x70, 0x2b, 0x48, 0x3d, 0x05, 0xd0, 0x30, 0x7e, 0x22, 0xc2,
0x80, 0xca, 0xda, 0xab, 0x04, 0x55, 0xc8, 0x5e, 0x28, 0x08, 0xfd, 0x22, 0x82, 0xe7, 0x96, 0xf4,
0xe1, 0x78, 0xc1, 0xcd, 0xf1, 0xcc, 0xcd, 0x35, 0xee, 0xd4, 0x3d, 0x59, 0xd0, 0x3d, 0x9e, 0xe8,
0x1e, 0x2f, 0xeb, 0xee, 0xbd, 0x7f, 0xa2, 0x65, 0x18, 0xe6, 0x46, 0xe5, 0xb7, 0xb8, 0x55, 0x14,
0xf0, 0x36, 0xd4, 0x7f, 0xeb, 0x88, 0x58, 0xc0, 0xd9, 0x6e, 0x26, 0x91, 0xac, 0x2b, 0xea, 0xc0,
0xdb, 0x88, 0x0d, 0xdc, 0x93, 0x3f, 0xd6, 0xc6, 0xb2, 0xa9, 0xd4, 0x12, 0x84, 0x47, 0x5c, 0x9e,
0x6d, 0x81, 0x72, 0x64, 0xeb, 0x06, 0x4e, 0x65, 0x76, 0x47, 0xbd, 0x67, 0x2f, 0x37, 0x2c, 0x4b,
0x37, 0xb1, 0x63, 0x3a, 0x4d, 0xc3, 0x9d, 0x6d, 0xc3, 0x7d, 0xab, 0x1a, 0x58, 0x8f, 0xc2, 0x79,
0xe7, 0xd0, 0x76, 0xad, 0xfe, 0x0c, 0xb1, 0x7a, 0x18, 0x00, 0x7d, 0x50, 0x35, 0x77, 0x03, 0x31,
0x0c, 0x56, 0xb9, 0x9d, 0xca, 0x34, 0x95, 0xb7, 0x52, 0x70, 0x2e, 0x33, 0xe3, 0xa8, 0xa7, 0xdc,
0x29, 0x89, 0x5f, 0x7c, 0xd6, 0x55, 0x76, 0xe0, 0xdf, 0x97, 0x99, 0x46, 0xc1, 0x87, 0x70, 0xb1,
0x7e, 0x88, 0xf9, 0x62, 0x2e, 0x83, 0x91, 0x24, 0x29, 0x39, 0x23, 0x0e, 0x95, 0x62, 0x23, 0xc8,
0xa1, 0xca, 0xea, 0x38, 0xc0, 0x64, 0xb9, 0x6e, 0x2d, 0x4c, 0xd6, 0x7d, 0x09, 0xfa, 0x6a, 0xc6,
0x7b, 0x4c, 0x24, 0xe6, 0x04, 0xfa, 0x94, 0x53, 0xa6, 0xc0, 0x8a, 0x25, 0x35, 0x71, 0x85, 0x51,
0x7d, 0x36, 0x97, 0x45, 0xcf, 0x6a, 0xc8, 0x66, 0xd0, 0x8a, 0x12, 0x4a, 0x20, 0x74, 0x82, 0xdc,
0x0a, 0xac, 0x8e, 0x2c, 0x7a, 0x76, 0x5e, 0xd4, 0xc3, 0x42, 0xf6, 0xff, 0xfb, 0xd0, 0xff, 0x57,
0xa5, 0x5a, 0xa6, 0xb0, 0xba, 0x9a, 0x41, 0x6f, 0x25, 0x60, 0xf4, 0xce, 0x05, 0x43, 0x75, 0x67,
0xca, 0x86, 0xe3, 0x4f, 0x9b, 0x66, 0xb6, 0x70, 0x97, 0x29, 0x7e, 0x2c, 0xfa, 0x02, 0x6b, 0x62,
0x9e, 0xe8, 0xa2, 0x2a, 0x05, 0xe8, 0xbc, 0x35, 0xfe, 0x98, 0x7d, 0x15, 0xaf, 0xa8, 0xf2, 0xa3,
0x82, 0x92, 0x65, 0x98, 0x7e, 0xca, 0x3d, 0xf0, 0x29, 0xf7, 0xc0, 0x5b, 0x6e, 0x98, 0xf8, 0x3f,
0xc4, 0xa0, 0x48, 0x82, 0x9e, 0xb5, 0xc1, 0x0a, 0x03, 0x2f, 0x4b, 0x80, 0x4b, 0x76, 0x9e, 0x37,
0x29, 0xc5, 0x4b, 0xb1, 0xa4, 0xf8, 0xce, 0xd5, 0xbb, 0xef, 0x62, 0x13, 0x51, 0x10, 0x5d, 0xda,
0xbe, 0x3c, 0x3b, 0x91, 0xe6, 0xb3, 0x99, 0x57, 0x1f, 0x4f, 0x16, 0x86, 0x8e, 0xe4, 0x82, 0x96,
0x4e, 0x32, 0xc5, 0x8b, 0xec, 0xb7, 0x13, 0x36, 0x4d, 0xd9, 0xc0, 0xd8, 0x77, 0xc0, 0xc2, 0xcb,
0xfe, 0x01, 0xb5, 0x8f, 0xb5, 0x95, 0xc4, 0x60, 0x0c, 0x84, 0x15, 0x1d, 0x33, 0x33, 0x54, 0x60,
0x6a, 0xe1, 0x20, 0x3c, 0x2a, 0x40, 0xcf, 0xa0, 0xf5, 0x93, 0x49, 0xfe, 0x61, 0xa1, 0xfb, 0x65,
0x5e, 0xa7, 0x97, 0x89, 0x66, 0xe6, 0x13, 0x18, 0x29, 0x42, 0xdd, 0xe3, 0x50, 0x30, 0x70, 0xff,
0x30, 0x96, 0xb2, 0x0e, 0x2b, 0x96, 0x0a, 0x75, 0x98, 0xb0, 0x60, 0x54, 0x10, 0x3c, 0xb5, 0x4d,
0x04, 0x47, 0x39, 0x1b, 0x83, 0xa5, 0x24, 0x54, 0xc2, 0x91, 0xa6, 0x9a, 0x42, 0x4b, 0x1a, 0xae,
0x30, 0xd2, 0x9c, 0xa4, 0x84, 0xda, 0xf8, 0x81, 0x0f, 0xc7, 0x22, 0x25, 0x1a, 0xd2, 0x9d, 0xe1,
0xea, 0x16, 0x0b, 0xd1, 0x21, 0x8a, 0x0d, 0x61, 0x3f, 0xf8, 0x27, 0x63, 0xf8, 0xb3, 0xa1, 0x1d,
0x79, 0x7e, 0xfa, 0x58, 0x48, 0x9a, 0x72, 0x95, 0xe2, 0xdc, 0xe2, 0x7e, 0x66, 0x41, 0xa6, 0xae,
0xa4, 0x0b, 0xfb, 0xf3, 0xcd, 0xf9, 0xda, 0x66, 0x37, 0xc0, 0xf7, 0xbf, 0xef, 0xc0, 0x6f, 0x84,
0x7c, 0x6d, 0xb5, 0x8b, 0x02, 0x5e, 0x47, 0xa8, 0xaf, 0x5d, 0x6e, 0x31, 0x9c, 0x2e, 0x01, 0x9f,
0x6e, 0xa4, 0x9e, 0x6a, 0x7f, 0xd1, 0x3c, 0xa0, 0x44, 0x13, 0x6a, 0x2c, 0x95, 0xb5, 0x70, 0xb9,
0xf4, 0xc6, 0xf3, 0xda, 0xee, 0x6c, 0x26, 0xba, 0x5d, 0x5f, 0xcc, 0x45, 0x9b, 0x86, 0x60, 0x3d,
0xd7, 0x75, 0xa9, 0x11, 0xfc, 0x7a, 0x84, 0xbc, 0xbd, 0x04, 0xbb, 0x3d, 0xe7, 0x1a, 0x00, 0x54,
0x4e, 0x85, 0x4d, 0x45, 0x41, 0x10, 0xdc, 0x28, 0xb4, 0xf0, 0x88, 0xe1, 0xee, 0x56, 0x00, 0x25,
0xe0, 0xd1, 0xc9, 0xc9, 0x42, 0x9e, 0xdc, 0xed, 0xd9, 0x97, 0xee, 0xbf, 0x6b, 0x7c, 0x7c, 0xa8,
0x3c, 0xdc, 0xdf, 0xdf, 0x33, 0x12, 0x07, 0x56, 0x85, 0x13, 0x3a, 0x49, 0x0a, 0x52, 0x14, 0xbc,
0x80, 0x87, 0x13, 0xc6, 0xab, 0x0e, 0x99, 0x7d, 0x96, 0x94, 0x69, 0x57, 0xc4, 0x21, 0x73, 0xea,
0x73, 0xa2, 0x9d, 0x39, 0x49, 0xf4, 0x89, 0x37, 0x56, 0x0e, 0x1f, 0x4f, 0xf8, 0xe8, 0x85, 0xc4,
0x8e, 0xf1, 0xf1, 0xfb, 0xf0, 0xf8, 0xfa, 0x08, 0x23, 0x2e, 0x28, 0x5f, 0x8b, 0x47, 0x52, 0xb6,
0x16, 0xe8, 0x8e, 0xe9, 0x1a, 0x21, 0xd3, 0xc5, 0xa0, 0xfe, 0xab, 0xa9, 0x6a, 0xeb, 0x9c, 0xe1,
0xc4, 0xb2, 0xa3, 0x6a, 0xb9, 0x4f, 0x07, 0xd0, 0xa9, 0xb6, 0xff, 0x0c, 0xce, 0xc1, 0xcb, 0x80,
0x0e, 0x45, 0xcf, 0x07, 0xe1, 0x62, 0x29, 0xb4, 0xb7, 0xf1, 0xe0, 0x18, 0xdb, 0xda, 0x78, 0x8c,
0x49, 0x40, 0x77, 0xe0, 0xb3, 0x5c, 0x01, 0x64, 0x39, 0x2f, 0x92, 0xb5, 0xbc, 0x70, 0x6b, 0x45,
0xd1, 0xcc, 0xe2, 0x43, 0xb2, 0x47, 0x02, 0x4d, 0x8e, 0xcb, 0xdc, 0x8c, 0x96, 0xc6, 0x33, 0x02,
0xc3, 0xb5, 0x24, 0xb1, 0x28, 0xde, 0xd9, 0x38, 0xbe, 0xb8, 0x02, 0xec, 0x17, 0x24, 0x23, 0x85,
0xe0, 0x46, 0x4a, 0xa1, 0x05, 0x94, 0x80, 0x05, 0x71, 0xea, 0x00, 0xb5, 0x51, 0x45, 0x56, 0x04,
0x51, 0x0c, 0xcc, 0x1a, 0xdb, 0xcd, 0x0e, 0x3b, 0x02, 0xce, 0x25, 0x70, 0xd8, 0xe7, 0xb5, 0xd4,
0xd0, 0x69, 0x49, 0xfa, 0x25, 0x7f, 0xe4, 0xf6, 0xb8, 0xbb, 0xee, 0xcb, 0x8b, 0xce, 0x2a, 0xd1,
0xe6, 0xc0, 0x4a, 0xf0, 0xfe, 0x3a, 0x17, 0xd6, 0xf5, 0x99, 0x8a, 0x29, 0x7b, 0x56, 0x8a, 0x71,
0xde, 0x82, 0x96, 0x46, 0x91, 0xd3, 0x7c, 0x23, 0x11, 0xdc, 0x59, 0x89, 0x8e, 0xe6, 0x1d, 0x3d,
0xb2, 0x04, 0xa7, 0x05, 0xb3, 0x8e, 0x03, 0x3e, 0x86, 0xd3, 0xec, 0xc6, 0xdb, 0xec, 0xe1, 0xa8,
0x6d, 0x69, 0x29, 0xe0, 0x92, 0xb5, 0xb7, 0x5a, 0x4b, 0x13, 0x19, 0xca, 0x73, 0xc5, 0x93, 0x7b,
0xc5, 0x01, 0xe7, 0x2e, 0xe5, 0x39, 0x18, 0xd1, 0xab, 0x4d, 0x68, 0xde, 0x9f, 0x58, 0xcc, 0x27,
0x78, 0x27, 0x9a, 0xb5, 0x5c, 0x54, 0x59, 0xa2, 0xd9, 0xcd, 0xef, 0xe6, 0x1d, 0x70, 0x6b, 0x9e,
0xb1, 0xa5, 0x8c, 0xd5, 0x18, 0xd3, 0x04, 0xbf, 0x56, 0x0b, 0x68, 0x98, 0xc7, 0xf3, 0x3a, 0xdb,
0x6f, 0x8a, 0xaf, 0x72, 0x1f, 0xff, 0xda, 0x69, 0xe7, 0x57, 0x1f, 0xec, 0xbe, 0xcd, 0x46, 0xaa,
0xe0, 0x8a, 0x74, 0x9f, 0xfc, 0x31, 0xf1, 0x25, 0x62, 0x08, 0xc9, 0xc8, 0xcc, 0xcb, 0xf6, 0xcb,
0x4f, 0x01, 0xb4, 0x41, 0x0d, 0xb8, 0x15, 0x05, 0x51, 0xf4, 0x06, 0xb5, 0xed, 0x35, 0xd4, 0x8a,
0x53, 0x33, 0xaa, 0xa3, 0x93, 0x67, 0xcf, 0x7d, 0x83, 0x8f, 0xe5, 0xf7, 0x9b, 0x63, 0x1f, 0x05,
0x78, 0xbf, 0x44, 0x96, 0x68, 0x8d, 0xa2, 0xb1, 0x29, 0x68, 0x41, 0x05, 0x65, 0xe0, 0x98, 0x40,
0xd1, 0x06, 0xd8, 0xd5, 0x81, 0xaa, 0x06, 0x0a, 0x85, 0x34, 0x86, 0x16, 0x99, 0x0b, 0xb4, 0x0f,
0x68, 0x7b, 0xe4, 0x6e, 0xee, 0x1a, 0x1e, 0x17, 0xa5, 0x23, 0x12, 0x16, 0x50, 0x8e, 0x9f, 0x30,
0xdc, 0x3c, 0x00, 0x1d, 0x54, 0xb4, 0xdb, 0x18, 0x6b, 0x49, 0x98, 0x80, 0x16, 0x6c, 0x34, 0x18,
0xc4, 0x05, 0xd1, 0xcf, 0xd3, 0xdf, 0xc9, 0xd1, 0xc9, 0x39, 0x5c, 0xc7, 0x8f, 0x02, 0x3b, 0xe8,
0x62, 0xff, 0x2f, 0xd5, 0x1a, 0x87, 0xb5, 0xe2, 0xf8, 0xf8, 0xf8, 0x08, 0x3d, 0xe1, 0xf4, 0x93,
0xf0, 0xa7, 0x72, 0xbc, 0x2c, 0x50, 0x6f, 0xc7, 0x36, 0x88, 0x96, 0xac, 0x86, 0x1c, 0xbb, 0xeb,
0x9c, 0x95, 0x03, 0x36, 0x0e, 0x04, 0x23, 0xa8, 0x37, 0xce, 0x84, 0x93, 0x43, 0x07, 0x42, 0xb3,
0xd2, 0x43, 0x21, 0xcc, 0xdf, 0x37, 0x22, 0xb4, 0xe2, 0xaf, 0x0c, 0x40, 0xd3, 0x6f, 0xe2, 0x30,
0x6e, 0x4f, 0xad, 0x0f, 0xf7, 0x50, 0x6a, 0xda, 0x6e, 0xaa, 0xd9, 0x6e, 0x66, 0x44, 0xd3, 0x03,
0x1f, 0xa7, 0x7b, 0x9a, 0x66, 0x06, 0x48, 0x27, 0x2f, 0xec, 0xa1, 0x58, 0x7f, 0x82, 0xfe, 0x01,
0x6d, 0x8e, 0x05, 0x72, 0x03, 0x31, 0x0c, 0x00, 0x55, 0x0e, 0x94, 0x99, 0xfb, 0x86, 0xd2, 0x23,
0x3a, 0x50, 0xa6, 0x41, 0xcd, 0xf1, 0xe9, 0x48, 0x8e, 0xe4, 0xc0, 0xef, 0xc3, 0x9c, 0x0c, 0x98,
0xbd, 0xbb, 0xf0, 0x4f, 0x0e, 0x63, 0x21, 0xe1, 0xf3, 0x03, 0x7a, 0x9c, 0x1b, 0xa7, 0x20, 0x2e,
0x50, 0x1c, 0x9f, 0x18, 0xdd, 0x2c, 0x80, 0xab, 0xba, 0xba, 0x50, 0x4d, 0x49, 0x15, 0x73, 0xf6,
0x83, 0x1c, 0xaa, 0x12, 0x98, 0xc0, 0xb1, 0x81, 0xc0, 0x11, 0xba, 0x42, 0x7e, 0x14, 0xa0, 0xe8,
0xc3, 0xe3, 0x03, 0x6c, 0x33, 0xe7, 0xa8, 0x81, 0x34, 0x02, 0x81, 0x8d, 0xaf, 0xc7, 0xe5, 0x34,
0x34, 0x0a, 0xe7, 0x3d, 0x64, 0x69, 0x0a, 0x59, 0x1a, 0x23, 0xf7, 0x47, 0xc8, 0x40, 0x4d, 0xc1,
0x4d, 0x38, 0x60, 0x77, 0x40, 0xc4, 0xbc, 0x66, 0x2d, 0x94, 0xa7, 0x14, 0x47, 0x38, 0x7b, 0x57,
0xd2, 0xa0, 0x50, 0x16, 0x28, 0xfd, 0xbd, 0xbc, 0x2d, 0x75, 0xdf, 0xf5, 0xdc, 0xd7, 0x61, 0x3d,
0xcb, 0xe0, 0x5a, 0x03, 0x21, 0xa7, 0x3b, 0xf7, 0x4a, 0xae, 0x9b, 0x14, 0x12, 0x6c, 0x4f, 0xf5,
0x6c, 0x1b, 0x47, 0x2c, 0xd9, 0xde, 0xf7, 0xa0, 0xd5, 0xe5, 0xf8, 0x5d, 0xdd, 0xf6, 0xb8, 0xf1,
0x78, 0xae, 0xf1, 0x64, 0xb6, 0x63, 0x67, 0xaa, 0xf1, 0x6c, 0xae, 0x71, 0x7b, 0xd8, 0xb8, 0xdd,
0xe5, 0xe4, 0x54, 0x38, 0x19, 0x7a, 0xb1, 0x63, 0xa1, 0x03,
0x7d, 0x51, 0x45, 0x76, 0x03, 0x31, 0x0c, 0xfd, 0xbb, 0xd2, 0xaa, 0xcc, 0xb8, 0xed, 0x2a, 0x70,
0x88, 0xde, 0x42, 0x43, 0x72, 0xe3, 0x17, 0xb3, 0x95, 0xc9, 0xf1, 0x0b, 0x61, 0x36, 0xa3, 0xf4,
0x01, 0x03, 0x36, 0xc2, 0x35, 0x49, 0x2a, 0xeb, 0x21, 0x27, 0x6a, 0xbb, 0x54, 0x7b, 0x1b, 0x4a,
0xa7, 0xbd, 0xa3, 0x54, 0x36, 0xda, 0xd3, 0x58, 0x2b, 0x4d, 0x8a, 0xad, 0x57, 0x0a, 0x9f, 0x0e,
0x17, 0x43, 0x9d, 0x33, 0x59, 0xdf, 0xb0, 0xc5, 0x45, 0xe2, 0xc0, 0xa5, 0x70, 0xc2, 0x03, 0x55,
0x49, 0x37, 0xdf, 0x4c, 0x9c, 0x83, 0xf3, 0x63, 0x7c, 0xa4, 0xdc, 0xed, 0x75, 0xf1, 0xe8, 0xab,
0xcc, 0xa9, 0xe5, 0x44, 0x36, 0x8a, 0xe0, 0xd2, 0x7b, 0x4b, 0xd3, 0x03, 0x3c, 0xd3, 0xfa, 0xdd,
0x45, 0x66, 0x97, 0x7d, 0xc2, 0x85, 0x70, 0xb2, 0xda, 0x95, 0x86, 0xea, 0x41, 0x29, 0xc0, 0xd7,
0x14, 0xe0, 0x0e, 0x60, 0x95, 0x61, 0x3c, 0x53, 0xc8, 0x78, 0x18, 0xe5, 0x0a, 0x0f, 0xff, 0x38,
0x71, 0xba, 0x82, 0xee, 0x74, 0x81, 0xee, 0x6c, 0x25, 0xef, 0xe9, 0x7a, 0xfc, 0xe3, 0xde, 0x81,
0xb8, 0x6a, 0x64, 0xcc, 0x6a, 0xf4, 0x97, 0x29, 0xd4, 0xdd, 0x49, 0x2e, 0x36, 0x24, 0x78, 0x98,
0x4a, 0x70, 0xb9, 0x4e, 0xf3, 0x8d, 0xb2, 0xb6, 0x6b, 0x80, 0xae, 0x37, 0x84, 0x38, 0x9d, 0x0a,
0xb1, 0x09, 0x74, 0xee, 0x54, 0xdb, 0xa7, 0xee, 0xff, 0xb0, 0x8d, 0xfb, 0x1f, 0xed, 0xc4, 0x28,
0x87, 0xd3, 0x9d, 0xac, 0xe6, 0x2e, 0xbe, 0x85, 0x3c, 0x5f, 0xfe, 0x02, 0x85, 0x4f, 0xc3, 0x76,
0x44, 0x41, 0x10, 0xbd, 0xdb, 0xd1, 0x36, 0x36, 0x36, 0xb1, 0x93, 0xbf, 0xa9, 0x79, 0xa8, 0x3e,
0xe9, 0x87, 0xf6, 0xe0, 0xef, 0xc7, 0xe6, 0xaa, 0x78, 0x85, 0x7f, 0x2e, 0x02, 0x27, 0xd4, 0xfe,
0xa2, 0x4f, 0x4a, 0x74, 0x69, 0x22, 0x25, 0xb5, 0x22, 0x17, 0xa5, 0x52, 0x93, 0x68, 0x15, 0x05,
0x09, 0x2e, 0xb5, 0x10, 0x38, 0x6b, 0xf9, 0x78, 0xd6, 0x77, 0xa4, 0x90, 0xd3, 0xe1, 0xba, 0xb4,
0x21, 0x4c, 0x87, 0x3a, 0x77, 0x4d, 0xa4, 0xbc, 0xd4, 0x2a, 0x97, 0x81, 0x82, 0x08, 0x6b, 0x9c,
0x71, 0xc1, 0xb3, 0xe7, 0x3d, 0xf7, 0xab, 0x45, 0xfd, 0xab, 0x05, 0xfd, 0xab, 0x45, 0xfd, 0xea,
0x06, 0x06, 0x9c, 0x4f, 0xaf, 0x37, 0x0a, 0x17, 0xc6, 0x8f, 0x26, 0x85, 0x93, 0x01, 0xc7, 0xe4,
0x34, 0x5f, 0x0b, 0x81, 0xfb, 0xc1, 0x69, 0x46, 0xa9, 0x70, 0x39, 0x4b, 0xa4, 0x50, 0x5d, 0x0a,
0xe4, 0xd8, 0x70, 0x14, 0xd8, 0xe1, 0x92, 0x74, 0xec, 0xd9, 0xb5, 0xd9, 0xd1, 0xf0, 0x19, 0x47,
0x5a, 0x97, 0x34, 0x59, 0x2c, 0x5f, 0x0e, 0x77, 0x85, 0x54, 0x78, 0x35, 0x1e, 0x8f, 0xb3, 0x9c,
0x0a, 0xd5, 0x5c, 0x7a, 0x4f, 0xa5, 0x4e, 0xb9, 0x44, 0xd5, 0x44, 0x2e, 0xc8, 0x30, 0x84, 0x8d,
0x5c, 0xa4, 0xec, 0x50, 0x75, 0x7f, 0x9b, 0xd8, 0x70, 0x45, 0xc6, 0xe3, 0x7c, 0x48, 0x84, 0xf3,
0x51, 0xca, 0x61, 0x1e, 0x34, 0x16, 0xd8, 0x1a, 0x33, 0xf3, 0x7d, 0x75, 0x50, 0xc5, 0x95, 0x04,
0x41, 0x08, 0xe5, 0xba, 0xee, 0xee, 0x31, 0xac, 0x66, 0xc3, 0x2b, 0xa1, 0x67, 0x68, 0xa3, 0x84,
0xb5, 0xe8, 0xc7, 0x5d, 0x4e, 0x38, 0x5f, 0xa0, 0x4b, 0xb5, 0x92, 0xc3, 0x9f, 0x37, 0x4c, 0x5f,
0x98, 0x28, 0x90, 0x51, 0x4a, 0x28, 0x36, 0x53, 0xfa, 0x19, 0x24, 0x4d, 0x54, 0x85, 0x43, 0x91,
0x06, 0xc7, 0x0d, 0xb8, 0x5e, 0x99, 0x1d, 0x64, 0x6a, 0xb3, 0x24, 0x38, 0x18, 0x9c, 0x35, 0xdc,
0x9a, 0x1a, 0x5d, 0xd7, 0x0c, 0x4e, 0x0a, 0x45, 0x27, 0x4d, 0x30, 0x2d, 0x4b, 0x8b, 0xc9, 0x78,
0x16, 0xb4, 0x35, 0xc1, 0x4b, 0xf1, 0x5d, 0xd7, 0xf0, 0xf2, 0x9d, 0x2d, 0xbc, 0xfc, 0x72, 0xc1,
0x70, 0x52, 0x71, 0xce, 0xd8, 0x88, 0xa7, 0x06, 0x4e, 0x82, 0x24, 0x4d, 0x86, 0x75, 0x1b, 0x91,
0xbb, 0x39, 0x91, 0x95, 0xc9, 0xc9, 0xf4, 0x02, 0x6e, 0x56, 0x26, 0xa7, 0x0b, 0xe4, 0xef, 0x57,
0xaf, 0x26, 0xe4, 0x4f, 0x96, 0xc9, 0xef, 0x4d, 0x7f, 0x9d, 0xa2, 0x4d, 0xec, 0x3b, 0x84, 0x94,
0x43, 0x2b, 0xbf, 0x70, 0x56, 0x53, 0xc7, 0xb8, 0x7f, 0x0c, 0x26, 0x29, 0xeb, 0x50, 0x99, 0x52,
0x56, 0x38, 0x5b, 0xfe, 0xba, 0xb7, 0x80, 0x78, 0xb1, 0x82, 0xb8, 0x3b, 0x41, 0xdc, 0x5d, 0x41,
0xdc, 0x6c, 0x17, 0x16, 0xd4, 0x48, 0x0b, 0x8f, 0x43, 0xd3, 0xa6, 0xf9, 0xc0, 0xba, 0x69, 0x3a,
0x34, 0x70, 0x92, 0x1f, 0xce, 0xf4, 0x5f, 0xae, 0x70, 0x3e, 0x5a, 0x60, 0x73, 0x38, 0x41, 0xef,
0x03, 0x75, 0xce, 0x83, 0x72, 0x04, 0x41, 0x10, 0x06, 0xe0, 0x2e, 0xc6, 0xb6, 0x6d, 0x3b, 0x79,
0x82, 0xd8, 0x2a, 0x77, 0x0d, 0x7a, 0xb3, 0xb3, 0x9a, 0xcd, 0xcc, 0xe4, 0xee, 0xf5, 0x73, 0xf6,
0x95, 0x9b, 0xdf, 0x0f, 0x3e, 0x45, 0x8e, 0x04, 0x66, 0x4e, 0xd1, 0x79, 0x0e, 0x1d, 0x99, 0x58,
0x25, 0x2c, 0x42, 0xe1, 0x33, 0x07, 0x70, 0x53, 0x1c, 0x7e, 0x2b, 0x43, 0x11, 0x59, 0x8b, 0x2f,
0x2c, 0x25, 0x83, 0x42, 0xc7, 0x29, 0x4b, 0x94, 0x4e, 0xd0, 0x30, 0xa9, 0x34, 0xf2, 0x88, 0x60,
0x0b, 0x53, 0x0b, 0xeb, 0x7f, 0x96, 0xc3, 0x78, 0xa8, 0x72, 0x9b, 0xb1, 0x96, 0x14, 0xc3, 0xb8,
0xa1, 0x94, 0x58, 0xee, 0x29, 0xcc, 0x23, 0x37, 0x4a, 0xfe, 0x10, 0x92, 0x4d, 0x13, 0x9d, 0x85,
0x05, 0xcd, 0x2d, 0x99, 0x0c, 0x19, 0x8c, 0x7f, 0x9d, 0x83, 0x09, 0xad, 0x63, 0x2c, 0x36, 0x60,
0x09, 0xeb, 0x67, 0x83, 0x1f, 0x86, 0x89, 0xb0, 0x8d, 0xbb, 0x93, 0x77, 0x37, 0xf3, 0xee, 0x64,
0x8d, 0x3b, 0x59, 0x71, 0x17, 0x1b, 0xdc, 0xa5, 0xfa, 0xdf, 0x53, 0x35, 0xee, 0x4a, 0x83, 0xdb,
0xf9, 0xfe, 0x70, 0xd2, 0xd2, 0x5c, 0xc8, 0x9b, 0xb3, 0x79, 0x73, 0x36, 0xab, 0x3c, 0x05, 0xbd,
0x15, 0x6d, 0xb4, 0x41, 0x9b, 0x35, 0xf6, 0xf8, 0xe4, 0x18, 0xc6, 0xea, 0x1f, 0xf7, 0xd5, 0xa0,
0x13, 0x0d, 0x68, 0xaf, 0xa5, 0xc4, 0x6a, 0x03, 0xa0, 0x42, 0x1d, 0x32, 0xf4, 0x59, 0x22, 0x7d,
0x8a, 0x24, 0x26, 0xc6, 0x43, 0x3a, 0x3e, 0xc6, 0x93, 0x43, 0xc9, 0x63, 0x3c, 0xbc, 0x3a, 0x6f,
0x99, 0xec, 0x28, 0x1f, 0x6a, 0xc9, 0x68, 0xc7, 0x1c, 0xc9, 0x96, 0x1b, 0x57, 0xf9, 0x8d, 0xb9,
0x4a, 0xe0, 0x7f, 0xa5, 0xcf, 0x31, 0x0e, 0xc2, 0x30, 0x0c, 0x85, 0x61, 0x4f, 0x5d, 0x01, 0xa1,
0x96, 0x13, 0xc0, 0x5c, 0x92, 0x63, 0xc0, 0xc8, 0x6e, 0xa5, 0x8d, 0x51, 0xa3, 0x34, 0x76, 0xe5,
0x14, 0xce, 0x8f, 0xca, 0x50, 0x95, 0x89, 0x21, 0xfb, 0xef, 0x4f, 0x7e, 0x10, 0xa2, 0x44, 0x87,
0x83, 0x63, 0x3f, 0xd0, 0xe8, 0x91, 0xf5, 0x89, 0x64, 0x0c, 0xda, 0xd6, 0x77, 0x09, 0x55, 0x24,
0x61, 0x26, 0x7d, 0x93, 0xc2, 0x31, 0x86, 0x9c, 0x31, 0x89, 0xa7, 0x04, 0x15, 0x3b, 0x96, 0x6f,
0x6b, 0xcc, 0xd2, 0xf5, 0x92, 0x26, 0xc7, 0x41, 0x18, 0xd5, 0xf9, 0x20, 0xd8, 0x8d, 0x04, 0xe7,
0x57, 0xee, 0xa0, 0xde, 0x1c, 0xd5, 0x4a, 0x13, 0xb9, 0x99, 0x14, 0x9a, 0x0d, 0xbb, 0xb3, 0x6d,
0xa1, 0xb0, 0xb7, 0x85, 0x40, 0x95, 0xe7, 0xd0, 0xc7, 0x3f, 0x73, 0x2e, 0x8b, 0xd5, 0x6c, 0xac,
0x66, 0xb5, 0x4e, 0x3f, 0xcf, 0xb4, 0x85, 0xc2, 0xc1, 0x16, 0x02, 0x70, 0x23, 0x9a, 0x66, 0x22,
0xbe, 0x3f, 0xae, 0xb8, 0x16, 0x1f, 0x8c, 0x8e, 0x35, 0x57, 0x04, 0x51, 0x0c, 0x85, 0xd3, 0xe1,
0x4e, 0x85, 0x7b, 0x85, 0x6b, 0x8f, 0x5b, 0xc7, 0xf6, 0xf7, 0x8c, 0xe4, 0xb0, 0x6f, 0x24, 0x79,
0x86, 0xfd, 0x7a, 0xdc, 0xbd, 0xbd, 0xfa, 0xd1, 0x09, 0xb3, 0x8d, 0xcc, 0x72, 0xda, 0x58, 0x81,
0x67, 0xcb, 0x49, 0x64, 0x8f, 0x4a, 0x7d, 0x02, 0x8d, 0x09, 0x44, 0xc1, 0xd7, 0x91, 0xbd, 0x24,
0x15, 0x02, 0x4b, 0x50, 0x1f, 0x88, 0x4e, 0x4d, 0x75, 0x73, 0xa0, 0x68, 0x2c, 0xec, 0x65, 0x4d,
0x5d, 0x38, 0x35, 0x91, 0x91, 0x69, 0x6d, 0x13, 0x31, 0x2a, 0xf0, 0x49, 0x6e, 0x14, 0x69, 0xc5,
0x34, 0x7d, 0x11, 0x52, 0x1a, 0x28, 0x4d, 0x08, 0xa8, 0x35, 0xe7, 0x9a, 0x06, 0x44, 0x05, 0xa1,
0xc9, 0x55, 0xf5, 0x6d, 0x7e, 0xe1, 0x31, 0xff, 0xc2, 0x40, 0x83, 0xaa, 0x35, 0x02, 0xfb, 0x4b,
0xf6, 0xd4, 0x85, 0x9d, 0xc4, 0xe7, 0xff, 0x7c, 0xf9, 0x76, 0xa1, 0xfb, 0xbb, 0xee, 0xd8, 0x43,
0xb7, 0xfb, 0x5d, 0xb7, 0xfb, 0xb5, 0xdb, 0xf3, 0xae, 0xdb, 0xba, 0x76, 0xb6, 0x86, 0x70, 0xbd,
0xb2, 0xba, 0xb9, 0xfa, 0x2d, 0xc2, 0x1c, 0x6c, 0xf8, 0x8d, 0x63, 0x18, 0xa9, 0x37, 0xf9, 0x39,
0x83, 0x83, 0x15, 0xbd, 0xa2, 0x11, 0x4d, 0x9f, 0x96, 0x51, 0xbb, 0x18, 0x3f, 0x60, 0x8e, 0xe1,
0xa3, 0x37, 0x10, 0xd9, 0xd7, 0x46, 0x92, 0x0a, 0x59, 0x33, 0x89, 0xd4, 0xbb, 0x75, 0x37, 0x33,
0x6c, 0x2e, 0xa0, 0xc0, 0x72, 0x00, 0x6d, 0x90, 0x35, 0x7b, 0x03, 0x31, 0x10, 0x44, 0xb7, 0x0b,
0x33, 0x33, 0x53, 0x67, 0x76, 0x1d, 0x66, 0xa6, 0x72, 0x4c, 0xeb, 0x44, 0x9f, 0xa5, 0xd3, 0x9d,
0x20, 0xf0, 0xeb, 0xc3, 0x60, 0x38, 0x71, 0xf5, 0xe6, 0x69, 0xe8, 0x44, 0xc8, 0xd7, 0x7d, 0x8d,
0x9b, 0xf4, 0x75, 0x1a, 0xf6, 0x25, 0x99, 0xca, 0xe7, 0x60, 0xb4, 0x56, 0xb0, 0x6c, 0x9e, 0xd8,
0xd0, 0x2c, 0x74, 0xe9, 0xfb, 0x09, 0x15, 0x39, 0x47, 0x43, 0x8e, 0x8d, 0x12, 0x41, 0x51, 0xa2,
0xfc, 0x58, 0x74, 0xd4, 0x8e, 0x43, 0x15, 0x7a, 0x69, 0x19, 0x17, 0xd2, 0x5b, 0x18, 0x0e, 0xb9,
0xe8, 0xd8, 0x50, 0xfb, 0x16, 0x17, 0x15, 0x92, 0xf7, 0x28, 0x6b, 0x15, 0x16, 0x03, 0xa1, 0x03,
0x98, 0x62, 0x45, 0x68, 0x94, 0x24, 0xd3, 0x12, 0x42, 0x4b, 0xf3, 0x55, 0x2f, 0x25, 0xcd, 0x7b,
0x5b, 0xa2, 0xf9, 0x67, 0x51, 0x15, 0xd4, 0x5f, 0x13, 0xd6, 0x42, 0xe9, 0x0a, 0x2b, 0xea, 0xff,
0x03, 0x8d, 0xa3, 0x64, 0x44, 0xe5, 0x81, 0xc1, 0x36, 0x0c, 0xf4, 0x33, 0x4d, 0x34, 0xea, 0x0c,
0xd4, 0xa9, 0x4e, 0x35, 0xa9, 0xb6, 0xef, 0x70, 0xb9, 0x16, 0x2b, 0x30, 0xf3, 0x99, 0xda, 0x53,
0x97, 0xd7, 0xf3, 0x2f, 0xbe, 0x7b, 0x73, 0x80, 0x5d, 0x29, 0x1c, 0x7f, 0xb5, 0x91, 0x4b, 0xc5,
0x02, 0x36, 0x3e, 0x01, 0x13, 0x75, 0x80, 0x89, 0x3f, 0xc0, 0x42, 0x93, 0xc5, 0x64, 0x9d, 0xe1,
0x72, 0xb3, 0xa1, 0xd4, 0xa6, 0x88, 0xed, 0x5c, 0x5c, 0x06, 0xe2, 0x0b, 0x41, 0xfd, 0x97, 0x41,
0xed, 0x27, 0x9f, 0x84, 0xbb, 0x14, 0x92, 0x1f, 0x33, 0x17, 0xef, 0xfa, 0xd9, 0xf6, 0xea, 0xa7,
0xf0, 0xea, 0x57, 0xcf, 0xa3, 0x75, 0xd8, 0x77, 0xad, 0x8f, 0x35, 0x5b, 0x04, 0x61, 0x0c, 0x84,
0x83, 0x53, 0xe1, 0x52, 0xe1, 0x52, 0xe3, 0x50, 0xe3, 0xda, 0x60, 0xed, 0x3c, 0x2b, 0x39, 0x36,
0x6b, 0xd9, 0x4f, 0xce, 0x7e, 0x3d, 0xee, 0x50, 0x5e, 0x19, 0x99, 0x99, 0x77, 0xe8, 0x52, 0xf2,
0xf6, 0x89, 0xe2, 0xf6, 0x52, 0x6d, 0x80, 0xfb, 0x0d, 0xac, 0x63, 0x1d, 0x3b, 0xb0, 0x5c, 0x71,
0xe0, 0xd9, 0xd2, 0x2c, 0x34, 0x74, 0x6c, 0x1b, 0x6c, 0x51, 0x18, 0xef, 0x41, 0xd3, 0xaa, 0x05,
0xde, 0x36, 0xb4, 0xf0, 0xfb, 0x38, 0xf5, 0x2c, 0x29, 0xa4, 0x0c, 0x72, 0x44, 0x49, 0xe0, 0x89,
0xae, 0xb6, 0x6f, 0x7c, 0x10, 0x65, 0xb8, 0x2b, 0xc5, 0xe3, 0x60, 0xe7, 0x12, 0x91, 0x16, 0x55,
0x50, 0x8a, 0x96, 0xb0, 0x41, 0x2c, 0x8a, 0x30, 0x67, 0x5a, 0xa8, 0xbb, 0x90, 0x46, 0x33, 0x71,
0x0e, 0x85, 0xc6, 0x5c, 0xd0, 0xe8, 0x67, 0xfa, 0xd8, 0xb7, 0xb0, 0x2e, 0x76, 0x09, 0x2a, 0x8d,
0x32, 0xf6, 0x5f, 0x78, 0x13, 0xc8, 0x5f, 0xb8, 0xd5, 0x07, 0x28, 0x15, 0xdc, 0x7a, 0xde, 0xbd,
0xa4, 0x3b, 0x2e, 0x9d, 0x5a, 0x47, 0xbd, 0x41, 0xdc, 0xe0, 0xd2, 0xd7, 0x2d, 0x5b, 0xb8, 0xd6,
0xfa, 0xc6, 0xce, 0xc6, 0xbf, 0x04, 0xab, 0x2f, 0x04, 0x53, 0xdf, 0x08, 0xa6, 0x3e, 0x03, 0xbe,
0xd5, 0xfd, 0xdd, 0x6e, 0x62, 0xaf, 0x83, 0x66, 0xbd, 0x52, 0xc8, 0x7f, 0x76, 0xa0, 0xc1, 0x0f,
0x39, 0x88, 0x0a, 0x76, 0x49, 0xa9, 0xb9, 0x3e, 0xb4, 0xd1, 0x5c, 0xff, 0xbf, 0xcb, 0x5c, 0xad,
0x9e, 0xe7, 0x34, 0xf7, 0x02, 0x31, 0xd7, 0x94, 0x9a, 0xd0, 0xf0, 0x37, 0x94, 0xe1, 0x4f, 0x94,
0x27, 0x6d, 0x8f, 0xb5, 0x76, 0x43, 0x31, 0x0c, 0x40, 0x55, 0x66, 0x58, 0xca, 0xb4, 0x75, 0x0d,
0xd3, 0x5e, 0xa6, 0xad, 0xb3, 0xce, 0x03, 0x25, 0xf1, 0x89, 0x6d, 0xbd, 0xca, 0x0e, 0xfd, 0x7d,
0xc3, 0x6c, 0x46, 0xe9, 0x5e, 0x30, 0xe4, 0xea, 0x96, 0x35, 0xd7, 0xba, 0xd8, 0xce, 0x64, 0x51,
0x28, 0xa1, 0xc0, 0x93, 0x60, 0x28, 0x2a, 0xae, 0x11, 0x92, 0x4b, 0x2c, 0xb7, 0xe1, 0x92, 0x43,
0x47, 0xd2, 0x22, 0x41, 0xf3, 0xe7, 0x3d, 0x9c, 0x32, 0x1b, 0x1c, 0x1e, 0xc0, 0x35, 0xce, 0xdf,
0x9d, 0x38, 0xb2, 0x8e, 0x05, 0x4e, 0x3c, 0x89, 0x51, 0x36, 0xd0, 0x18, 0xd5, 0x03, 0x0f, 0xf0,
0xad, 0x2c, 0xb5, 0x1d, 0x19, 0x85, 0x86, 0xb2, 0x05, 0xed, 0xd2, 0x99, 0x49, 0x32, 0xb8, 0x41,
0xcd, 0x12, 0x20, 0xfb, 0x00, 0x2d, 0x23, 0x75, 0x3c, 0x49, 0xff, 0xe3, 0x30, 0x92, 0x83, 0xb5,
0xcf, 0x67, 0x74, 0x39, 0x8c, 0xd8, 0x24, 0x81, 0x55, 0x6c, 0x51, 0x82, 0x58, 0x31, 0x86, 0x9a,
0xe0, 0xa2, 0xe9, 0x42, 0xb8, 0x68, 0xab, 0xaa, 0x82, 0x9d, 0x49, 0xb8, 0x93, 0x05, 0xfa, 0x0b,
0x71, 0xd9, 0x5c, 0x16, 0x76, 0x67, 0xa8, 0x77, 0x46, 0x94, 0x3b, 0x0b, 0x94, 0x86, 0xc5, 0x11,
0x86, 0x64, 0xa3, 0x3a, 0x4a, 0xd0, 0xc8, 0x17, 0x73, 0x99, 0x69, 0x5e, 0x38, 0xf4, 0xe9, 0x4a,
0x71, 0x66, 0xbf, 0x9d, 0x08, 0xc7, 0xcd, 0xc8, 0xf7, 0xd6, 0x2b, 0x5e, 0xc3, 0x4f, 0x60, 0x19,
0x5f, 0xb3, 0xf8, 0xab, 0xbd, 0x04, 0x53, 0x5b, 0xb0, 0x2a, 0x0c, 0x35, 0xa1, 0x8b, 0x84, 0xc8,
0x62, 0xc4, 0xd6, 0x52, 0xe4, 0x57, 0xe9, 0x21, 0x3c, 0x56, 0x9b, 0x5a, 0xf7, 0xa6, 0x9e, 0x66,
0x6f, 0xec, 0x7b, 0x22, 0x9c, 0x37, 0x94, 0x73, 0x68, 0x38, 0x26, 0xd3, 0xdb, 0x8c, 0xc3, 0x22,
0x3c, 0xcc, 0x59, 0x23, 0x5c, 0x4c, 0x7d, 0x7b, 0xef, 0xe6, 0x44, 0x11, 0xfe, 0x01, 0x7d, 0x90,
0x45, 0x5a, 0x04, 0x41, 0x0c, 0x46, 0x83, 0xbb, 0xbb, 0xed, 0x38, 0xc0, 0xc8, 0x09, 0xd8, 0xe2,
0x70, 0x81, 0xb4, 0x65, 0x20, 0x2d, 0x95, 0x26, 0x55, 0xe8, 0xe9, 0xa9, 0x71, 0x9f, 0xee, 0x72,
0x7b, 0xff, 0xf7, 0xc0, 0x70, 0x14, 0xe5, 0x84, 0x7f, 0xa4, 0x82, 0xb1, 0x18, 0x43, 0xb1, 0xc3,
0x58, 0x8a, 0x32, 0x34, 0x2c, 0x06, 0x35, 0x4c, 0x58, 0xb0, 0x79, 0x00, 0xae, 0x1b, 0x9f, 0x79,
0xee, 0xbb, 0x4f, 0x1b, 0xf9, 0xf6, 0x9b, 0x1b, 0x8c, 0x70, 0xa8, 0x54, 0x52, 0xe8, 0x48, 0x11,
0xae, 0x22, 0xe5, 0xe4, 0x8d, 0x90, 0x6c, 0x69, 0xe4, 0x1b, 0xe1, 0x48, 0xa4, 0x40, 0x4b, 0xfa,
0xd5, 0xdc, 0x3c, 0x74, 0xa4, 0x05, 0x9b, 0x30, 0xc7, 0xf8, 0x3d, 0x74, 0x08, 0xf0, 0xac, 0xf2,
0xc0, 0xb1, 0xca, 0x24, 0x14, 0x9c, 0x79, 0x04, 0xac, 0x65, 0x6c, 0x2d, 0x16, 0x92, 0x50, 0x01,
0x6b, 0x5d, 0x0c, 0xec, 0x61, 0x07, 0xa3, 0xb6, 0x56, 0xaf, 0x35, 0xdf, 0xe7, 0x30, 0xaf, 0xc2,
0x7e, 0x2e, 0x1a, 0xa2, 0xb8, 0x10, 0x8d, 0x20, 0xfd, 0x38, 0xd2, 0x26, 0xcb, 0x92, 0xb1, 0xa2,
0x16, 0xd6, 0xfb, 0x51, 0x60, 0xad, 0xb5, 0xe8, 0xfb, 0xa1, 0x44, 0x00, 0xaf, 0xd5, 0x47, 0x9a,
0x9c, 0xe6, 0xb8, 0x99, 0x66, 0x79, 0x20, 0xcd, 0x72, 0x2f, 0xcd, 0x36, 0xce, 0xe4, 0xae, 0x0c,
0x70, 0x97, 0x3b, 0xdc, 0xe5, 0x61, 0xee, 0xdc, 0xcd, 0x1d, 0xd6, 0xab, 0xd5, 0xda, 0x64, 0x0f,
0x4d, 0xe3, 0x6d, 0x1b, 0x67, 0x4d, 0xdf, 0xb3, 0x9c, 0xf4, 0xd5, 0xc3, 0x59, 0xcb, 0x0d, 0xec,
0x4b, 0xd4, 0xd1, 0x5f, 0x7c, 0x38, 0x37, 0x24, 0xe1, 0x1f, 0x62, 0x08, 0x72, 0xf4, 0x06, 0x10,
0x3e, 0xdd, 0x58, 0x0a, 0xc4, 0x30, 0x54, 0x9b, 0x77, 0x5a, 0xba, 0x39, 0xc0, 0xd6, 0x93, 0x7d,
0x1d, 0x3d, 0x07, 0xb1, 0xf8, 0x61, 0x5b, 0x20, 0x8b, 0x70, 0xfb, 0xcd, 0xa1, 0x83, 0x4a, 0xf1,
0x27, 0x9c, 0x86, 0x61, 0x44, 0x61, 0xce, 0x58, 0x49, 0x8e, 0x24, 0xc8, 0xee, 0xa7, 0xc9, 0x7b,
0x55, 0x68, 0x2a, 0x95, 0xca, 0x02, 0x8d, 0x92, 0xe4, 0x58, 0x6c, 0x42, 0xbf, 0xb1, 0x0a, 0x0f,
0x73, 0x3f, 0xa0, 0xd0, 0x8e, 0xac, 0x92, 0x60, 0x62, 0xb1, 0xc8, 0x6a, 0xb1, 0x30, 0xd2, 0x59,
0x49, 0x3e, 0xff, 0xbe, 0x81, 0x15, 0x56, 0x62, 0xb7, 0xc3, 0x34, 0xf5, 0x23, 0x9e, 0xc6, 0x65,
0x3f, 0x98, 0xbf, 0x6f, 0x58, 0x1d, 0x4a, 0x5c, 0x47, 0x0a, 0xff, 0x9b, 0xfb, 0xd9, 0x4c, 0x03,
0x7a, 0xce, 0x3b, 0x5b, 0x22, 0x17, 0x14, 0x1b, 0x22, 0xa3, 0x4b, 0x04, 0x2d, 0xe9, 0x86, 0xa4,
0x90, 0x42, 0xbb, 0x3e, 0xa4, 0x04, 0xed, 0xa1, 0x3a, 0x68, 0xb6, 0xb1, 0x56, 0xcc, 0x1c, 0x28,
0x43, 0xf3, 0xe7, 0xc6, 0x49, 0x0c, 0xaf, 0x84, 0x52, 0xc7, 0x69, 0xfc, 0x0c, 0x15, 0x6d, 0x1a,
0x2e, 0x5b, 0x7c, 0x19, 0xaf, 0x44, 0xb8, 0x1e, 0x70, 0xe9, 0xfb, 0x01, 0x6b, 0x62, 0x45, 0xff,
0x1f, 0xa7, 0xfd, 0xd0, 0x0f, 0x1f, 0x7f, 0x14, 0x48, 0x90, 0x4b, 0x7a, 0x5f, 0x25, 0x16, 0x2f,
0x0b, 0xa1, 0x87, 0x0a, 0x17, 0x52, 0x5c, 0xc0, 0x3c, 0x8d, 0xdf, 0x79, 0xa2, 0x78, 0x65, 0x01,
0xb4, 0x4f, 0x03, 0x8f, 0x05, 0x41, 0x0c, 0xee, 0xd9, 0xb6, 0x1d, 0x9e, 0x15, 0x27, 0x67, 0x9b,
0x71, 0x33, 0x58, 0xcc, 0x5b, 0x74, 0xd3, 0x99, 0x87, 0x7f, 0x7f, 0xb6, 0xc2, 0x0b, 0xea, 0x7e,
0x80, 0x8b, 0xf5, 0x43, 0x5c, 0x5e, 0x5d, 0x5a, 0x40, 0xeb, 0xa5, 0x96, 0x18, 0x63, 0x62, 0x81,
0xe4, 0x04, 0xa6, 0x84, 0x5e, 0xc9, 0x79, 0x9c, 0x8a, 0xf8, 0x65, 0x67, 0xa1, 0xee, 0xa1, 0x4f,
0xcc, 0xe3, 0x44, 0x85, 0xc2, 0x41, 0xe5, 0xad, 0xb1, 0xc7, 0x9e, 0x0d, 0xf1, 0x4a, 0x04, 0xa8,
0x28, 0xc9, 0x44, 0x6a, 0x28, 0x45, 0x16, 0xda, 0x10, 0xca, 0xd8, 0x83, 0xd1, 0xbc, 0x95, 0xd0,
0x1a, 0x19, 0x6b, 0x31, 0x21, 0xed, 0x25, 0xd0, 0xca, 0x5e, 0xe6, 0x09, 0xe7, 0x31, 0xf4, 0xfe,
0xcd, 0xd4, 0x46, 0x94, 0x3c, 0x34, 0x5c, 0xf0, 0x18, 0x5a, 0x9f, 0x87, 0x00, 0x97, 0x0f, 0xd9,
0xdb, 0x14, 0x19, 0x5e, 0x52, 0x2c, 0x18, 0xdf, 0xb0, 0x6a, 0xf7, 0x53, 0x6d, 0x94, 0x70, 0xc4,
0x33, 0xdb, 0x97, 0x67, 0x27, 0x54, 0x7c, 0x12, 0xf3, 0xa2, 0xe3, 0x51, 0x42, 0xfb, 0x11, 0x5d,
0x88, 0x99, 0x93, 0x85, 0xd5, 0x8b, 0xc5, 0x2f, 0x2b, 0x2c, 0x1a, 0xdf, 0x40, 0xf7, 0xd7, 0x87,
0xa9, 0xe7, 0x39, 0xd8, 0x47, 0xa2, 0x92, 0x11, 0x84, 0x4b, 0xf3, 0x97, 0x3f, 0x5a, 0x1c, 0x7e,
0x3c, 0x6f, 0xfe, 0x60, 0xb1, 0xf9, 0x4d, 0x56, 0x37, 0x4a, 0x36, 0x3a, 0xf0, 0x90, 0xed, 0xe2,
0xd2, 0x22, 0xb4, 0x7c, 0xb0, 0xd4, 0xf7, 0xe6, 0x1e, 0x1a, 0x96, 0xfe, 0x09, 0x18, 0x2e, 0x9d,
0x70, 0x8f, 0x98, 0xf7, 0x73, 0x37, 0x02, 0x0a, 0xa6, 0xa7, 0x67, 0xe6, 0xa5, 0x23, 0x82, 0x0c,
0x00, 0x6d, 0x51, 0x03, 0x8f, 0xc4, 0x50, 0x10, 0x9e, 0xe8, 0x6c, 0x5b, 0xd1, 0x19, 0xab, 0x18,
0xe7, 0xf8, 0x8c, 0x70, 0x52, 0xcc, 0x36, 0xaf, 0x9a, 0x3e, 0x2c, 0xfe, 0xfd, 0x9a, 0x4d, 0xc3,
0xf1, 0x7c, 0x80, 0x2f, 0x63, 0x19, 0xc1, 0x31, 0xbe, 0xe6, 0x30, 0x64, 0xcf, 0x13, 0xb1, 0x87,
0x8a, 0x12, 0xb2, 0x0c, 0x29, 0xb4, 0x95, 0x70, 0x3d, 0x42, 0xd2, 0x49, 0xcc, 0x35, 0x98, 0xed,
0xd7, 0x61, 0x35, 0xd5, 0x99, 0xce, 0xe3, 0xf3, 0xd7, 0x7b, 0x3e, 0x87, 0x0e, 0x47, 0x89, 0x15,
0xb7, 0xcf, 0x29, 0xcb, 0x15, 0x8c, 0x76, 0x48, 0x70, 0x5c, 0xae, 0x84, 0x21, 0x1c, 0x57, 0xb4,
0x0d, 0xc7, 0x35, 0x51, 0x16, 0xb0, 0x1c, 0x08, 0xad, 0x31, 0x62, 0x97, 0x22, 0x58, 0x4e, 0xff,
0x84, 0x65, 0x95, 0x1b, 0x26, 0x7b, 0xc8, 0xb6, 0x26, 0x55, 0x25, 0x85, 0x91, 0x34, 0x06, 0xd6,
0x99, 0x23, 0xec, 0x16, 0xe0, 0x30, 0xd5, 0x5b, 0xd3, 0x14, 0x6b, 0x56, 0xb0, 0xd2, 0x5f, 0xde,
0x4e, 0x0d, 0xac, 0xf4, 0x97, 0x33, 0x0e, 0x2f, 0xf7, 0x96, 0xc1, 0xdc, 0xdd, 0xde, 0xde, 0x52,
0x26, 0x91, 0xcd, 0x36, 0x85, 0xc9, 0x11, 0xf0, 0x93, 0x03, 0x9c, 0x8b, 0x18, 0xb2, 0xb2, 0x90,
0x8d, 0x85, 0x31, 0x23, 0xd5, 0x5b, 0xb5, 0xd8, 0x0a, 0xb1, 0x7b, 0x54, 0xc3, 0xd4, 0xc8, 0xe7,
0xc9, 0xfe, 0xa7, 0x6f, 0x7c, 0x20, 0x2b, 0xc2, 0xaf, 0x3c, 0x7e, 0x55, 0x12, 0x45, 0x11, 0xe1,
0xd7, 0xff, 0x5d, 0xae, 0x94, 0x2d, 0xe2, 0x25, 0x26, 0x1a, 0xce, 0x3a, 0xf2, 0x6d, 0x8f, 0x20,
0xd8, 0x1e, 0x20, 0x38, 0x4e, 0x59, 0x72, 0x32, 0xce, 0x6f, 0x67, 0x04, 0x41, 0x13, 0xb5, 0x50,
0xb5, 0x56, 0x05, 0x31, 0x10, 0x9d, 0x2d, 0x71, 0x77, 0xfd, 0x04, 0x1c, 0x6a, 0x68, 0x71, 0xef,
0xee, 0x59, 0x99, 0x07, 0x39, 0x2f, 0x2e, 0x2c, 0x7f, 0x8f, 0xbb, 0x6b, 0x95, 0x8c, 0x5d, 0xa3,
0x7d, 0xac, 0x72, 0xae, 0xb0, 0x37, 0x8f, 0xbd, 0x64, 0x3d, 0x2b, 0xc6, 0xde, 0xf1, 0xec, 0xdc,
0xd2, 0x1c, 0xbc, 0x31, 0x0a, 0x81, 0xfd, 0x19, 0x7b, 0x98, 0xe2, 0xee, 0xa3, 0x5c, 0x8c, 0x94,
0x15, 0x9c, 0xab, 0xfb, 0xb5, 0xd2, 0x28, 0x9b, 0x6b, 0x61, 0x34, 0x7c, 0x5e, 0x09, 0x83, 0x42,
0x32, 0x4d, 0xc3, 0x06, 0xea, 0x68, 0x8a, 0x10, 0xa0, 0x4c, 0xc5, 0x8a, 0x3a, 0x3c, 0x5b, 0xce,
0x23, 0x7b, 0x1a, 0x78, 0x81, 0xd5, 0xf9, 0xc8, 0x42, 0xc3, 0x2f, 0x66, 0xad, 0xcb, 0x4b, 0xff,
0x09, 0x9f, 0xb1, 0x0e, 0x31, 0xf7, 0x58, 0x9b, 0x47, 0x38, 0xbf, 0xf1, 0xf2, 0x04, 0xb2, 0xf7,
0x01, 0x72, 0x14, 0x85, 0x17, 0xd5, 0x09, 0x83, 0x83, 0xd5, 0xa6, 0xa6, 0xee, 0x95, 0xaf, 0xed,
0x65, 0xa7, 0x42, 0x37, 0x37, 0x4d, 0xc5, 0xd8, 0x98, 0x7d, 0xd3, 0xc6, 0x44, 0x0a, 0xc5, 0x3b,
0x36, 0x9e, 0xca, 0x6e, 0x9b, 0x7b, 0xfb, 0xba, 0x91, 0xa4, 0xa4, 0x89, 0xab, 0x25, 0x91, 0xcb,
0x5b, 0xac, 0x89, 0x5a, 0x34, 0xc4, 0x73, 0xc4, 0xdd, 0xc7, 0x60, 0x9e, 0xab, 0xbb, 0x04, 0x95,
0xd0, 0x41, 0x4b, 0xc4, 0x30, 0x10, 0x05, 0xe0, 0xb9, 0x97, 0x7a, 0x13, 0xbc, 0x14, 0xfc, 0x0d,
0x8a, 0x8a, 0x3f, 0x42, 0x0f, 0xe2, 0xfd, 0x91, 0x6e, 0xdf, 0xda, 0xb0, 0x49, 0xa6, 0xce, 0x64,
0x5d, 0x7f, 0xbe, 0x68, 0x0f, 0x6e, 0x21, 0x08, 0x9e, 0x43, 0xde, 0xf7, 0xde, 0xc8, 0xeb, 0x1c,
0xcb, 0xe1, 0x59, 0x27, 0xe2, 0xe9, 0x06, 0xa6, 0x9a, 0xe1, 0xb4, 0x0f, 0x9a, 0xf4, 0x95, 0x96,
0x63, 0x09, 0x09, 0xbb, 0x39, 0x54, 0xe9, 0x6e, 0xb1, 0xd3, 0xbc, 0x84, 0x12, 0xb5, 0xc0, 0xc2,
0x14, 0x15, 0x63, 0xa2, 0x0c, 0x47, 0x1f, 0xa5, 0x3f, 0x44, 0x77, 0x64, 0x9d, 0x98, 0xa5, 0x37,
0x2e, 0x0c, 0x95, 0x26, 0x97, 0x48, 0x6a, 0x01, 0x5a, 0x03, 0x8a, 0x82, 0x9f, 0x95, 0xf6, 0x9d,
0xe6, 0x2c, 0xae, 0xe6, 0x72, 0x71, 0x66, 0x75, 0x77, 0xcd, 0xf0, 0x6b, 0x2c, 0x2e, 0xc3, 0xfe,
0x98, 0x92, 0x0c, 0x4e, 0x8b, 0x21, 0xad, 0xde, 0x70, 0x8a, 0xfb, 0xb8, 0x55, 0x5f, 0x7e, 0xd5,
0xd1, 0xe2, 0xf4, 0x46, 0xd0, 0x97, 0xa2, 0x27, 0xe9, 0xff, 0xda, 0x74, 0xff, 0xdf, 0x4d, 0x9b,
0xd2, 0x0f, 0xed, 0xdf, 0xac, 0x33, 0xad, 0xb0, 0xae, 0xcd, 0xdb, 0x8d, 0xb7, 0x77, 0xd2, 0x71,
0x8d, 0x44, 0x7e, 0xaf, 0x75, 0x63, 0x5c, 0x61, 0xfb, 0xd6, 0x3d, 0xb6, 0x4c, 0xfc, 0x20, 0x58,
0x15, 0xb4, 0x19, 0x9c, 0xe7, 0x42, 0xbe, 0x00, 0x6c, 0x50, 0x35, 0x76, 0xc3, 0x60, 0x0c, 0x56,
0xe6, 0x32, 0x33, 0xe3, 0x16, 0xbe, 0x40, 0x99, 0x96, 0x72, 0x3b, 0xe9, 0xc9, 0xb1, 0x5e, 0x6b,
0x94, 0x7f, 0x08, 0x1c, 0x3f, 0xcc, 0x9e, 0xc4, 0xfa, 0x00, 0xbe, 0xa9, 0xc6, 0xe6, 0x9f, 0x34,
0xe3, 0x7b, 0x11, 0x5f, 0xde, 0xf2, 0xf9, 0x42, 0x1e, 0x2b, 0x12, 0x25, 0x14, 0x7b, 0x12, 0xa3,
0x26, 0xd7, 0x13, 0x74, 0x42, 0x86, 0xd3, 0xaa, 0x71, 0x60, 0x35, 0xf0, 0x8c, 0xc1, 0x48, 0x5c,
0x8e, 0x60, 0x35, 0x21, 0x6d, 0x3d, 0xdb, 0xde, 0xe2, 0x46, 0x6b, 0xdd, 0x65, 0x8d, 0x9a, 0x13,
0x26, 0xdb, 0x4a, 0xc4, 0x31, 0xac, 0x6b, 0xad, 0x24, 0x52, 0xd6, 0xc2, 0xa5, 0x48, 0x84, 0xed,
0xc6, 0xd4, 0x64, 0xb5, 0x7f, 0x01, 0xbb, 0x13, 0x93, 0xb5, 0xe1, 0x0d, 0x1c, 0x4c, 0xcc, 0x32,
0x0f, 0xbf, 0x85, 0x5c, 0xa1, 0x8c, 0x0e, 0xb9, 0x7f, 0x9c, 0xc6, 0x16, 0x61, 0x7e, 0x40, 0x05,
0x32, 0xad, 0xde, 0x87, 0xa6, 0x4a, 0xd0, 0x2a, 0x5e, 0x0a, 0xa9, 0xe2, 0x0e, 0xda, 0xe2, 0x16,
0x06, 0x5c, 0x16, 0x47, 0xb0, 0xa1, 0xde, 0xda, 0xa9, 0x47, 0xf9, 0x7c, 0x3e, 0x87, 0x83, 0x05,
0xf8, 0xf6, 0xa4, 0xf7, 0xf2, 0x25, 0xdd, 0xae, 0xbd, 0xf6, 0xc7, 0xb9, 0x11, 0xbb, 0xe6, 0x06,
0xc7, 0x6b, 0x18, 0x8a, 0x26, 0x14, 0x4b, 0x18, 0x0b, 0x72, 0xa3, 0xd5, 0x8b, 0x29, 0x44, 0xc3,
0xb1, 0x11, 0x6d, 0x60, 0x7e, 0x04, 0x7c, 0xf6, 0xd6, 0x8b, 0x83, 0xd4, 0xff, 0x4b, 0x23, 0xaf,
0xe1, 0xc7, 0x23, 0xc1, 0xeb, 0x62, 0xea, 0xe2, 0x0e, 0x26, 0x06, 0x36, 0x9b, 0x03, 0xb9, 0x46,
0xa2, 0x3c, 0x33, 0x2d, 0x93, 0x81, 0x03, 0x49, 0x23, 0x00, 0x6d, 0x8f, 0x05, 0x72, 0x03, 0x31,
0x0c, 0x45, 0x55, 0x0e, 0x94, 0x99, 0x7b, 0x86, 0xe2, 0x05, 0x3a, 0xd4, 0x81, 0x32, 0x0d, 0x6a,
0x16, 0xb4, 0xad, 0x97, 0xe4, 0x48, 0x0e, 0xdc, 0x3e, 0xcc, 0xc9, 0x80, 0xf9, 0xff, 0xf7, 0x0c,
0xbf, 0xc6, 0x63, 0x7c, 0xbc, 0x45, 0x21, 0x4b, 0x9e, 0x23, 0x81, 0x22, 0x73, 0x86, 0x4a, 0x52,
0x23, 0x81, 0xd5, 0x07, 0x0c, 0x38, 0xb3, 0x5e, 0x6e, 0x38, 0x47, 0xf1, 0x42, 0xc3, 0xe8, 0xa7,
0x84, 0x50, 0x48, 0x8c, 0x2a, 0x66, 0x1c, 0x52, 0x06, 0x85, 0x41, 0x12, 0x61, 0x25, 0x97, 0xe8,
0xfe, 0x66, 0x5e, 0x04, 0xce, 0xaa, 0xea, 0x43, 0x79, 0x2c, 0x56, 0x1e, 0x02, 0xf7, 0xd0, 0x17,
0x13, 0xfe, 0x11, 0x8a, 0xde, 0xdc, 0xde, 0xc0, 0xfa, 0x18, 0x7f, 0xe5, 0xe3, 0x76, 0x7e, 0x1b,
0x5a, 0x85, 0xe3, 0x4e, 0xe5, 0x5c, 0x13, 0xd8, 0x1e, 0x54, 0x92, 0xda, 0x9c, 0xeb, 0xb0, 0xc3,
0x7e, 0xaf, 0x11, 0xb3, 0x8a, 0x73, 0x13, 0x5f, 0xdc, 0xc3, 0xc9, 0xbb, 0x82, 0x52, 0xae, 0x2c,
0x50, 0xf8, 0x79, 0x7a, 0x99, 0xcb, 0xbe, 0xea, 0xb0, 0xcf, 0xa3, 0x6a, 0x9a, 0xc2, 0xb9, 0x92,
0x18, 0xaf, 0x3d, 0x77, 0x4c, 0xce, 0xeb, 0x26, 0x32, 0xb0, 0x3e, 0xe6, 0xb3, 0x6e, 0x3d, 0x71,
0xc6, 0x75, 0xe2, 0xd4, 0x68, 0xf7, 0x84, 0x24, 0xb0, 0x3e, 0x74, 0xdc, 0x9f, 0x72, 0x3c, 0x98,
0xf4, 0xd8, 0x18, 0x73, 0x3c, 0x9a, 0x72, 0x5c, 0xef, 0x3b, 0xae, 0x3b, 0x92, 0xcc, 0xe4, 0x5e,
0x8a, 0xc1, 0xbf, 0xe7, 0xa0, 0x05,
};
#endif
for (unsigned i = 0; i < sizeof(IDS) / sizeof(IDS[0]); i++) {
if (IDS[i] != target_id) continue;
unsigned group = 0;
while (i >= FIRSTS[group + 1]) group++;
uint8_t raw[512];
unsigned int produced = sizeof raw;
#if defined(OTA_RAK15002_SD_STORE) && defined(COMPANION_RADIO_FULL)
// Rare display lookup: larger chunks save flash without growing the loop stack.
uint8_t* raw = (uint8_t*)malloc(DECODE_CHUNK_BYTES);
if (!raw) return nullptr;
struct Scratch { uint8_t* bytes; ~Scratch() { free(bytes); } } scratch = {raw};
#else
uint8_t raw[DECODE_CHUNK_BYTES];
#endif
unsigned int produced = DECODE_CHUNK_BYTES;
if (tinf_uncompress_exact(raw, &produced, NAMES + OFFSETS[group],
OFFSETS[group + 1] - OFFSETS[group]) != TINF_OK ||
produced != RAW_LENGTHS[group]) return nullptr;
@@ -563,6 +784,7 @@ inline const char* ota_target_env_name(uint32_t target_id) {
{ 0xb62a1e75, "heltec_tracker_v2_kiss_modem" },
{ 0x9e64845d, "heltec_tracker_v2_repeater" },
{ 0x19232e8b, "heltec_tracker_v2_repeater_bridge_espnow" },
{ 0x10f41878, "heltec_tracker_v2_repeater_bridge_rs232" },
{ 0xfaf164f4, "heltec_tracker_v2_repeater_observer_mqtt" },
{ 0x38b948b4, "heltec_tracker_v2_room_server" },
{ 0x462ddb9b, "heltec_tracker_v2_room_server_observer_mqtt" },
@@ -605,6 +827,7 @@ inline const char* ota_target_env_name(uint32_t target_id) {
{ 0xb016979a, "heltec_v4_3_tft_companion_radio_usb_femoff" },
{ 0xb49b6b55, "heltec_v4_3_tft_companion_radio_wifi_femoff" },
{ 0x01d8f124, "heltec_v4_companion_radio_ble" },
{ 0x64140fd9, "heltec_v4_companion_radio_ble_femoff" },
{ 0x86fb85f0, "heltec_v4_companion_radio_ble_femon" },
{ 0xd33b0986, "heltec_v4_companion_radio_ble_ps_femon" },
{ 0x1a4d0096, "heltec_v4_companion_radio_usb" },
@@ -848,6 +1071,9 @@ inline const char* ota_target_env_name(uint32_t target_id) {
{ 0x49d11eca, "MKE_s3_room_server" },
{ 0xd41f0638, "MKE_s3_sensor" },
{ 0xe3105eb8, "MKE_s3_terminal_chat" },
{ 0xba76cc33, "morse_bench_rak4631_companion" },
{ 0x944263f8, "morse_bench_t096_companion" },
{ 0xc7a8c754, "morse_production_rak4631_companion" },
{ 0xf8259880, "Nano_G2_Ultra_repeater" },
{ 0x063f1f00, "nibble_screen_connect_companion_radio_ble_" },
{ 0x70d71f92, "nibble_screen_connect_companion_radio_full_" },
@@ -908,8 +1134,12 @@ inline const char* ota_target_env_name(uint32_t target_id) {
{ 0x41e33a97, "RAK_4631_repeater_bridge_rs232_serial2_lora_ota_no_external_sensors" },
{ 0x29a0da19, "RAK_4631_repeater_lora_ota_no_external_sensors" },
{ 0x29bc38dc, "RAK_4631_repeater_rak15001_slot_c_lora_ota" },
{ 0xd5fc71d4, "RAK_4631_repeater_rak15002_sd_seeder_only" },
{ 0x05f5ffae, "RAK_4631_repeater_unified_lora_ota" },
{ 0xc62da371, "RAK_4631_repeater_w25q16_lora_ota" },
{ 0x96fc4c26, "RAK_4631_room_server_ethernet_lora_ota_no_external_sensors" },
{ 0x8a6dc63b, "RAK_4631_room_server_lora_ota_no_external_sensors" },
{ 0xb48950bb, "RAK_4631_sensor_lora_ota_no_external_sensors" },
{ 0xf1d3c5a8, "RAK_4631_terminal_chat" },
{ 0xc6d55752, "RAK_WisMesh_Tag_companion_radio_ble" },
{ 0x60683191, "RAK_WisMesh_Tag_companion_radio_usb" },
@@ -951,10 +1181,12 @@ inline const char* ota_target_env_name(uint32_t target_id) {
{ 0xd62ed92e, "Station_G3_ESP32_r2_repeater_observer_mqtt" },
{ 0x7abaf0d5, "Station_G3_ESP32_r2_room_server" },
{ 0x87edc08b, "Station_G3_ESP32_r2_room_server_observer_mqtt" },
{ 0xc50b0e48, "Station_G3_ESP32_r2_sensor" },
{ 0x3c49caf8, "Station_G3_ESP32_repeater" },
{ 0x58689ef9, "Station_G3_ESP32_repeater_observer_mqtt" },
{ 0x91878dd8, "Station_G3_ESP32_room_server" },
{ 0x72a93c45, "Station_G3_ESP32_room_server_observer_mqtt" },
{ 0x347e087a, "Station_G3_ESP32_sensor" },
{ 0x4dbf035e, "t1000e_companion_radio_ble" },
{ 0xff4102d6, "t1000e_companion_radio_usb" },
{ 0x2f9a9185, "t1000e_kiss_modem" },
@@ -1044,8 +1276,21 @@ inline const char* ota_target_env_name(uint32_t target_id) {
{ 0x7190df40, "ThinkNode_M9_kiss_modem" },
{ 0x609d50b3, "ThinkNode_M9_repeater_" },
{ 0x5718c7ea, "ThinkNode_M9_room_server_" },
{ 0x7bbf1081, "Waveshare_S3_LR1121_companion_radio_ble" },
{ 0xb151c2aa, "Waveshare_S3_LR1121_companion_radio_usb" },
{ 0x3b0174fd, "Waveshare_S3_LR1121_kiss_modem" },
{ 0xc1fd2578, "Waveshare_S3_LR1121_partition_expander_repeater_observer_mqtt" },
{ 0xeb440352, "Waveshare_S3_LR1121_partition_expander_room_server_observer_mqtt" },
{ 0xa297f85a, "Waveshare_S3_LR1121_repeater" },
{ 0xcbe54778, "Waveshare_S3_LR1121_repeater_observer_mqtt" },
{ 0x0343bdf0, "Waveshare_S3_LR1121_room_server" },
{ 0x3de413b1, "Waveshare_S3_LR1121_room_server_observer_mqtt" },
{ 0x37443a5b, "WHY2025_badge_companion_radio_ble_" },
{ 0x603384a7, "WHY2025_badge_repeater_" },
{ 0x76456c88, "Wio_Tracker_L2_companion_radio_ble" },
{ 0x28c4948b, "Wio_Tracker_L2_companion_radio_usb" },
{ 0xb34a83c5, "Wio_Tracker_L2_repeater" },
{ 0x63fe9591, "Wio_Tracker_L2_room_server" },
{ 0xce3ef40f, "wio_wm1110_repeater" },
{ 0x475a2a6a, "WioTrackerL1_companion_radio_ble" },
{ 0xf4175d2c, "WioTrackerL1_companion_radio_usb" },
@@ -1383,7 +1383,7 @@ uint8_t EnvironmentSensorManager::detectWisBlockStorageHolds() {
|| (w25_id != 0 && w25_id != 0xFFFFFFUL)) {
holds |= mesh::gps::ExternalStorage;
}
#elif defined(OTA_QSPI_SHARED_WISBLOCK_SPI) || (defined(OTA_SD_STORE) && !defined(OTA_RAK15002_SD_CACHE))
#elif defined(OTA_QSPI_SHARED_WISBLOCK_SPI) || (defined(OTA_SD_STORE) && !defined(OTA_RAK15002_SD_CACHE) && !defined(OTA_RAK15002_SD_STORE))
holds |= mesh::gps::ExternalStorage;
#endif
// Exact RAK15002 uses connector pin 5 (unswitched 3V3), not 3V3_S. Its