You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
363 lines
10 KiB
363 lines
10 KiB
//
|
|
// Created by MightyPork on 2017/11/26.
|
|
//
|
|
|
|
#include "platform.h"
|
|
#include "utils/hexdump.h"
|
|
#include "settings.h"
|
|
#include "unit_registry.h"
|
|
#include "system_settings.h"
|
|
#include "utils/str_utils.h"
|
|
#include "unit_base.h"
|
|
#include "utils/avrlibc.h"
|
|
|
|
// pre-declarations
|
|
static void savebuf_flush(PayloadBuilder *pb, bool final);
|
|
static bool savebuf_ovhandler(PayloadBuilder *pb, uint32_t more);
|
|
|
|
// This is the first entry in a valid config.
|
|
// Change with each breaking change to force config reset.
|
|
#define CONFIG_MARKER 0xA55E
|
|
|
|
void settings_load(void)
|
|
{
|
|
uint8_t *buffer = (uint8_t *) SETTINGS_FLASH_ADDR;
|
|
|
|
#if DEBUG_FLASH_WRITE
|
|
dbg("reading @ %p", (void*)SETTINGS_FLASH_ADDR);
|
|
hexDump("Flash", buffer, 64);
|
|
#endif
|
|
|
|
PayloadParser pp = pp_start(buffer, SETTINGS_BLOCK_SIZE, NULL);
|
|
|
|
// Check the integrity marker
|
|
if (pp_u16(&pp) != CONFIG_MARKER) {
|
|
dbg("Config not valid!");
|
|
// Save for next run
|
|
settings_save();
|
|
return;
|
|
}
|
|
|
|
uint8_t version = pp_u8(&pp); // top level settings format version
|
|
|
|
{ // Settings
|
|
(void)version; // Conditional choices based on version
|
|
|
|
// System section
|
|
if (!systemsettings_load(&pp)) {
|
|
dbg("!! System settings failed to load");
|
|
return;
|
|
}
|
|
|
|
if (!ureg_load_units(&pp)) {
|
|
dbg("!! Unit settings failed to load");
|
|
return;
|
|
}
|
|
} // End settings
|
|
|
|
dbg("System settings loaded OK");
|
|
}
|
|
|
|
|
|
static uint8_t *save_buffer = NULL;
|
|
static uint32_t save_addr;
|
|
|
|
#if DEBUG_FLASH_WRITE
|
|
#define fls_printf(fmt, ...) PRINTF(fmt, ##__VA_ARGS__)
|
|
#else
|
|
#define fls_printf(fmt, ...) do {} while (0)
|
|
#endif
|
|
|
|
/**
|
|
* Save buffer overflow handler.
|
|
* This should flush whatever is in the buffer and let CWPack continue
|
|
*
|
|
* @param pb - buffer
|
|
* @param more - how many more bytes are needed (this is meant for realloc / buffer expanding)
|
|
* @return - success code
|
|
*/
|
|
static bool savebuf_ovhandler(PayloadBuilder *pb, uint32_t more)
|
|
{
|
|
if (more > FLASH_SAVE_BUF_LEN) return false;
|
|
savebuf_flush(pb, false);
|
|
return true;
|
|
}
|
|
|
|
// Save settings to flash
|
|
void settings_save(void)
|
|
{
|
|
HAL_StatusTypeDef hst;
|
|
|
|
assert_param(save_buffer == NULL); // It must be NULL here - otherwise we have a leak
|
|
save_buffer = malloc_ck(FLASH_SAVE_BUF_LEN);
|
|
assert_param(save_buffer != NULL);
|
|
|
|
PayloadBuilder pb = pb_start(save_buffer, FLASH_SAVE_BUF_LEN, savebuf_ovhandler);
|
|
|
|
save_addr = SETTINGS_FLASH_ADDR;
|
|
|
|
fls_printf("--- Starting flash write... ---\r\n");
|
|
hst = HAL_FLASH_Unlock();
|
|
assert_param(hst == HAL_OK);
|
|
{
|
|
fls_printf("ERASE flash pages for settings storage...\r\n");
|
|
// We have to first erase the pages
|
|
FLASH_EraseInitTypeDef erase;
|
|
|
|
#if PLAT_FLASHBANKS
|
|
erase.Banks = FLASH_BANK_1; // TODO ?????
|
|
#endif
|
|
|
|
#if defined(GEX_PLAT_F407_DISCOVERY)
|
|
// specialty for F4 with too much flash
|
|
erase.NbSectors = 1;
|
|
erase.Sector = SETTINGS_FLASH_SECTOR;
|
|
erase.TypeErase = FLASH_TYPEERASE_SECTORS;
|
|
erase.VoltageRange = FLASH_VOLTAGE_RANGE_3;
|
|
erase.Banks = FLASH_BANK_1; // unused for sector erase
|
|
#else
|
|
erase.NbPages = SETTINGS_BLOCK_SIZE/FLASH_PAGE_SIZE;
|
|
erase.PageAddress = SETTINGS_FLASH_ADDR;
|
|
erase.TypeErase = FLASH_TYPEERASE_PAGES;
|
|
#endif
|
|
uint32_t pgerror = 0;
|
|
hst = HAL_FLASHEx_Erase(&erase, &pgerror);
|
|
assert_param(pgerror == 0xFFFFFFFFU);
|
|
assert_param(hst == HAL_OK);
|
|
|
|
// and now we can start writing...
|
|
|
|
fls_printf("Beginning settings collect\r\n");
|
|
|
|
// Marker that this is a valid save
|
|
pb_u16(&pb, CONFIG_MARKER);
|
|
pb_u8(&pb, 0); // Settings format version
|
|
|
|
{ // Settings
|
|
fls_printf("Saving system settings\r\n");
|
|
systemsettings_save(&pb);
|
|
|
|
fls_printf("Saving units\r\n");
|
|
ureg_save_units(&pb);
|
|
} // End settings
|
|
|
|
fls_printf("Final flush\r\n");
|
|
savebuf_flush(&pb, true);
|
|
}
|
|
|
|
fls_printf("Locking flash...\r\n");
|
|
hst = HAL_FLASH_Lock();
|
|
assert_param(hst == HAL_OK);
|
|
fls_printf("--- Flash done ---\r\n");
|
|
|
|
free_ck(save_buffer);
|
|
save_buffer = NULL;
|
|
|
|
#if DEBUG_FLASH_WRITE
|
|
dbg("written @ %p", (void*)SETTINGS_FLASH_ADDR);
|
|
hexDump("Flash", (void*)SETTINGS_FLASH_ADDR, 64);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Flush the save buffer to flash, moving leftovers from uneven half-words
|
|
* to the beginning and adjusting the CWPack curent pointer accordingly.
|
|
*
|
|
* @param ctx - pack context
|
|
* @param final - if true, flush uneven leftovers; else move them to the beginning and keep for next call.
|
|
*/
|
|
static void savebuf_flush(PayloadBuilder *pb, bool final)
|
|
{
|
|
assert_param(save_buffer != NULL);
|
|
|
|
// TODO this might be buggy, was not tested cross-boundary yet
|
|
// TODO remove those printf's after verifying correctness
|
|
|
|
uint32_t bytes = (uint32_t) pb_length(pb);
|
|
|
|
// Dump what we're flushing
|
|
fls_printf("Flush: ");
|
|
for (uint32_t i = 0; i < bytes; i++) {
|
|
fls_printf("%02X ", save_buffer[i]);
|
|
}
|
|
fls_printf("\r\n");
|
|
|
|
uint32_t halfwords = bytes >> 1;
|
|
uint32_t remain = bytes & 1; // how many bytes won't be programmed
|
|
|
|
fls_printf("Halfwords: %d, Remain: %d, last? %d\r\n", (int)halfwords, (int)remain, final);
|
|
|
|
uint16_t *hwbuf = (void*) &save_buffer[0];
|
|
|
|
for (; halfwords > 0; halfwords--) {
|
|
uint16_t hword = *hwbuf++;
|
|
|
|
fls_printf("%04X ", hword);
|
|
|
|
HAL_StatusTypeDef res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, save_addr, hword);
|
|
assert_param(HAL_OK == res);
|
|
save_addr += 2; // advance
|
|
}
|
|
|
|
// rewind the context buffer
|
|
pb->current = pb->start;
|
|
|
|
if (remain) {
|
|
// We have an odd byte left to write
|
|
if (final) {
|
|
// We're done writing, this is the last call. Append the last byte to flash.
|
|
uint16_t hword = save_buffer[bytes-1];
|
|
fls_printf("& %02X ", hword);
|
|
|
|
HAL_StatusTypeDef res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, save_addr, hword);
|
|
assert_param(HAL_OK == res);
|
|
} else {
|
|
// Move the leftover to the beginning of the buffer for next call.
|
|
save_buffer[0] = save_buffer[bytes-1];
|
|
pb->current++;
|
|
}
|
|
}
|
|
fls_printf("\r\n");
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
/** Generate a file header */
|
|
static void gex_file_preamble(IniWriter *iw, const char *filename)
|
|
{
|
|
// File header
|
|
iw_hdr_comment(iw, filename);
|
|
iw_hdr_comment(iw, "GEX v%s on %s", GEX_VERSION, GEX_PLATFORM);
|
|
iw_hdr_comment(iw, "built %s at %s", __DATE__, __TIME__);
|
|
}
|
|
|
|
/** Generate a config file header (write instructions) */
|
|
static void ini_preamble(IniWriter *iw, const char *filename)
|
|
{
|
|
gex_file_preamble(iw, filename);
|
|
|
|
if (iw->tag == 0) { // tag 1 is set when exporting via the API
|
|
iw_cmt_newline(iw);
|
|
iw_comment(iw, "Overwrite this file to change settings.");
|
|
#if PLAT_LOCK_BTN
|
|
iw_comment(iw, "Press the LOCK button to save them to Flash.");
|
|
#else
|
|
iw_comment(iw, "Close the LOCK jumper to save them to Flash.");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// --- UNITS.INI ---
|
|
|
|
extern osMutexId mutScratchBufferHandle;
|
|
|
|
/**
|
|
* Build units ini file
|
|
*/
|
|
void settings_build_units_ini(IniWriter *iw)
|
|
{
|
|
ini_preamble(iw, "UNITS.INI");
|
|
assert_param(osOK == osMutexWait(mutScratchBufferHandle, 5000));
|
|
{
|
|
ureg_build_ini(iw);
|
|
}
|
|
assert_param(osOK == osMutexRelease(mutScratchBufferHandle));
|
|
}
|
|
|
|
// --- SYSTEM.INI ---
|
|
|
|
/**
|
|
* Build system settings ini file
|
|
*/
|
|
void settings_build_system_ini(IniWriter *iw)
|
|
{
|
|
ini_preamble(iw, "SYSTEM.INI");
|
|
systemsettings_build_ini(iw);
|
|
}
|
|
|
|
// --- PINOUT.TXT ---
|
|
|
|
/** print system pin.-out info (platform.c) */
|
|
extern void plat_print_system_pinout(IniWriter *iw);
|
|
|
|
/**
|
|
* Build pinout info file
|
|
*/
|
|
void settings_build_pinout_txt(IniWriter *iw)
|
|
{
|
|
gex_file_preamble(iw, "PINOUT.TXT");
|
|
iw_cmt_newline(iw);
|
|
rsc_print_all_available(iw);
|
|
ureg_print_unit_resources(iw);
|
|
plat_print_system_pinout(iw);
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
void settings_load_ini_begin(void)
|
|
{
|
|
SystemSettings.modified = true;
|
|
SystemSettings.loading_inifile = 0;
|
|
}
|
|
|
|
void settings_load_ini_key(const char *restrict section, const char *restrict key, const char *restrict value)
|
|
{
|
|
// dbg("[%s] %s = %s", section, key, value);
|
|
char namebuf[INI_KEY_MAX];
|
|
|
|
// SYSTEM and UNITS files must be separate.
|
|
// Init functions are run for first key in the section.
|
|
|
|
if (streq(section, "SYSTEM")) {
|
|
if (SystemSettings.loading_inifile == 0) {
|
|
SystemSettings.loading_inifile = 'S';
|
|
systemsettings_mco_teardown();
|
|
systemsettings_loadDefaults();
|
|
}
|
|
|
|
// system is always at the top
|
|
systemsettings_load_ini(key, value);
|
|
}
|
|
else if (streq(section, "UNITS")) {
|
|
|
|
if (SystemSettings.loading_inifile == 0) {
|
|
SystemSettings.loading_inifile = 'U';
|
|
ureg_remove_all_units();
|
|
}
|
|
|
|
// this will always come before individual units config
|
|
// install or tear down units as described by the config
|
|
ureg_instantiate_by_ini(key, value);
|
|
}
|
|
else {
|
|
// not a standard section, may be some unit config
|
|
// all unit sections contain the colon character [TYPE:NAME@CALLSIGN]
|
|
const char *nameptr = strchr(section, ':');
|
|
const char *csptr = strchr(section, '@');
|
|
|
|
if (nameptr != NULL && csptr != NULL) {
|
|
strncpy(namebuf, nameptr+1, csptr - nameptr - 1);
|
|
namebuf[csptr - nameptr - 1] = 0;
|
|
uint8_t cs = (uint8_t) avr_atoi(csptr + 1);
|
|
|
|
error_t rv = ureg_load_unit_ini_key(namebuf, key, value, cs);
|
|
if (rv != E_SUCCESS)
|
|
dbg("!! error loading %s@%d.%s = %s - error %s", namebuf, (int)cs, key, value, error_get_message(rv));
|
|
} else {
|
|
dbg("! Bad config key: [%s] %s = %s", section, key, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void settings_load_ini_end(void)
|
|
{
|
|
if (SystemSettings.loading_inifile == 'U') {
|
|
bool suc = ureg_finalize_all_init();
|
|
if (!suc) dbg("Some units failed to init!!");
|
|
}
|
|
|
|
if (SystemSettings.loading_inifile == 'S') {
|
|
systemsettings_mco_init();
|
|
}
|
|
}
|
|
|