NVProperty generic key value store using the MCU flash area.
Dependents: Turtle_RadioShuttle
NVProperty_L4OTP.cpp
- Committer:
- Helmut Tschemernjak
- Date:
- 2019-02-04
- Revision:
- 8:b42bb4130002
- Parent:
- 5:2560e615ccd5
- Child:
- 12:5539cdc8be4b
File content as of revision 8:b42bb4130002:
/*
* This is an unpublished work copyright
* (c) 2019 Helmut Tschemernjak
* 30826 Garbsen (Hannover) Germany
*
*
* Use is granted to registered RadioShuttle licensees only.
* Licensees must own a valid serial number and product code.
* Details see: www.radioshuttle.de
*/
#ifdef TARGET_STM32L4
#include <mbed.h>
#include "main.h"
#include "arch.h"
#include <algorithm>
#include <NVPropertyProviderInterface.h>
#include <NVProperty_L4OTP.h>
#include <NVProperty.h>
#if 0 // sample test code for a main app.
{
NVProperty p;
p.OpenPropertyStore(true);
dprintf("OTP--1: %d", p.GetProperty(p.CPUID, -1));
p.SetProperty(p.CPUID, p.T_32BIT, 123, p.S_OTP);
dprintf("OTP123: %d", p.GetProperty(p.CPUID, 0));
p.SetProperty(p.CPUID, p.T_32BIT, 0x12345678, p.S_OTP);
dprintf("OTP0x12345678: %x", p.GetProperty(p.CPUID, 0));
p.EraseProperty(p.CPUID, p.S_OTP);
dprintf("OTP:-2 %d", p.GetProperty(p.CPUID, -2));
dprintf("OTP: Host %s", p.GetProperty(p.HOSTNAME, "MyHost"));
p.SetProperty(p.HOSTNAME, p.T_STR, "Wunstorf", p.S_OTP);
dprintf("OTP: Host %s", p.GetProperty(p.HOSTNAME, "MyHost"));
p.SetProperty(p.CPUID, p.T_32BIT, 9876, p.S_OTP);
dprintf("OTP9876: %d", p.GetProperty(p.CPUID, 0));
dprintf("OTP: Host %s", p.GetProperty(p.HOSTNAME, "MyHost"));
}
#endif
// #define OTP_TEST_IN_RAM // test OTP in RAM
NVProperty_L4OTP::NVProperty_L4OTP()
{
_debug = false;
_propSize = 1 * 1024; // no define in HAL laye
_startAddress = (uint8_t *) 0x1FFF7000; // no define in HAL layer
#ifdef OTP_TEST_IN_RAM
static uint8_t *savedStart;
if (!savedStart) {
_startAddress = (uint8_t *) malloc(512);
memset(_startAddress, 0xff, 512);
savedStart = _startAddress;
} else {
_startAddress = savedStart;
}
#endif
_endAddress = _startAddress + (_propSize);
_lastEntry = NULL;
_FlashInititalize();
_GetFlashEntry(0); // inits the _lastEntry record
}
NVProperty_L4OTP::~NVProperty_L4OTP()
{
#ifdef OTP_TEST_IN_RAM
_debug = true;
_DumpAllEntires();
wait_ms(100);
dump("_startAddress", _startAddress, 100);
#endif
}
void
NVProperty_L4OTP::_FlashInititalize(void)
{
_flash_header *fh = (_flash_header *)_startAddress;
if (fh->magic == FLASH_PROP_MAGIC && fh->version == FLASH_PROP_VERSION && fh->size == _propSize) {
return;
}
uint8_t *p = _startAddress;
for (int i = 0; i < (int)sizeof(_flash_header); i++) {
if (*p++ != 0xff)
return; // invalid data
}
_flash_header f;
memset(&f, 0, sizeof(f));
f.magic = FLASH_PROP_MAGIC;
f.version = FLASH_PROP_VERSION;
f.size = _propSize;
_OTPWrite(_startAddress, &f, sizeof(f));
}
int
NVProperty_L4OTP::GetProperty(int key)
{
return GetProperty64(key);
}
int64_t
NVProperty_L4OTP::GetProperty64(int key)
{
_flashEntry *p = _GetFlashEntry(key);
if (!p)
return NVProperty::NVP_ENOENT;
int64_t value = 0;
switch(p->t.type) {
case NVProperty::T_BIT:
if (p->t.t_bit)
value = 1;
else
value = 0;
break;
case NVProperty::T_8BIT:
value = p->u.v_8bit;
break;
case NVProperty::T_16BIT:
{
int16_t v;
memcpy(&v, &p->u.v_16bit, sizeof(p->u.v_16bit));
value = v;
}
break;
case NVProperty::T_32BIT:
{
int32_t v;
memcpy(&v, &p->data.v_32bit, sizeof(p->data.v_32bit));
value = v;
}
break;
case NVProperty::T_64BIT:
memcpy(&value, p->data.v_64bit, sizeof(p->data.v_64bit));
break;
case NVProperty::T_STR:
case NVProperty::T_BLOB:
value = p->u.option.d_len;
break;
}
return value;
}
const char *
NVProperty_L4OTP::GetPropertyStr(int key)
{
_flashEntry *p = _GetFlashEntry(key);
if (!p || p->t.type != NVProperty::T_STR)
return NULL;
return strdup(p->data.v_str);
}
int
NVProperty_L4OTP::GetPropertyBlob(int key, const void *blob, int *size)
{
_flashEntry *p = _GetFlashEntry(key);
if (!p || p->t.type != NVProperty::T_BLOB)
return NVProperty::NVP_ENOENT;
int cplen = std::min(*size, (int)p->u.option.d_len);
if (blob)
memcpy((void *)blob, p->data.v_blob, cplen);
*size = cplen;
return NVProperty::NVP_OK;
}
int
NVProperty_L4OTP::SetProperty(int key, int64_t value, int type)
{
UNUSED(type);
uint8_t valbuf[FLASH_ENTRY_MIN_SIZE + sizeof(int64_t)];
_flashEntry *p = (_flashEntry *) valbuf;
int storeType;
if (GetProperty64(key) == value) // no need to save it again.
return NVProperty::NVP_OK;
memset(valbuf, 0, sizeof(valbuf));
if (value == 0 || value == 1)
storeType = NVProperty::T_BIT;
else if (value >= -128 && value < 128)
storeType = NVProperty::T_8BIT;
else if (value >= -32768 && value < 32768)
storeType = NVProperty::T_16BIT;
else if (value > -2147483647LL && value < 2147483648LL)
storeType = NVProperty::T_32BIT;
else
storeType = NVProperty::T_64BIT;
p->key = key;
p->t.type = storeType;
switch(storeType) {
case NVProperty::T_BIT:
p->t.t_bit = value;
break;
case NVProperty::T_8BIT:
p->u.v_8bit = value;
break;
case NVProperty::T_16BIT:
p->u.v_16bit = value;
break;
case NVProperty::T_32BIT:
p->u.option.d_len = sizeof(p->data.v_32bit);
{
int32_t v = value;
memcpy(&p->data.v_32bit, &v, sizeof(p->data.v_32bit));
}
break;
case NVProperty::T_64BIT:
p->u.option.d_len = sizeof(p->data.v_64bit);
memcpy(p->data.v_64bit, &value, sizeof(p->data.v_64bit));
break;
}
int len;
if (storeType == NVProperty::T_BIT || storeType == NVProperty::T_8BIT || storeType == NVProperty::T_16BIT || storeType == NVProperty::T_32BIT) {
len = FLASH_ENTRY_MIN_SIZE;
} else { // 64/STR/BLOB
len = (FLASH_ENTRY_MIN_SIZE - 4) + p->u.option.d_len;
len += _GetFlashPaddingSize(len);
}
if ((uint8_t *)_lastEntry + len >= _endAddress) {
if (!_FlashReorgEntries(len))
return NVProperty::NVP_ERR_NOSPACE;
}
_OTPWrite((uint8_t *)_lastEntry, p, len);
_lastEntry = (_flashEntry *)((uint8_t *)_lastEntry + len);
// _DumpAllEntires();
return NVProperty::NVP_OK;
}
int
NVProperty_L4OTP::SetPropertyStr(int key, const char *value, int type)
{
if (type != NVProperty::T_STR)
return NVProperty::NVP_INVALD_PARM;
_flashEntry *p = _GetFlashEntry(key);
if (p && p->t.type == NVProperty::T_STR && strcmp(p->data.v_str, value) == 0) {
return NVProperty::NVP_OK;
}
int err = NVProperty::NVP_OK;
p = new _flashEntry();
if (!p)
return NVProperty::NVP_ERR_NOSPACE;
p->key = key;
p->t.type = NVProperty::T_STR;
int cplen = std::min(strlen(value), sizeof(p->data.v_str)-1);
memcpy(p->data.v_str, value, cplen);
p->u.option.d_len = cplen + 1; // zero term
int len = (FLASH_ENTRY_MIN_SIZE - 4) + p->u.option.d_len;
len += _GetFlashPaddingSize(len);
if ((uint8_t *)_lastEntry + len >= _endAddress) {
if (!_FlashReorgEntries(len)) {
err = NVProperty::NVP_ERR_NOSPACE;
goto done;
}
}
_OTPWrite((uint8_t *)_lastEntry, p, len);
_lastEntry = (_flashEntry *)((uint8_t *)_lastEntry + len);
done:
delete[] p;
// _DumpAllEntires();
return err;
}
int
NVProperty_L4OTP::SetPropertyBlob(int key, const void *blob, int size, int type)
{
if (type != NVProperty::T_BLOB)
return NVProperty::NVP_INVALD_PARM;
_flashEntry *p = _GetFlashEntry(key);
if (p && p->t.type == NVProperty::T_BLOB && size == p->u.option.d_len) { // check for duplicate
if (memcmp(blob, p->data.v_blob, size) == 0)
return NVProperty::NVP_OK;
}
int err = NVProperty::NVP_OK;
p = new _flashEntry();
if (!p)
return NVProperty::NVP_ERR_NOSPACE;
p->key = key;
p->t.type = NVProperty::T_BLOB;
int cplen = std::min(size, (int)sizeof(p->data.v_blob));
p->u.option.d_len = cplen;
memcpy(p->data.v_blob, blob, cplen);
int len = (FLASH_ENTRY_MIN_SIZE - 4) + p->u.option.d_len;
len += _GetFlashPaddingSize(len);
if ((uint8_t *)_lastEntry + len >= _endAddress) {
if (!_FlashReorgEntries(len)) {
err = NVProperty::NVP_ERR_NOSPACE;
goto done;
}
}
_OTPWrite((uint8_t *)_lastEntry, p, len);
_lastEntry = (_flashEntry *)((uint8_t *)_lastEntry + len);
done:
delete[] p;
// _DumpAllEntires();
return err;
}
int
NVProperty_L4OTP::EraseProperty(int key)
{
uint8_t valbuf[FLASH_ENTRY_MIN_SIZE];
_flashEntry *p = (_flashEntry *) valbuf;
_flashEntry *op = _GetFlashEntry(key);
if (!op)
return NVProperty::NVP_ENOENT;
if (op->t.deleted)
return NVProperty::NVP_OK;
memset(valbuf, 0, sizeof(valbuf));
p->key = key;
p->t.type = op->t.type;
p->t.deleted = true;
if ((uint8_t *)_lastEntry + FLASH_ENTRY_MIN_SIZE > _endAddress) {
if (!_FlashReorgEntries(FLASH_ENTRY_MIN_SIZE))
return NVProperty::NVP_ERR_NOSPACE;
}
_OTPWrite((uint8_t *)_lastEntry, p, FLASH_ENTRY_MIN_SIZE);
_lastEntry = (_flashEntry *)((uint8_t *)_lastEntry + FLASH_ENTRY_MIN_SIZE);
// _DumpAllEntires();
return NVProperty::NVP_OK;
}
int
NVProperty_L4OTP::ReorgProperties(void)
{
return NVProperty::NVP_OK;
}
int
NVProperty_L4OTP::OpenPropertyStore(bool forWrite)
{
UNUSED(forWrite);
return NVProperty::NVP_OK;
}
int
NVProperty_L4OTP::ClosePropertyStore(bool flush)
{
return NVProperty::NVP_OK;
}
#if 1
void
NVProperty_L4OTP::_DumpAllEntires(void)
{
if (!_debug)
return;
dprintf("------------- DumpAllEntires -------- ");
int index = 0;
_flashEntry *p = (_flashEntry *)(_startAddress + sizeof(_flash_header));
while((uint8_t *)p < _endAddress && p->key != NVProperty::PROPERTIES_EOF) {
int64_t value = 0;
switch(p->t.type) {
case NVProperty::T_BIT:
if (p->t.t_bit)
value = 1;
else
value = 0;
break;
case NVProperty::T_8BIT:
value = p->u.v_8bit;
break;
case NVProperty::T_16BIT:
{
int16_t v;
memcpy(&v, &p->u.v_16bit, sizeof(p->u.v_16bit));
value = v;
}
break;
case NVProperty::T_32BIT:
{
int32_t v;
memcpy(&v, &p->data.v_32bit, sizeof(p->data.v_32bit));
value = v;
}
break;
case NVProperty::T_64BIT:
memcpy(&value, p->data.v_64bit, sizeof(p->data.v_64bit));
break;
case NVProperty::T_STR:
case NVProperty::T_BLOB:
value = p->u.option.d_len;
break;
}
index++;
if (p->t.deleted) {
dprintf("Dump[%.2d] Key: %d Type: %d deleted(%d)", index, p->key, p->t.type, p->t.deleted);
} else if (p->t.type == NVProperty::T_STR) {
dprintf("Dump[%.2d] Key: %d Type: %d value: %s", index, p->key, p->t.type, p->data.v_str);
} else if (p->t.type == NVProperty::T_BLOB) {
dprintf("Dump[%.2d] Key: %d Type: %d len: %d", index, p->key, p->t.type, p->u.option.d_len);
dump("Blob", p->data.v_str, p->u.option.d_len);
} else {
if (p->t.type == NVProperty::T_64BIT) {
dprintf("Dump[%.2d] Key: %d Type: %d value: %lld (0x%llx)", index, p->key, p->t.type, value, value);
} else {
dprintf("Dump[%.2d] Key: %d Type: %d value: %ld (0x%x)", index, p->key, p->t.type, (int32_t)value, (unsigned int)value);
}
}
p = (_flashEntry *)((uint8_t *)p + _GetFlashEntryLen(p));
}
int freebytes = _endAddress -(uint8_t *)_lastEntry;
if (_lastEntry)
dprintf("------ %d bytes free -------", freebytes);
}
#endif
NVProperty_L4OTP::_flashEntry *
NVProperty_L4OTP::_GetFlashEntry(int key, uint8_t *start)
{
_flashEntry *p;
if (start)
p = (_flashEntry *)start;
else
p = (_flashEntry *)(_startAddress + sizeof(_flash_header));
_flashEntry *lastP = NULL;
while(true) {
if ((uint8_t*)p >= _endAddress || p->key == NVProperty::PROPERTIES_EOF) {
if ((uint8_t*)p <= _endAddress)
_lastEntry = p;
if (!lastP || lastP->t.deleted)
return NULL;
break;
}
if (p->key == key)
lastP = p;
p = (_flashEntry *)((uint8_t *)p + _GetFlashEntryLen(p));
}
return lastP;
}
int
NVProperty_L4OTP::_GetFlashEntryLen(_flashEntry *p)
{
int len = 0;
switch(p->t.type) {
case NVProperty::T_64BIT:
case NVProperty::T_STR:
case NVProperty::T_BLOB:
len = (FLASH_ENTRY_MIN_SIZE - 4) + p->u.option.d_len;
len += _GetFlashPaddingSize(len);
break;
default:
len = FLASH_ENTRY_MIN_SIZE;
}
return len;
}
int
NVProperty_L4OTP::_GetFlashPaddingSize(int len)
{
int remain = len % FLASH_PADDING_SIZE;
if (remain == 0)
return 0;
return (len + FLASH_PADDING_SIZE - remain) - len;
}
int
NVProperty_L4OTP::_FlashReorgEntries(int minRequiredSpace)
{
return 0; // no reorg on OTP
}
void
NVProperty_L4OTP::_OTPWrite(uint8_t *address, const void *d, size_t length)
{
#ifdef OTP_TEST_IN_RAM
memcpy(address, d, length);
#else
OTPWrite(address, d, length);
#endif
}
#endif // TARGET_STM32L4
Helmut Tschemernjak