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.
219 lines
5.1 KiB
219 lines
5.1 KiB
#include <EEPROM.h>
|
|
|
|
#include "watchos.h"
|
|
#include "Module_Storage.h"
|
|
|
|
// TODO: Make tiered storage. Smaller allocations can go to RTC RAM.
|
|
|
|
struct StorageEntry
|
|
{
|
|
byte module;
|
|
uint16_t length;
|
|
byte options;
|
|
};
|
|
|
|
#define MODULE_STORAGE_SIZE 3524
|
|
#define MODULE_STORAGE_HEADER_SIZE 4
|
|
#define MODULE_STORAGE_ENTRY_SIZE 4
|
|
|
|
#define MODULE_STORAGE_MAGIC 0x6580
|
|
#define MODULE_STORAGE_VERSION 0x00
|
|
|
|
RTC_NOINIT_ATTR byte RTC_MEM[MODULE_STORAGE_SIZE];
|
|
|
|
byte read_rtc(uint16_t offset);
|
|
void write_rtc(uint16_t offset, byte value);
|
|
|
|
void Module_Storage::initialize()
|
|
{
|
|
for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++)
|
|
{
|
|
m_entry[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
void Module_Storage::start()
|
|
{
|
|
load();
|
|
}
|
|
|
|
void Module_Storage::suspend()
|
|
{
|
|
commit();
|
|
}
|
|
|
|
bool Module_Storage::isValid()
|
|
{
|
|
return ((read_rtc(0) << 8) | read_rtc(1)) == MODULE_STORAGE_MAGIC;
|
|
}
|
|
|
|
void Module_Storage::load()
|
|
{
|
|
// Read rtc ram to m_entry
|
|
m_magic = (read_rtc(0) << 8) | read_rtc(1);
|
|
m_version = read_rtc(2);
|
|
m_options = read_rtc(3);
|
|
|
|
if (m_magic != MODULE_STORAGE_MAGIC)
|
|
watchos::panic("Invalid magic in persistent data");
|
|
if (m_version != MODULE_STORAGE_VERSION)
|
|
watchos::panic("Invalid version of persistent data");
|
|
|
|
for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++)
|
|
{
|
|
int offset = MODULE_STORAGE_HEADER_SIZE + (MODULE_STORAGE_ENTRY_SIZE * i);
|
|
byte module = read_rtc(offset + 0);
|
|
if (module == 0xFF)
|
|
continue;
|
|
|
|
m_entry[i] = new StorageEntry();
|
|
m_entry[i]->module = module;
|
|
m_entry[i]->length = (read_rtc(offset + 1) << 8) | read_rtc(offset + 2);
|
|
m_entry[i]->options = read_rtc(offset + 3);
|
|
}
|
|
}
|
|
|
|
void Module_Storage::commit()
|
|
{
|
|
// Sync allocations into RTC memory
|
|
write_rtc(0, (byte)(m_magic >> 8));
|
|
write_rtc(1, (byte)(m_magic));
|
|
write_rtc(2, m_version);
|
|
write_rtc(3, m_options);
|
|
|
|
for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++)
|
|
{
|
|
int offset = MODULE_STORAGE_HEADER_SIZE + (MODULE_STORAGE_ENTRY_SIZE * i);
|
|
if (m_entry[i] == nullptr)
|
|
{
|
|
write_rtc(offset + 0, 0xFF);
|
|
}
|
|
else
|
|
{
|
|
write_rtc(offset + 0, m_entry[i]->module);
|
|
write_rtc(offset + 1, (byte)(m_entry[i]->length >> 8));
|
|
write_rtc(offset + 2, (byte)(m_entry[i]->length));
|
|
write_rtc(offset + 3, m_entry[i]->options);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Module_Storage::reset()
|
|
{
|
|
m_magic = MODULE_STORAGE_MAGIC;
|
|
m_version = MODULE_STORAGE_VERSION;
|
|
m_options = 0;
|
|
|
|
// Write out blank allocations
|
|
write_rtc(0, (byte)(m_magic >> 8));
|
|
write_rtc(1, (byte)(m_magic));
|
|
write_rtc(2, m_version);
|
|
write_rtc(3, m_options);
|
|
|
|
for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++)
|
|
{
|
|
if (m_entry[i] != nullptr)
|
|
{
|
|
free(m_entry[i]);
|
|
m_entry[i] = nullptr;
|
|
}
|
|
|
|
write_rtc(MODULE_STORAGE_HEADER_SIZE + (MODULE_STORAGE_ENTRY_SIZE * i), 0xFF);
|
|
}
|
|
}
|
|
|
|
bool Module_Storage::allocate(byte module, uint16_t size, byte options)
|
|
{
|
|
// Check if an allocation already exists
|
|
for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++)
|
|
{
|
|
if (m_entry[i] != nullptr && m_entry[i]->module == module)
|
|
{
|
|
if (m_entry[i]->length != size)
|
|
watchos::panic("Storage module does not support reallocating storage with a new size.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// None exists, allocate
|
|
for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++)
|
|
{
|
|
if (m_entry[i] == nullptr)
|
|
{
|
|
m_entry[i] = new StorageEntry();
|
|
m_entry[i]->module = module;
|
|
m_entry[i]->length = size;
|
|
m_entry[i]->options = options;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// None exists and we were unable to allocate one
|
|
watchos::panic("Exceeded MODULE_STORAGE_MAX_ENTRIES");
|
|
}
|
|
|
|
void Module_Storage::write(byte module, uint16_t offset, byte value)
|
|
{
|
|
// TODO: Check offset doesn't put us past the end of the allocation
|
|
write_rtc(getOffset(module) + offset, value);
|
|
}
|
|
|
|
byte Module_Storage::read(byte module, uint16_t offset)
|
|
{
|
|
// TODO: Check offset doesn't put us past the end of the allocation
|
|
return read_rtc(getOffset(module) + offset);
|
|
}
|
|
|
|
bool Module_Storage::allocate(well_known_handle_t module, uint16_t size, byte options)
|
|
{
|
|
return allocate(wellKnownHandleToByte(module), size);
|
|
}
|
|
|
|
void Module_Storage::write(well_known_handle_t module, uint16_t offset, byte value)
|
|
{
|
|
return write(wellKnownHandleToByte(module), offset, value);
|
|
}
|
|
|
|
byte Module_Storage::read(well_known_handle_t module, uint16_t offset)
|
|
{
|
|
return read(wellKnownHandleToByte(module), offset);
|
|
}
|
|
|
|
byte Module_Storage::wellKnownHandleToByte(well_known_handle_t handle)
|
|
{
|
|
byte count = 0;
|
|
for (;;)
|
|
{
|
|
if (((handle >> count) & 1) == 1)
|
|
return count;
|
|
else
|
|
count++;
|
|
assert(count < 64);
|
|
}
|
|
}
|
|
|
|
int Module_Storage::getOffset(byte module)
|
|
{
|
|
int offset = MODULE_STORAGE_HEADER_SIZE + (MODULE_STORAGE_ENTRY_SIZE * MODULE_STORAGE_MAX_ENTRIES);
|
|
for (int i = 0; i < MODULE_STORAGE_MAX_ENTRIES; i++)
|
|
{
|
|
if (m_entry[i] != nullptr)
|
|
{
|
|
if (m_entry[i]->module == module)
|
|
return offset;
|
|
else
|
|
offset += m_entry[i]->length;
|
|
}
|
|
}
|
|
watchos::panic("Cannot get offset for module %d; not found", module);
|
|
}
|
|
|
|
byte read_rtc(uint16_t offset)
|
|
{
|
|
return RTC_MEM[offset];
|
|
}
|
|
|
|
void write_rtc(uint16_t offset, byte value)
|
|
{
|
|
RTC_MEM[offset] = value;
|
|
}
|