File syn_param.c¶
File List > src > syntropic > storage > syn_param.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_PARAM) || SYN_USE_PARAM
#include "../util/syn_assert.h"
#include "../util/syn_crc.h"
#include "syn_param.h"
#include <string.h>
/* ── Helpers ────────────────────────────────────────────────────────────── */
static uint16_t align16(uint16_t size)
{
return (uint16_t)((size + 15u) & ~15u);
}
static uint32_t slot_addr(const SYN_ParamStore *store, uint8_t sector, uint16_t slot)
{
return store->flash_base + (uint32_t)sector * store->sector_size +
(uint32_t)slot * store->slot_size;
}
static bool verify_slot_crc(const SYN_ParamStore *store, uint8_t sector, uint16_t slot,
uint16_t expected_crc)
{
uint32_t addr = slot_addr(store, sector, slot) + sizeof(SYN_ParamSlotHeader);
uint16_t crc = SYN_CRC16_CCITT_INIT;
uint16_t remaining = store->data_size;
uint8_t chunk[32];
while (remaining > 0) {
uint16_t len = remaining > sizeof(chunk) ? sizeof(chunk) : remaining;
if (syn_port_flash_read(addr, chunk, len) != SYN_OK)
return false;
crc = syn_crc16_ccitt_update(crc, chunk, len);
addr += len;
remaining -= len;
}
return crc == expected_crc;
}
static bool read_slot(const SYN_ParamStore *store, uint8_t sector, uint16_t slot,
SYN_ParamSlotHeader *hdr, void *data)
{
uint32_t addr = slot_addr(store, sector, slot);
if (syn_port_flash_read(addr, hdr, sizeof(*hdr)) != SYN_OK) {
return false;
}
if (hdr->magic != SYN_PARAM_MAGIC)
return false;
if (hdr->data_size != store->data_size)
return false;
/* Read data */
if (data != NULL) {
if (syn_port_flash_read(addr + sizeof(*hdr), data, store->data_size) != SYN_OK) {
return false;
}
}
/* Verify CRC */
if (data != NULL) {
uint16_t crc = syn_crc16_ccitt(data, store->data_size);
if (crc != hdr->crc)
return false;
} else {
if (!verify_slot_crc(store, sector, slot, hdr->crc))
return false;
}
return true;
}
/* ── API ────────────────────────────────────────────────────────────────── */
SYN_Status syn_param_init(SYN_ParamStore *store, uint32_t flash_base, uint8_t sector_count,
uint16_t data_size)
{
SYN_ASSERT(store != NULL);
SYN_ASSERT(sector_count > 0);
SYN_ASSERT(data_size > 0);
if (store == NULL || sector_count == 0 || data_size == 0) {
return SYN_INVALID_PARAM;
}
memset(store, 0, sizeof(*store));
store->flash_base = flash_base;
store->sector_count = sector_count;
store->data_size = data_size;
store->sector_size = syn_port_flash_sector_size(flash_base);
store->slot_size = align16((uint16_t)(sizeof(SYN_ParamSlotHeader) + data_size));
store->slots_per_sector = (uint16_t)(store->sector_size / store->slot_size);
if (store->slots_per_sector == 0) {
return SYN_ERROR; /* data too large for sector */
}
/* Scan all sectors and slots for the highest valid sequence number */
uint16_t best_seq = 0;
bool found = false;
SYN_ParamSlotHeader hdr;
for (uint8_t sec = 0; sec < sector_count; sec++) {
for (uint16_t sl = 0; sl < store->slots_per_sector; sl++) {
if (read_slot(store, sec, sl, &hdr, NULL)) {
/* Can't verify CRC without reading data, so re-read
* with data for the best candidate only. For the scan,
* we just check magic + data_size. */
if (!found || ((int16_t)(hdr.seq - best_seq) > 0)) {
best_seq = hdr.seq;
store->active_sector = sec;
store->active_slot = sl;
found = true;
}
}
}
}
if (found) {
store->next_seq = (uint16_t)(best_seq + 1);
store->initialized = true;
return SYN_OK;
}
/* No valid data found — flash is blank */
store->active_sector = 0;
store->active_slot = 0;
store->next_seq = 1;
store->initialized = true;
return SYN_ERROR;
}
SYN_Status syn_param_load(const SYN_ParamStore *store, void *data)
{
SYN_ASSERT(store != NULL);
SYN_ASSERT(store->initialized);
SYN_ASSERT(data != NULL);
if (store == NULL || !store->initialized || data == NULL) {
return SYN_INVALID_PARAM;
}
SYN_ParamSlotHeader hdr;
if (read_slot(store, store->active_sector, store->active_slot, &hdr, data)) {
/* Verify CRC with actual data */
uint16_t crc = syn_crc16_ccitt(data, store->data_size);
if (crc == hdr.crc) {
return SYN_OK;
}
}
return SYN_ERROR;
}
SYN_Status syn_param_save(SYN_ParamStore *store, const void *data)
{
SYN_ASSERT(store != NULL);
SYN_ASSERT(store->initialized);
SYN_ASSERT(data != NULL);
if (store == NULL || !store->initialized || data == NULL) {
return SYN_INVALID_PARAM;
}
/* Determine next write position */
uint8_t sec = store->active_sector;
uint16_t sl = store->active_slot;
/* If this isn't the first write, advance to next slot */
if (store->next_seq > 1) {
sl++;
if (sl >= store->slots_per_sector) {
/* Move to next sector */
sl = 0;
sec = (uint8_t)((sec + 1) % store->sector_count);
/* Erase the new sector */
SYN_Status err =
syn_port_flash_erase(store->flash_base + (uint32_t)sec * store->sector_size);
if (err != SYN_OK)
return err;
}
} else {
/* First write ever — erase the initial sector */
SYN_Status err =
syn_port_flash_erase(store->flash_base + (uint32_t)sec * store->sector_size);
if (err != SYN_OK)
return err;
}
/* Build header */
SYN_ParamSlotHeader hdr;
hdr.magic = SYN_PARAM_MAGIC;
hdr.seq = store->next_seq;
hdr.data_size = store->data_size;
hdr.crc = syn_crc16_ccitt(data, store->data_size);
/* Write header */
uint32_t addr = slot_addr(store, sec, sl);
SYN_Status err = syn_port_flash_write(addr, &hdr, sizeof(hdr));
if (err != SYN_OK)
return err;
/* Write data */
err = syn_port_flash_write(addr + sizeof(hdr), data, store->data_size);
if (err != SYN_OK)
return err;
/* Update state */
store->active_sector = sec;
store->active_slot = sl;
store->next_seq++;
return SYN_OK;
}
SYN_Status syn_param_erase_all(SYN_ParamStore *store)
{
SYN_ASSERT(store != NULL);
SYN_ASSERT(store->initialized);
if (store == NULL || !store->initialized) {
return SYN_INVALID_PARAM;
}
for (uint8_t sec = 0; sec < store->sector_count; sec++) {
SYN_Status err =
syn_port_flash_erase(store->flash_base + (uint32_t)sec * store->sector_size);
if (err != SYN_OK)
return err;
}
store->active_sector = 0;
store->active_slot = 0;
store->next_seq = 1;
return SYN_OK;
}
#endif /* SYN_USE_PARAM */