mirror of
https://github.com/xoseperez/espurna.git
synced 2026-03-15 04:37:07 +01:00
268 lines
6.7 KiB
C++
268 lines
6.7 KiB
C++
/*
|
|
|
|
EEPROM MODULE
|
|
|
|
*/
|
|
|
|
#include "espurna.h"
|
|
#include "storage_eeprom.h"
|
|
|
|
namespace {
|
|
|
|
bool _eeprom_commit = false;
|
|
|
|
size_t _eeprom_commit_count = 0;
|
|
bool _eeprom_last_commit_result = false;
|
|
|
|
bool _eeprom_ready = false;
|
|
|
|
bool _eepromCommitResult(bool value) {
|
|
// note that even an no-op commit is a 'success'
|
|
if (value) {
|
|
_eeprom_commit_count += 1;
|
|
}
|
|
|
|
_eeprom_last_commit_result = value;
|
|
return value;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
StorageEEPROM_Rotate& eepromInstance() {
|
|
static StorageEEPROM_Rotate instance;
|
|
return instance;
|
|
}
|
|
|
|
StorageEEPROM_Rotate::StorageEEPROM_Rotate() :
|
|
EEPROM_Rotate()
|
|
{
|
|
#ifdef EEPROM_ROTATE_SECTORS
|
|
static_assert(EEPROM_ROTATE_SECTORS > 0, "");
|
|
_pool_size = EEPROM_ROTATE_SECTORS;
|
|
#else
|
|
// ESP8266 environment uses fixed addresses for globally accessible `FS` & `EEPROM`.
|
|
// By default, last() is `(FLASH_SIZE / SECTOR_SIZE) - 5` aka base EEPROM address.
|
|
//
|
|
// EEPROM_Rotate also tries to check whether the `_FS_end` allows for any sectors
|
|
// between it and `_EEPROM_start`. But, this is generally never true.
|
|
const auto last = this->last();
|
|
|
|
// Thus, override original behaviour in regards to size limits and auto-size detection
|
|
// `last()` returns the number of available sectors MINUS reserved by the SDK
|
|
// Prefer to ignore the embedded numbers and use generic `SIZE * 2`.
|
|
if (last > 4000) { // 16Mb
|
|
_pool_size = 32;
|
|
} else if (last > 2000) { // 8Mb
|
|
_pool_size = 16;
|
|
} else if (last > 1000) { // 4Mb
|
|
_pool_size = 8;
|
|
} else if (last > 500) { // 2Mb
|
|
_pool_size = 4;
|
|
} else if (last > 250) { // 1Mb
|
|
_pool_size = 2;
|
|
}
|
|
#endif
|
|
|
|
_offset = EepromRotateOffset;
|
|
as_rotate()->begin(EepromSize);
|
|
|
|
// With all other things equal, make sure this sector could be written to later
|
|
if (!_checkCRC()) {
|
|
fill(0xff);
|
|
writeReservedData();
|
|
_eepromCommitResult(as_base()->commit());
|
|
}
|
|
}
|
|
|
|
void StorageEEPROM_Rotate::fill(uint8_t value) {
|
|
auto* begin = &_data[0] + _offset + EepromRotateReservedSize;
|
|
auto* end = &_data[0] + _size;
|
|
if (_size && (begin < end)) {
|
|
_dirty = true;
|
|
std::fill(begin, end, value);
|
|
}
|
|
}
|
|
|
|
void StorageEEPROM_Rotate::writeReservedData() {
|
|
uint16_t crc = _calculateCRC(); // TODO use poly version vs. sum?
|
|
write(_offset + EEPROM_ROTATE_CRC_OFFSET, (crc >> 8) & 0xFF);
|
|
write(_offset + EEPROM_ROTATE_CRC_OFFSET + 1, crc & 0xFF);
|
|
write(_offset + EEPROM_ROTATE_COUNTER_OFFSET, _sector_value);
|
|
}
|
|
|
|
bool eepromReady() {
|
|
return _eeprom_ready;
|
|
}
|
|
|
|
void eepromRotate(bool value) {
|
|
// Only matters when the instance actually allows rotation.
|
|
// Because .rotate(false) marks EEPROM as dirty, this is equivalent to the .backup(0)
|
|
auto& instance = eepromInstance();
|
|
if (instance.canRotate()) {
|
|
DEBUG_MSG_P(PSTR("[EEPROM] %s EEPROM rotation\n"),
|
|
value ? PSTR("Enabling") : PSTR("Disabling"));
|
|
instance.rotate(value);
|
|
eepromCommit();
|
|
}
|
|
}
|
|
|
|
static String _eepromAvailableSectors(StorageEEPROM_Rotate& instance) {
|
|
const auto current_sector = instance.current();
|
|
|
|
String out;
|
|
for (uint8_t i = 0; i < instance.size(); ++i) {
|
|
if (i > 0) {
|
|
out += STRING_VIEW(", ").toString();
|
|
}
|
|
|
|
const auto sector = instance.getSector(i);
|
|
if (sector == current_sector) {
|
|
out += '(';
|
|
}
|
|
|
|
out += String(sector);
|
|
if (sector == current_sector) {
|
|
out += ')';
|
|
}
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
static String _eepromAvailableSectors() {
|
|
return _eepromAvailableSectors(eepromInstance());
|
|
}
|
|
|
|
String eepromSectors() {
|
|
return _eepromAvailableSectors();
|
|
}
|
|
|
|
static bool _eepromCommit(StorageEEPROM_Rotate& instance) {
|
|
return _eepromCommitResult(instance.commit());
|
|
}
|
|
|
|
static bool _eepromCommit() {
|
|
return _eepromCommit(eepromInstance());
|
|
}
|
|
|
|
void eepromForceCommit(StorageEEPROM_Rotate& instance) {
|
|
_eepromCommit(instance);
|
|
}
|
|
|
|
void eepromForceCommit() {
|
|
_eepromCommit();
|
|
}
|
|
|
|
void eepromCommit() {
|
|
_eeprom_commit = true;
|
|
}
|
|
|
|
size_t eepromSpace() {
|
|
auto& instance = eepromInstance();
|
|
|
|
constexpr auto SectorSize = size_t{ SPI_FLASH_SEC_SIZE };
|
|
const size_t size = instance.size();
|
|
|
|
return size * SectorSize;
|
|
}
|
|
|
|
void eepromClear() {
|
|
auto& instance = eepromInstance();
|
|
instance.fill(0xFF);
|
|
instance.commit();
|
|
}
|
|
|
|
void eepromBackup(uint32_t index){
|
|
auto& instance = eepromInstance();
|
|
instance.backup(index);
|
|
}
|
|
|
|
#if TERMINAL_SUPPORT
|
|
|
|
STRING_VIEW_INLINE(EepromCommand, "EEPROM");
|
|
|
|
static void _eepromCommand(::terminal::CommandContext&& ctx) {
|
|
auto& instance = eepromInstance();
|
|
ctx.output.printf_P(PSTR("Sector%s: %s\n"),
|
|
instance.canRotate() ? "s" : "",
|
|
_eepromAvailableSectors(instance).c_str());
|
|
|
|
if (_eeprom_commit_count > 0) {
|
|
ctx.output.printf_P(PSTR("Commits done: %lu, last: %s\n"),
|
|
_eeprom_commit_count,
|
|
_eeprom_last_commit_result
|
|
? PSTR("OK")
|
|
: PSTR("ERROR"));
|
|
}
|
|
|
|
terminalOK(ctx);
|
|
}
|
|
|
|
STRING_VIEW_INLINE(EepromCommit, "EEPROM.COMMIT");
|
|
|
|
static void _eepromCommandCommit(::terminal::CommandContext&& ctx) {
|
|
if (_eepromCommit()) {
|
|
terminalOK(ctx);
|
|
} else {
|
|
terminalError(ctx);
|
|
}
|
|
}
|
|
|
|
STRING_VIEW_INLINE(EepromDump, "EEPROM.DUMP");
|
|
|
|
static void _eepromCommandDump(::terminal::CommandContext&& ctx) {
|
|
auto& instance = eepromInstance();
|
|
instance.dump(ctx.output);
|
|
terminalOK(ctx);
|
|
}
|
|
|
|
STRING_VIEW_INLINE(FlashDump, "FLASH.DUMP");
|
|
|
|
static void _flashCommandDump(::terminal::CommandContext&& ctx) {
|
|
auto& instance = eepromInstance();
|
|
|
|
static const uint32_t Sectors = ESP.getFlashChipSize() / SPI_FLASH_SEC_SIZE;
|
|
if (ctx.argv.size() < 2) {
|
|
terminalError(ctx, _eepromAvailableSectors(instance));
|
|
return;
|
|
}
|
|
uint32_t sector = espurna::settings::internal::convert<uint32>(ctx.argv[1]);
|
|
if (sector >= Sectors) {
|
|
terminalError(ctx, STRING_VIEW("Sector out of range").toString());
|
|
return;
|
|
}
|
|
|
|
instance.dump(ctx.output, sector);
|
|
terminalOK(ctx);
|
|
}
|
|
|
|
static constexpr ::terminal::Command EepromCommands[] PROGMEM {
|
|
{EepromCommand, _eepromCommand},
|
|
{EepromCommit, _eepromCommandCommit},
|
|
{EepromDump, _eepromCommandDump},
|
|
{FlashDump, _flashCommandDump},
|
|
};
|
|
|
|
static void _eepromCommandsSetup() {
|
|
espurna::terminal::add(EepromCommands);
|
|
}
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void eepromLoop() {
|
|
if (_eeprom_commit) {
|
|
_eepromCommit();
|
|
_eeprom_commit = false;
|
|
}
|
|
}
|
|
|
|
void eepromSetup() {
|
|
#if TERMINAL_SUPPORT
|
|
_eepromCommandsSetup();
|
|
#endif
|
|
|
|
espurnaRegisterLoop(eepromLoop);
|
|
_eeprom_ready = true;
|
|
}
|