SDK for TESAIoT Dev Kit
API reference & tutorials (ModusToolbox)
Loading...
Searching...
No Matches

Functions

void lfs_wifi_creds_init (void)
 On this variant a no-op — bento_storage_init() owns bring-up.
void lfs_wifi_creds_deinit (void)
 On this variant a no-op; the mtb-mpy teardown ordering does not apply here.
bool lfs_wifi_creds_ready (void)
 Forwards to bento_storage_ready(); checked before every read and write.
int lfs_wifi_creds_read (qspi_wifi_entry_t *entries, int max_entries)
 Fill the caller's array; returns the entry count, 0 on any validation failure.
bool lfs_wifi_creds_write (const qspi_wifi_entry_t *entries, int count)
 Overwrite /.wifi_creds with CRC32; snapshot under the lock, write outside it.
bool lfs_wifi_creds_needs_resave (void)
 Always false on this variant — the C store writes only the current format.

Detailed Description

Six functions over one file, /.wifi_creds on the LittleFS partition: up to QSPI_WIFI_CREDS_MAX (6) entries of qspi_wifi_entry_t (100 bytes, packed: SSID, password, security, flags; declared in wifi_creds_types.h, prototypes in lfs_wifi_creds.h). On the mtb-only variant they are provided by shipped source — template/bento_libs/claw/common/storage_c/lfs_wifi_creds_c.c — which proj_cm33_ns/wifi_init.c and the WiFi worker in sensor_auto_task.c link against. Every exemplar in this family is quoted from those two files, both of which are in the mtb-only zip.

Variant
mtb-mpy and mtb-only

Function Documentation

◆ lfs_wifi_creds_init()

void lfs_wifi_creds_init ( void )

On this variant a no-op — bento_storage_init() owns bring-up.

Initialize LFS credential store. MUST be called from MicroPython task AFTER VFS mount succeeds.

Contract
On this variant a no-op: bento_storage_init() owns bring-up, and the store is usable as soon as lfs_wifi_creds_ready() reports the volume mounted. Keep the call where the shared boot code places it so the same source compiles on mtb-mpy, where it is real bring-up that must run from the MicroPython task after the VFS mount with gc_collect() immediately before it. Store init must precede every ready / read / write. Void.
Variant
mtb-mpy and mtb-only
Example
The shipped definition (first line of the store).
void lfs_wifi_creds_init(void) { /* bento_storage_init() owns bring-up */ }
void lfs_wifi_creds_deinit(void) { }
bool lfs_wifi_creds_ready(void) { return bento_storage_ready(); }
/* The mtb-mpy store can inherit a pre-LFS sector image that wants rewriting;
* this store writes only the current format, so never. */
bool lfs_wifi_creds_needs_resave(void) { return false; }
int lfs_wifi_creds_read(qspi_wifi_entry_t *entries, int max_entries) {
if (entries == NULL || max_entries <= 0) return 0;
if (!bento_storage_ready()) return 0;
int n = bento_storage_read_file(CREDS_FILE, s_buf, sizeof(s_buf));
if (n < CREDS_TOTAL_SIZE) return 0;
uint32_t magic; memcpy(&magic, &s_buf[0], 4);
uint16_t version; memcpy(&version, &s_buf[4], 2);
uint16_t count; memcpy(&count, &s_buf[6], 2);
if (magic != QSPI_WIFI_CREDS_MAGIC) return 0;
if (version != QSPI_WIFI_CREDS_VERSION) return 0;
if (count > QSPI_WIFI_CREDS_MAX) return 0;
uint32_t stored; memcpy(&stored, &s_buf[CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE], 4);
if (stored != calc_crc32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE)) {
/* Legacy XOR-32 fallback, exactly as the mtb-mpy reader does. */
if (stored != calc_xor32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE)) return 0;
}
int out = (count < (uint16_t)max_entries) ? count : max_entries;
memcpy(entries, &s_buf[CREDS_HEADER_SIZE], (size_t)out * sizeof(qspi_wifi_entry_t));
/* Defensive termination, same as the mtb-mpy reader. */
for (int i = 0; i < out; i++) {
entries[i].ssid[32] = '\0';
entries[i].password[64] = '\0';
}
return out;
}
bool lfs_wifi_creds_write(const qspi_wifi_entry_t *entries, int count) {
if (count < 0 || count > QSPI_WIFI_CREDS_MAX) return false;
if (count > 0 && entries == NULL) return false;
if (!bento_storage_ready()) return false;
memset(s_buf, 0, sizeof(s_buf));
uint32_t magic = QSPI_WIFI_CREDS_MAGIC;
uint16_t version = QSPI_WIFI_CREDS_VERSION;
uint16_t cnt = (uint16_t)count;
memcpy(&s_buf[0], &magic, 4);
memcpy(&s_buf[4], &version, 2);
memcpy(&s_buf[6], &cnt, 2);
if (count > 0) {
memcpy(&s_buf[CREDS_HEADER_SIZE], entries,
(size_t)count * sizeof(qspi_wifi_entry_t));
}
uint32_t crc = calc_crc32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE);
memcpy(&s_buf[CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE], &crc, 4);
return bento_storage_write_file(CREDS_FILE, s_buf, sizeof(s_buf));
}

◆ lfs_wifi_creds_deinit()

void lfs_wifi_creds_deinit ( void )

On this variant a no-op; the mtb-mpy teardown ordering does not apply here.

Invalidate LFS handle before MicroPython soft reset. Called from mpy_main.c before mp_deinit().

Contract
On this variant a no-op. On mtb-mpy it invalidates the store's Python handles and its ordering is fixed — after a final wifi_creds_flush_if_dirty() and before gc_sweep_all() destroys the VFS objects; there is no equivalent teardown for the C store because bento_storage stays mounted for the life of the firmware. Void.
Variant
mtb-mpy and mtb-only
Example
The shipped definition.
void lfs_wifi_creds_init(void) { /* bento_storage_init() owns bring-up */ }
void lfs_wifi_creds_deinit(void) { }
bool lfs_wifi_creds_ready(void) { return bento_storage_ready(); }
/* The mtb-mpy store can inherit a pre-LFS sector image that wants rewriting;
* this store writes only the current format, so never. */
bool lfs_wifi_creds_needs_resave(void) { return false; }
int lfs_wifi_creds_read(qspi_wifi_entry_t *entries, int max_entries) {
if (entries == NULL || max_entries <= 0) return 0;
if (!bento_storage_ready()) return 0;
int n = bento_storage_read_file(CREDS_FILE, s_buf, sizeof(s_buf));
if (n < CREDS_TOTAL_SIZE) return 0;
uint32_t magic; memcpy(&magic, &s_buf[0], 4);
uint16_t version; memcpy(&version, &s_buf[4], 2);
uint16_t count; memcpy(&count, &s_buf[6], 2);
if (magic != QSPI_WIFI_CREDS_MAGIC) return 0;
if (version != QSPI_WIFI_CREDS_VERSION) return 0;
if (count > QSPI_WIFI_CREDS_MAX) return 0;
uint32_t stored; memcpy(&stored, &s_buf[CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE], 4);
if (stored != calc_crc32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE)) {
/* Legacy XOR-32 fallback, exactly as the mtb-mpy reader does. */
if (stored != calc_xor32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE)) return 0;
}
int out = (count < (uint16_t)max_entries) ? count : max_entries;
memcpy(entries, &s_buf[CREDS_HEADER_SIZE], (size_t)out * sizeof(qspi_wifi_entry_t));
/* Defensive termination, same as the mtb-mpy reader. */
for (int i = 0; i < out; i++) {
entries[i].ssid[32] = '\0';
entries[i].password[64] = '\0';
}
return out;
}
bool lfs_wifi_creds_write(const qspi_wifi_entry_t *entries, int count) {
if (count < 0 || count > QSPI_WIFI_CREDS_MAX) return false;
if (count > 0 && entries == NULL) return false;
if (!bento_storage_ready()) return false;
memset(s_buf, 0, sizeof(s_buf));
uint32_t magic = QSPI_WIFI_CREDS_MAGIC;
uint16_t version = QSPI_WIFI_CREDS_VERSION;
uint16_t cnt = (uint16_t)count;
memcpy(&s_buf[0], &magic, 4);
memcpy(&s_buf[4], &version, 2);
memcpy(&s_buf[6], &cnt, 2);
if (count > 0) {
memcpy(&s_buf[CREDS_HEADER_SIZE], entries,
(size_t)count * sizeof(qspi_wifi_entry_t));
}
uint32_t crc = calc_crc32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE);
memcpy(&s_buf[CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE], &crc, 4);
return bento_storage_write_file(CREDS_FILE, s_buf, sizeof(s_buf));
}

◆ lfs_wifi_creds_ready()

bool lfs_wifi_creds_ready ( void )

Forwards to bento_storage_ready(); checked before every read and write.

Check if LFS credential store is ready for use.

Contract
Forwards to bento_storage_ready(). Checked before every read and every write at all shipped sites, without exception — read returns 0 and write returns false when not ready, but the guard keeps "store not ready" distinguishable from "no data". Store init must precede it. Cheap; no locking.
Variant
mtb-mpy and mtb-only
Example
The shipped definition, and its use inside read and write.
void lfs_wifi_creds_init(void) { /* bento_storage_init() owns bring-up */ }
void lfs_wifi_creds_deinit(void) { }
bool lfs_wifi_creds_ready(void) { return bento_storage_ready(); }
/* The mtb-mpy store can inherit a pre-LFS sector image that wants rewriting;
* this store writes only the current format, so never. */
bool lfs_wifi_creds_needs_resave(void) { return false; }
int lfs_wifi_creds_read(qspi_wifi_entry_t *entries, int max_entries) {
if (entries == NULL || max_entries <= 0) return 0;
if (!bento_storage_ready()) return 0;
int n = bento_storage_read_file(CREDS_FILE, s_buf, sizeof(s_buf));
if (n < CREDS_TOTAL_SIZE) return 0;
uint32_t magic; memcpy(&magic, &s_buf[0], 4);
uint16_t version; memcpy(&version, &s_buf[4], 2);
uint16_t count; memcpy(&count, &s_buf[6], 2);
if (magic != QSPI_WIFI_CREDS_MAGIC) return 0;
if (version != QSPI_WIFI_CREDS_VERSION) return 0;
if (count > QSPI_WIFI_CREDS_MAX) return 0;
uint32_t stored; memcpy(&stored, &s_buf[CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE], 4);
if (stored != calc_crc32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE)) {
/* Legacy XOR-32 fallback, exactly as the mtb-mpy reader does. */
if (stored != calc_xor32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE)) return 0;
}
int out = (count < (uint16_t)max_entries) ? count : max_entries;
memcpy(entries, &s_buf[CREDS_HEADER_SIZE], (size_t)out * sizeof(qspi_wifi_entry_t));
/* Defensive termination, same as the mtb-mpy reader. */
for (int i = 0; i < out; i++) {
entries[i].ssid[32] = '\0';
entries[i].password[64] = '\0';
}
return out;
}
bool lfs_wifi_creds_write(const qspi_wifi_entry_t *entries, int count) {
if (count < 0 || count > QSPI_WIFI_CREDS_MAX) return false;
if (count > 0 && entries == NULL) return false;
if (!bento_storage_ready()) return false;
memset(s_buf, 0, sizeof(s_buf));
uint32_t magic = QSPI_WIFI_CREDS_MAGIC;
uint16_t version = QSPI_WIFI_CREDS_VERSION;
uint16_t cnt = (uint16_t)count;
memcpy(&s_buf[0], &magic, 4);
memcpy(&s_buf[4], &version, 2);
memcpy(&s_buf[6], &cnt, 2);
if (count > 0) {
memcpy(&s_buf[CREDS_HEADER_SIZE], entries,
(size_t)count * sizeof(qspi_wifi_entry_t));
}
uint32_t crc = calc_crc32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE);
memcpy(&s_buf[CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE], &crc, 4);
return bento_storage_write_file(CREDS_FILE, s_buf, sizeof(s_buf));
}

◆ lfs_wifi_creds_read()

int lfs_wifi_creds_read ( qspi_wifi_entry_t * entries,
int max_entries )

Fill the caller's array; returns the entry count, 0 on any validation failure.

Read all saved WiFi credentials from LittleFS.

Waits up to 10 seconds for LFS init (VFS mount). Validates magic, version, checksum before returning data.

Parameters
entriesOutput array (must hold QSPI_WIFI_CREDS_MAX elements)
max_entriesMax entries to read
Returns
Number of valid entries read (0 if LFS not ready or no data)
Contract
Fills the caller's array (sized QSPI_WIFI_CREDS_MAX) and returns the entry count; 0 on any magic, version or checksum failure — no separate error code, and 0 also means an empty file. Validates magic, version and count, then CRC32 with the legacy XOR-32 fallback, then NUL-terminates every SSID and password defensively, exactly as the mtb-mpy reader does. When the target is the shared g_boot_wifi_creds array the call goes under wifi_creds_lock() / wifi_creds_unlock(); the boot read is single-claimer but takes the lock anyway for uniformity (wifi_creds_lock is a no-op before the scheduler starts). Reads through bento_storage_read_file(); any task.
Variant
mtb-mpy and mtb-only
Example
The shipped reader.
void lfs_wifi_creds_init(void) { /* bento_storage_init() owns bring-up */ }
void lfs_wifi_creds_deinit(void) { }
bool lfs_wifi_creds_ready(void) { return bento_storage_ready(); }
/* The mtb-mpy store can inherit a pre-LFS sector image that wants rewriting;
* this store writes only the current format, so never. */
bool lfs_wifi_creds_needs_resave(void) { return false; }
int lfs_wifi_creds_read(qspi_wifi_entry_t *entries, int max_entries) {
if (entries == NULL || max_entries <= 0) return 0;
if (!bento_storage_ready()) return 0;
int n = bento_storage_read_file(CREDS_FILE, s_buf, sizeof(s_buf));
if (n < CREDS_TOTAL_SIZE) return 0;
uint32_t magic; memcpy(&magic, &s_buf[0], 4);
uint16_t version; memcpy(&version, &s_buf[4], 2);
uint16_t count; memcpy(&count, &s_buf[6], 2);
if (magic != QSPI_WIFI_CREDS_MAGIC) return 0;
if (version != QSPI_WIFI_CREDS_VERSION) return 0;
if (count > QSPI_WIFI_CREDS_MAX) return 0;
uint32_t stored; memcpy(&stored, &s_buf[CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE], 4);
if (stored != calc_crc32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE)) {
/* Legacy XOR-32 fallback, exactly as the mtb-mpy reader does. */
if (stored != calc_xor32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE)) return 0;
}
int out = (count < (uint16_t)max_entries) ? count : max_entries;
memcpy(entries, &s_buf[CREDS_HEADER_SIZE], (size_t)out * sizeof(qspi_wifi_entry_t));
/* Defensive termination, same as the mtb-mpy reader. */
for (int i = 0; i < out; i++) {
entries[i].ssid[32] = '\0';
entries[i].password[64] = '\0';
}
return out;
}
bool lfs_wifi_creds_write(const qspi_wifi_entry_t *entries, int count) {
if (count < 0 || count > QSPI_WIFI_CREDS_MAX) return false;
if (count > 0 && entries == NULL) return false;
if (!bento_storage_ready()) return false;
memset(s_buf, 0, sizeof(s_buf));
uint32_t magic = QSPI_WIFI_CREDS_MAGIC;
uint16_t version = QSPI_WIFI_CREDS_VERSION;
uint16_t cnt = (uint16_t)count;
memcpy(&s_buf[0], &magic, 4);
memcpy(&s_buf[4], &version, 2);
memcpy(&s_buf[6], &cnt, 2);
if (count > 0) {
memcpy(&s_buf[CREDS_HEADER_SIZE], entries,
(size_t)count * sizeof(qspi_wifi_entry_t));
}
uint32_t crc = calc_crc32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE);
memcpy(&s_buf[CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE], &crc, 4);
return bento_storage_write_file(CREDS_FILE, s_buf, sizeof(s_buf));
}

◆ lfs_wifi_creds_write()

bool lfs_wifi_creds_write ( const qspi_wifi_entry_t * entries,
int count )

Overwrite /.wifi_creds with CRC32; snapshot under the lock, write outside it.

Write WiFi credentials to LittleFS.

Creates/overwrites /.wifi_creds with magic + version + entries + checksum.

Parameters
entriesArray of entries to write
countNumber of entries (1..QSPI_WIFI_CREDS_MAX)
Returns
true on success
Contract
Overwrites /.wifi_creds with magic, version, count entries and a CRC32 — always CRC32, never the legacy checksum. Returns bool; on failure the dirty flag is deliberately left set so a later pass retries — never clear g_boot_wifi_creds_dirty around a failed write. The cross-task path on this variant is the opposite of mtb-mpy's: snapshot the globals under the lock, then write outside it, because the flash write takes milliseconds and the BLE worker shares the globals. It runs immediately in the WiFi worker task (compiled only when BENTO_HAS_MPY is 0) — with no VM there is no mpy_main.c and therefore no deferred REPL-idle flusher; the "requires MicroPython task context" wording in lfs_wifi_creds.h is about the Python-VFS store, not this one. Writes through bento_storage_write_file().
Variant
mtb-mpy and mtb-only
Example
The immediate save in the WiFi worker (sensor_auto_task.c): snapshot under the lock, write outside it, dirty flag cleared only on success.
/* ...context: inside the IPC_CMD_WIFI_CONNECT success path ... */
#if !BENTO_HAS_MPY
/* No VM means no mpy_main.c and therefore no deferred flusher — the
* "requires MicroPython task context" above is about the Python-VFS
* store, not this one. This runs in the WiFi worker task, and the C
* store is plain lfs2 over serial memory, so write it out now.
* Snapshot under the lock, write outside it: the flash write takes
* milliseconds and the BLE worker shares these globals. */
{
qspi_wifi_entry_t snap[QSPI_WIFI_CREDS_MAX];
int n;
wifi_creds_lock();
n = (int)g_boot_wifi_creds_count;
if (n > QSPI_WIFI_CREDS_MAX) n = QSPI_WIFI_CREDS_MAX;
memcpy(snap, g_boot_wifi_creds, (size_t)n * sizeof(qspi_wifi_entry_t));
wifi_creds_unlock();
extern bool lfs_wifi_creds_write(const qspi_wifi_entry_t *entries, int count);
if (lfs_wifi_creds_write(snap, n)) {
wifi_creds_lock();
g_boot_wifi_creds_dirty = false;
wifi_creds_unlock();
printf("[WiFiIPC] Credentials persisted (%d entries)\r\n", n);
} else {
printf("[WiFiIPC] ERROR: credential save failed — kept dirty\r\n");
}
}
#endif

The shipped writer itself:

void lfs_wifi_creds_init(void) { /* bento_storage_init() owns bring-up */ }
void lfs_wifi_creds_deinit(void) { }
bool lfs_wifi_creds_ready(void) { return bento_storage_ready(); }
/* The mtb-mpy store can inherit a pre-LFS sector image that wants rewriting;
* this store writes only the current format, so never. */
bool lfs_wifi_creds_needs_resave(void) { return false; }
int lfs_wifi_creds_read(qspi_wifi_entry_t *entries, int max_entries) {
if (entries == NULL || max_entries <= 0) return 0;
if (!bento_storage_ready()) return 0;
int n = bento_storage_read_file(CREDS_FILE, s_buf, sizeof(s_buf));
if (n < CREDS_TOTAL_SIZE) return 0;
uint32_t magic; memcpy(&magic, &s_buf[0], 4);
uint16_t version; memcpy(&version, &s_buf[4], 2);
uint16_t count; memcpy(&count, &s_buf[6], 2);
if (magic != QSPI_WIFI_CREDS_MAGIC) return 0;
if (version != QSPI_WIFI_CREDS_VERSION) return 0;
if (count > QSPI_WIFI_CREDS_MAX) return 0;
uint32_t stored; memcpy(&stored, &s_buf[CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE], 4);
if (stored != calc_crc32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE)) {
/* Legacy XOR-32 fallback, exactly as the mtb-mpy reader does. */
if (stored != calc_xor32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE)) return 0;
}
int out = (count < (uint16_t)max_entries) ? count : max_entries;
memcpy(entries, &s_buf[CREDS_HEADER_SIZE], (size_t)out * sizeof(qspi_wifi_entry_t));
/* Defensive termination, same as the mtb-mpy reader. */
for (int i = 0; i < out; i++) {
entries[i].ssid[32] = '\0';
entries[i].password[64] = '\0';
}
return out;
}
bool lfs_wifi_creds_write(const qspi_wifi_entry_t *entries, int count) {
if (count < 0 || count > QSPI_WIFI_CREDS_MAX) return false;
if (count > 0 && entries == NULL) return false;
if (!bento_storage_ready()) return false;
memset(s_buf, 0, sizeof(s_buf));
uint32_t magic = QSPI_WIFI_CREDS_MAGIC;
uint16_t version = QSPI_WIFI_CREDS_VERSION;
uint16_t cnt = (uint16_t)count;
memcpy(&s_buf[0], &magic, 4);
memcpy(&s_buf[4], &version, 2);
memcpy(&s_buf[6], &cnt, 2);
if (count > 0) {
memcpy(&s_buf[CREDS_HEADER_SIZE], entries,
(size_t)count * sizeof(qspi_wifi_entry_t));
}
uint32_t crc = calc_crc32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE);
memcpy(&s_buf[CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE], &crc, 4);
return bento_storage_write_file(CREDS_FILE, s_buf, sizeof(s_buf));
}

◆ lfs_wifi_creds_needs_resave()

bool lfs_wifi_creds_needs_resave ( void )

Always false on this variant — the C store writes only the current format.

Check if credentials need re-saving (XOR-32 → CRC32 migration). Call after boot WiFi auto-connect; if true, re-read and re-write.

Contract
On this variant always false: the C store writes only the current CRC32 format, so nothing it wrote can need migrating. Shared boot code still calls it after the read and re-writes when it returns true with a count greater than 0 — that branch is live on mtb-mpy, where a legacy XOR-32 file is re-saved as CRC32, and is simply never taken here. A legacy file read on this variant is still accepted (XOR-32 fallback) and becomes CRC32 at the next write.
Variant
mtb-mpy and mtb-only
Example
The shipped definition.
void lfs_wifi_creds_init(void) { /* bento_storage_init() owns bring-up */ }
void lfs_wifi_creds_deinit(void) { }
bool lfs_wifi_creds_ready(void) { return bento_storage_ready(); }
/* The mtb-mpy store can inherit a pre-LFS sector image that wants rewriting;
* this store writes only the current format, so never. */
bool lfs_wifi_creds_needs_resave(void) { return false; }
int lfs_wifi_creds_read(qspi_wifi_entry_t *entries, int max_entries) {
if (entries == NULL || max_entries <= 0) return 0;
if (!bento_storage_ready()) return 0;
int n = bento_storage_read_file(CREDS_FILE, s_buf, sizeof(s_buf));
if (n < CREDS_TOTAL_SIZE) return 0;
uint32_t magic; memcpy(&magic, &s_buf[0], 4);
uint16_t version; memcpy(&version, &s_buf[4], 2);
uint16_t count; memcpy(&count, &s_buf[6], 2);
if (magic != QSPI_WIFI_CREDS_MAGIC) return 0;
if (version != QSPI_WIFI_CREDS_VERSION) return 0;
if (count > QSPI_WIFI_CREDS_MAX) return 0;
uint32_t stored; memcpy(&stored, &s_buf[CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE], 4);
if (stored != calc_crc32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE)) {
/* Legacy XOR-32 fallback, exactly as the mtb-mpy reader does. */
if (stored != calc_xor32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE)) return 0;
}
int out = (count < (uint16_t)max_entries) ? count : max_entries;
memcpy(entries, &s_buf[CREDS_HEADER_SIZE], (size_t)out * sizeof(qspi_wifi_entry_t));
/* Defensive termination, same as the mtb-mpy reader. */
for (int i = 0; i < out; i++) {
entries[i].ssid[32] = '\0';
entries[i].password[64] = '\0';
}
return out;
}
bool lfs_wifi_creds_write(const qspi_wifi_entry_t *entries, int count) {
if (count < 0 || count > QSPI_WIFI_CREDS_MAX) return false;
if (count > 0 && entries == NULL) return false;
if (!bento_storage_ready()) return false;
memset(s_buf, 0, sizeof(s_buf));
uint32_t magic = QSPI_WIFI_CREDS_MAGIC;
uint16_t version = QSPI_WIFI_CREDS_VERSION;
uint16_t cnt = (uint16_t)count;
memcpy(&s_buf[0], &magic, 4);
memcpy(&s_buf[4], &version, 2);
memcpy(&s_buf[6], &cnt, 2);
if (count > 0) {
memcpy(&s_buf[CREDS_HEADER_SIZE], entries,
(size_t)count * sizeof(qspi_wifi_entry_t));
}
uint32_t crc = calc_crc32(s_buf, CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE);
memcpy(&s_buf[CREDS_HEADER_SIZE + CREDS_ENTRIES_SIZE], &crc, 4);
return bento_storage_write_file(CREDS_FILE, s_buf, sizeof(s_buf));
}