/*************************************************************************** * Copyright (C) 2021 by Federico Amedeo Izzo IU2NUO, * * Niccolò Izzo IU2KIN * * Frederik Saraci IU2NRO * * Silvano Seva IU2KWO * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see * ***************************************************************************/ #include #include #include #include "flash.h" /* * Data structure defining the memory layout used for saving and restore * of user settings and VFO configuration. */ typedef struct { uint32_t magic; uint32_t flags[64]; struct dataBlock { settings_t settings; channel_t vfoData; } data[2048]; } __attribute__((packed)) memory_t; // Legacy magic number, from previous versions static const uint32_t legacyMagic = 0x5854524F; // "ORTX" static const uint32_t validMagic = 0x5854504F; // "OPTX" static const uint32_t baseAddress = 0x080E0000; memory_t *memory = ((memory_t *) baseAddress); /** * \internal * Utility function to find the currently active data block inside memory, that * is the one containing the last saved settings. Blocks containing legacy data * are marked with numbers starting from 4096. * * @return number currently active data block or -1 if memory data is invalid. */ int findActiveBlock() { if((memory->magic != validMagic) && (memory->magic != legacyMagic)) { return -1; // Invalid memory data } uint16_t block = 0; for(; block < 64; block++) { if(memory->flags[block] != 0x00000000) { break; } } uint16_t bit = 0; for(; bit < 32; bit++) { if((memory->flags[block] & (1 << bit)) != 0) { break; } } block = (block * 32) + bit; // Mark block as containing legacy data by adding 4096. if(memory->magic == legacyMagic) block += 4096; return block - 1; } int nvm_readVFOChannelData(channel_t *channel) { int block = findActiveBlock(); // Invalid data found if(block < 0) return -1; // Try loading data, if "block" is greater than 2048 the pointer will be // overwritten below without harm. uint8_t *ptr = ((uint8_t *) &(memory->data[block].vfoData)); // Check if we have to load legacy data. if(block > 2048) { block -= 4096; ptr = ((uint8_t *) &(memory->data[block].vfoData)); ptr -= 1; } memcpy(channel, ptr, sizeof(channel_t)); return 0; } int nvm_readSettings(settings_t *settings) { int block = findActiveBlock(); // Invalid data found if(block < 0) return -1; // Check if we have to load legacy data. if(block > 2048) { block -= 4096; memcpy(settings, &(memory->data[block].settings), sizeof(settings_t)); settings->display_timer = TIMER_30S; } else { memcpy(settings, &(memory->data[block].settings), sizeof(settings_t)); } return 0; } int nvm_writeSettingsAndVfo(const settings_t *settings, const channel_t *vfo) { uint32_t addr = 0; int block = findActiveBlock(); /* * Memory never initialised or save space finished: erase all the sector. * On STM32F405 the settings are saved in sector 11, starting at address * 0x08060000. */ if((block < 0) || (block >= 2047)) { flash_eraseSector(11); addr = ((uint32_t) &(memory->magic)); flash_write(addr, &validMagic, sizeof(validMagic)); block = 0; } else { block += 1; } // Save settings addr = ((uint32_t) &(memory->data[block].settings)); flash_write(addr, settings, sizeof(settings_t)); // Save VFO configuration addr = ((uint32_t) &(memory->data[block].vfoData)); flash_write(addr, vfo, sizeof(channel_t)); // Update the flags marking used data blocks uint32_t flag = ~(1 << (block % 32)); addr = ((uint32_t) &(memory->flags[block / 32])); flash_write(addr, &flag, sizeof(uint32_t)); return 0; }