NVProperty generic key value store using the MCU flash area.
Dependents: Turtle_RadioShuttle
Diff: NVProperty_L4OTP.cpp
- Revision:
- 1:3a8297ad8cd9
- Child:
- 2:7ab0f5202860
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NVProperty_L4OTP.cpp Thu Jan 24 14:28:11 2019 +0100 @@ -0,0 +1,529 @@ +/* + * 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 <NVPropertyProviderInterface.h> +#include <NVProperty_L4OTP.h> +#include <NVProperty.h> + + +#if 0 // sample test code for a man 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(); +} + + +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