NVProperty generic key value store using the MCU flash area.
Dependents: Turtle_RadioShuttle
Revision 1:3a8297ad8cd9, committed 2019-01-24
- Comitter:
- Helmut Tschemernjak
- Date:
- Thu Jan 24 14:28:11 2019 +0100
- Parent:
- 0:1083b3c785dc
- Child:
- 2:7ab0f5202860
- Commit message:
- Added new files
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty.cpp Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,355 @@
+/*
+ * 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 ARDUINO
+#include <Arduino.h>
+#elif __MBED__
+#include <mbed.h>
+#else
+#error "Unkown operating system"
+#endif
+
+
+#include <NVPropertyProviderInterface.h>
+#include <NVProperty_SRAM.h>
+#ifdef ARDUINO_ARCH_ESP32
+ #include <NVProperty_ESP32NVS.h>
+ #include <NVProperty_ESP32efuse.h>
+#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_ARCH_SAMD)
+ #include <NVProperty_D21Flash.h>
+#elif defined(__MBED__) && defined(TARGET_STM32L4)
+ #include <mbed.h>
+ #include <NVProperty_L4OTP.h>
+ #include <NVProperty_L4Flash.h>
+#else
+#error "Unkown implementation"
+#endif
+#include <NVProperty.h>
+
+
+NVProperty::NVProperty(int propSizekB, bool erase)
+{
+ _flash = NULL;
+ _otp = NULL;
+ _ram = new NVProperty_SRAM();
+
+#ifdef ARDUINO_ARCH_ESP32
+ _flash = new NVProperty_ESP32NVS();
+#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_ARCH_SAMD)
+ _flash = new NVProperty_D21Flash(propSizekB, erase);
+#elif TARGET_STM32L4
+ // TODO _flash = new NVProperty_L4Flash(propSizekB, erase);
+#else
+ #error "unkown platform"
+#endif
+
+#ifdef ARDUINO_ARCH_ESP32
+ _otp = new NVProperty_ESP32efuse();
+#elif TARGET_STM32L4
+ _otp = new NVProperty_L4OTP();
+#endif
+ _allowWrite = false;
+ _didOpen = false;
+}
+
+NVProperty::~NVProperty()
+{
+ if (_ram) {
+ delete _ram;
+ }
+ if (_flash)
+ delete _flash;
+ if (_otp)
+ delete _otp;
+}
+
+
+int
+NVProperty::GetProperty(int key, int defaultValue)
+{
+ int res;
+
+ if (!_didOpen)
+ OpenPropertyStore();
+
+ if (_ram) {
+ res = _ram->GetProperty(key);
+ if (res != NVP_ENOENT)
+ return res;
+ }
+ if (_flash) {
+ res = _flash->GetProperty(key);
+ if (res != NVP_ENOENT)
+ return res;
+ }
+ if (_otp) {
+ res = _otp->GetProperty(key);
+ if (res != NVP_ENOENT)
+ return res;
+ }
+ return defaultValue;
+}
+
+
+int64_t
+NVProperty::GetProperty64(int key, int64_t defaultValue)
+{
+ int64_t res;
+
+ if (!_didOpen)
+ OpenPropertyStore();
+
+ if (_ram) {
+ res = _ram->GetProperty64(key);
+ if (res != NVP_ENOENT)
+ return res;
+ }
+ if (_flash) {
+ res = _flash->GetProperty64(key);
+ if (res != NVP_ENOENT)
+ return res;
+ }
+ if (_otp) {
+ res = _otp->GetProperty64(key);
+ if (res != NVP_ENOENT)
+ return res;
+ }
+ return defaultValue;
+}
+
+const char *
+NVProperty::GetProperty(int key, const char *defaultValue)
+{
+ const char *res;
+
+ if (!_didOpen)
+ OpenPropertyStore();
+
+ if (_ram) {
+ res = _ram->GetPropertyStr(key);
+ if (res != NULL)
+ return res;
+ }
+ if (_flash) {
+ res = _flash->GetPropertyStr(key);
+ if (res != NULL)
+ return res;
+ }
+ if (_otp) {
+ res = _otp->GetPropertyStr(key);
+ if (res != NULL)
+ return res;
+ }
+ if (res != NULL)
+ return res;
+
+ return defaultValue;
+}
+
+int
+NVProperty::GetProperty(int key, void *buffer, int *size)
+{
+ int res;
+ int maxsize = *size;
+
+ if (!_didOpen)
+ OpenPropertyStore();
+
+ if (_ram) {
+ res = _ram->GetPropertyBlob(key, buffer, &maxsize);
+ if (res == NVP_OK)
+ return res;
+ }
+ if (_flash) {
+ res = _flash->GetPropertyBlob(key, buffer, &maxsize);
+ if (res == NVP_OK)
+ return res;
+ }
+ if (_otp) {
+ res = _otp->GetPropertyBlob(key, buffer, &maxsize);
+ if (res == NVP_OK)
+ return res;
+ }
+
+ return NVP_ENOENT;
+}
+
+
+int
+NVProperty::SetProperty(int key, NVPType ptype, int64_t value, NVPStore store)
+{
+ int res = NVP_OK;
+
+ if (!_didOpen)
+ OpenPropertyStore();
+
+ if (!_allowWrite)
+ return NVP_NO_PERM;
+
+ if (store == S_RAM && _ram) {
+ res = _ram->SetProperty(key, value, ptype);
+ } else if (store == S_FLASH && _flash) {
+ res = _flash->SetProperty(key, value, ptype);
+ } else if (store == S_OTP && _otp) {
+ res = _otp->SetProperty(key, value, ptype);
+ } else {
+ return NVP_NO_STORE;
+ }
+ return res;
+}
+
+
+int
+NVProperty::SetProperty(int key, NVPType ptype, const char *value, NVPStore store)
+{
+ int res = NVP_OK;
+
+ if (!_didOpen)
+ OpenPropertyStore();
+
+ if (!_allowWrite)
+ return NVP_NO_PERM;
+
+ if (store == S_RAM && _ram) {
+ res = _ram->SetPropertyStr(key, value, ptype);
+ } else if (store == S_FLASH && _flash) {
+ res = _flash->SetPropertyStr(key, value, ptype);
+ } else if (store == S_OTP && _otp) {
+ res = _otp->SetPropertyStr(key, value, ptype);
+ } else {
+ return NVP_NO_STORE;
+ }
+
+ return res;
+}
+
+// NVProperty_SRAM::SetPropertyBlob(int key, const void *blob, int size, int type)
+
+
+int
+NVProperty::SetProperty(int key, NVPType ptype, const void *blob, int length, NVPStore store)
+{
+ int res = NVP_OK;
+
+ if (!_didOpen)
+ OpenPropertyStore();
+
+ if (!_allowWrite) {
+ return NVP_NO_PERM;
+ }
+
+ if (store == S_RAM && _ram) {
+ res = _ram->SetPropertyBlob(key, blob, length, ptype);
+ } else if (store == S_FLASH && _flash) {
+ res = _flash->SetPropertyBlob(key, blob, length, ptype);
+ } else if (store == S_OTP && _otp) {
+ res = _otp->SetPropertyBlob(key, blob, length, ptype);
+ } else {
+ return NVP_NO_STORE;
+ }
+
+ return res;
+}
+
+int
+NVProperty::EraseProperty(int key, NVPStore store)
+{
+ if (!_didOpen)
+ OpenPropertyStore();
+
+ int res = NVP_OK;
+
+ if (!_allowWrite)
+ return NVP_NO_PERM;
+
+ if (store == S_RAM && _ram) {
+ res = _ram->EraseProperty(key);
+ } else if (store == S_FLASH && _flash) {
+ res = _flash->EraseProperty(key);
+ } else if (store == S_OTP && _otp) {
+ res = _otp->EraseProperty(key);
+ } else {
+ return NVP_NO_STORE;
+ }
+
+ return res;
+}
+
+int
+NVProperty::ReorgProperties(NVPStore store)
+{
+ int res = NVP_OK;
+
+ if (!_didOpen)
+ OpenPropertyStore();
+
+ if (!_allowWrite)
+ return NVP_NO_PERM;
+
+ if (store == S_RAM && _ram) {
+ res = _ram->ReorgProperties();
+ } else if (store == S_FLASH && _flash) {
+ res = _flash->ReorgProperties();
+ } else if (store == S_OTP && _otp) {
+ res = _otp->ReorgProperties();
+ } else {
+ return NVP_NO_STORE;
+ }
+
+ return res;
+}
+
+int
+NVProperty::OpenPropertyStore(bool forWrite)
+{
+ int res = NVP_OK;
+
+ if (_didOpen) {
+ if (_ram)
+ _ram->ClosePropertyStore();
+ if (_flash)
+ _flash->ClosePropertyStore();
+ if (_otp)
+ _otp->ClosePropertyStore();
+ }
+
+ if (_ram)
+ _ram->OpenPropertyStore(forWrite);
+ if (_flash)
+ res = _flash->OpenPropertyStore(forWrite);
+ if (_otp)
+ _otp->OpenPropertyStore(forWrite);
+ _didOpen = true;
+ if(forWrite)
+ _allowWrite = true;
+
+
+ return res;
+}
+
+int
+NVProperty::ClosePropertyStore(bool flush)
+{
+ int res = NVP_OK;
+
+ if (_didOpen)
+ return NVP_NO_PERM;
+
+ if (_ram)
+ _ram->ClosePropertyStore(flush);
+ if (_flash)
+ res = _flash->ClosePropertyStore(flush);
+ if (_otp)
+ _otp->ClosePropertyStore(flush);
+ return res;
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty.h Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,175 @@
+/*
+ * 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
+ */
+
+#ifndef __NVPROPERTY_H__
+#define __NVPROPERTY_H__
+
+
+#ifndef UNUSED
+ #define UNUSED(x) (void)(x)
+#endif
+
+class NVProperty {
+public:
+ /*
+ * The property store size depends on the implementation
+ * The ESP32 uses the NVS partition therefore the size is being ignored.
+ * The D21 uses the default 16kByte which is a good tradeoff between
+ * space needed for the application versus space for properties.
+ * Larger D21 property space (e.g. 20kB) has the advantage that the
+ * flash blocks are less busy. For the D21 it is a good idea to
+ * use increments of 16kB because this is the region locking area size
+ */
+ NVProperty(int propSizekB = 16, bool erase = false);
+ ~NVProperty();
+public:
+ enum NVPType {
+ T_BIT = 1,
+ T_8BIT = 2,
+ T_16BIT = 3,
+ T_32BIT = 4,
+ T_64BIT = 5,
+ T_STR = 6, // strings can be up to 256 bytes including zero
+ T_BLOB = 7, // blobs can be up to 256 bytes long
+ T_MAX = 15 // we have only 4 bit space for the NVP types
+ };
+
+ enum NVPStore {
+ S_OTP = 0x01,
+ S_FLASH = 0x02,
+ S_RAM = 0x04,
+ };
+
+ enum NVPErrCode {
+ NVP_OK = 0,
+ NVP_NO_FLASH = -1,
+ NVP_NO_RAM = -2,
+ NVP_NO_STORE = -3,
+ NVP_NO_PERM = -4,
+ NVP_ERR_NOSPACE = -5,
+ NVP_ERR_FAIL = -6,
+ NVP_INVALD_PARM = -7,
+ NVP_ENOENT = -0x12345687,
+ };
+
+ /*
+ * Get property protocol version to allow
+ * API differences between multiple versions
+ */
+ int GetVersion(void) { return 100; };
+
+ /*
+ * A simple GetProperty retuns its values as an int or int64
+ * The order should always be S_RAM,S_FLASH, S_OTP
+ */
+ int GetProperty(int key, int defaultValue = 0);
+ int64_t GetProperty64(int key, int64_t defaultValue = 0);
+ /*
+ * const char *GetProperty: receives a copy of the propery, use free to release it.
+ */
+ const char *GetProperty(int key, const char *defaultValue = NULL);
+ /*
+ * when a block gets returned the buffer is filled up to the property
+ * or max at the bsize length.
+ * if the buffer is NULL only the size value will be set
+ */
+ int GetProperty(int key, void *buffer, int *size);
+
+ /*
+ * SetProperty
+ * It requires to use OpenPropertyStore and finally ClosePropertyStore(true)
+ * to write out all properties.
+ * Properties are being limited to 256 bytes. (e.g. 255 long strings or 256 bytes blobs)
+ *
+ * Number properties e.g. 0 or 1, or 123 are highly optimized in storage sizes
+ * therefore the value is automatically being compressed to a bit or a little
+ * number to use less flash storage space.
+ */
+ int SetProperty(int key, NVPType ptype, int64_t value, NVPStore store = S_FLASH);
+ int SetProperty(int key, NVPType ptype, const char *value, NVPStore store = S_FLASH);
+ int SetProperty(int key, NVPType ptype, const void *blob, int length, NVPStore store = S_FLASH);
+
+ int EraseProperty(int key, NVPStore store = S_FLASH);
+
+ /*
+ * ReorgProperties is usually not needed because when a property storage is
+ * full it reorganizes itself to make space for new properties.
+ */
+ int ReorgProperties(NVPStore store = S_FLASH);
+ /*
+ * Opens a property store for reading or writing.
+ */
+ int OpenPropertyStore(bool forWrite = false);
+ /*
+ * Closes the property store and flushes the data, depending on the
+ * implementation flush may be not needed, e.g. D21
+ */
+ int ClosePropertyStore(bool flush = false);
+
+ enum Properties {
+ RTC_AGING_CAL = 1, // int8_t the RTC aging calibration value
+ ADC_VREF = 2, // the adc refernce volatge in millivolt
+ HARDWARE_REV = 3, // the hardware revision of the board
+ CPUID = 4, // the internal CPU ID
+
+ LORA_DEVICE_ID = 10, // uint32_t the LoRa device ID
+ LORA_CODE_ID = 11, // uint32_t the Code for the RadioShuttle protocol
+ LORA_REMOTE_ID = 12, // specifies the server address
+ LORA_REMOTE_ID_ALT = 13, // specifies the alternate server address
+ LORA_RADIO_TYPE = 14, // 1 = Offline, 3 = Online, 4 = RS_Station_Basic
+ LORA_FREQUENCY = 15, // channel frequency in Hz, e.g. 868100000
+ LORA_BANDWIDTH = 16, // channel bandwidth in Hz, e.g. 125000
+ LORA_SPREADING_FACTOR = 17, // e.g. 7
+ LORA_TXPOWER = 18, // e.g. 14 for 15 dBm.
+ LORA_FREQUENCY_OFFSET = 19,
+ LORA_APP_PWD = 20, // passwords are per app, there are only two placeholders
+ LORA_APP_PWD_ALT = 21,
+
+ LOC_LONGITUDE = 25, // a string
+ LOC_LATITUDE = 26, // a string
+ LOC_NAME = 27, // a string with the location name
+ HOSTNAME = 28, // the device host name
+
+ WIFI_SSID = 30,
+ WIFI_PASSWORD = 31,
+ WIFI_SSID_ALT = 32,
+ WIFI_PASSWORD_ALT = 33,
+ USE_DHCP = 34,
+ MAC_ADDR = 35,
+ NET_IP_ADDR = 36,
+ NET_IP_ROUTER = 37,
+ NET_IP_DNS_SERVER = 38,
+
+ MQTT_SERVER = 40, // url mqtt or mqtts://user.password@server:port
+ MQTT_SERVER_ALT = 41,
+ MQTT_TOPIC_INFO = 42,
+ MQTT_TOPIC_ALARM= 43,
+ MQTT_TOPIC_CONTROL = 44,
+ MQTT_TOPIC_4 = 45,
+ MQTT_TOPIC_5 = 46,
+
+ ALARM_STATUS = 50, // alarm on=0, off !0
+
+ PRIVATE_RANGE_START = 128,
+ PRIVATE_RANGE_END = 254,
+ PROPERTIES_EOF = 255,
+ MAX_PROPERTIES = 256,
+ };
+
+private:
+ NVPropertyProviderInterface *_ram;
+ NVPropertyProviderInterface *_flash;
+ NVPropertyProviderInterface *_otp;
+ bool _allowWrite;
+ bool _didOpen;
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NVPropertyProviderInterface.h Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,32 @@
+/*
+ * 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
+ */
+
+#ifndef __NVPROPERTYPROVIDERINTERFACE__
+#define __NVPROPERTYPROVIDERINTERFACE__
+
+class NVPropertyProviderInterface {
+public:
+ virtual ~NVPropertyProviderInterface() { }
+
+ virtual int GetProperty(int key) = 0;
+ virtual int64_t GetProperty64(int key) = 0;
+ virtual const char *GetPropertyStr(int key) = 0;
+ virtual int GetPropertyBlob(int key, const void *blob, int *size) = 0;
+ virtual int SetProperty(int key, int64_t value, int type) = 0;
+ virtual int SetPropertyStr(int key, const char *str, int type) = 0;
+ virtual int SetPropertyBlob(int key, const void *blob, int size, int type) = 0;
+ virtual int EraseProperty(int key) = 0;
+ virtual int ReorgProperties(void) = 0;
+ virtual int OpenPropertyStore(bool forWrite = false) = 0;
+ virtual int ClosePropertyStore(bool flush = false) = 0;
+};
+
+#endif // __NVPROPERTYPROVIDERINTERFACE__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty_L4Flash.cpp Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,523 @@
+/*
+ * 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_L4Flash.h>
+#include <NVProperty.h>
+
+#if 0
+#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()
+{
+ _debug = true;
+ _DumpAllEntires();
+}
+
+
+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 > -2147483647 && value < 2147483648)
+ 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)
+{
+ //FlashIAP f;
+ //f.init();
+#ifdef OTP_TEST_IN_RAM
+ memcpy(address, d, length);
+#else
+ uint8_t *data = (uint8_t *)d;
+ uint32_t addr = (uint32_t)address;
+ volatile uint64_t data64;
+
+ HAL_FLASH_Unlock();
+ while (length > 0) {
+ for (uint8_t i = 0; i < 8; i++) {
+ *(((uint8_t *) &data64) + i) = *(data + i);
+ }
+ int err = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr, data64);
+ addr += 8;
+ data += 8;
+ length -= 8;
+ }
+ HAL_FLASH_Lock();
+#endif
+}
+
+#endif
+
+
+#endif // TARGET_STM32L4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty_L4Flash.h Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,96 @@
+/*
+ * 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
+ */
+
+#ifndef __NVPROPERTY_L4FLASH__
+#define __NVPROPERTY_L4FLASH__
+
+class NVProperty_L4Flash : public NVPropertyProviderInterface {
+public:
+ NVProperty_L4Flash(int propSizekB, bool erase);
+ ~NVProperty_L4Flash();
+
+ virtual int GetProperty(int key);
+ virtual int64_t GetProperty64(int key);
+ virtual int GetPropertyBlob(int key, const void *blob, int *size);
+ virtual const char *GetPropertyStr(int key);
+ virtual int SetProperty(int key, int64_t value, int type);
+ virtual int SetPropertyStr(int key, const char *value, int type);
+ virtual int SetPropertyBlob(int key, const void *blob, int size, int type);
+ virtual int EraseProperty(int key);
+ virtual int ReorgProperties(void);
+ virtual int OpenPropertyStore(bool forWrite = false);
+ virtual int ClosePropertyStore(bool flush = false);
+
+private:
+ void _FlashInititalize(bool force = false);
+ void _FlashEraseRow(int startRow, int count = 1);
+ void _FlashWrite(uint8_t *address, const void *data, size_t length);
+ bool _FlashIsCleared(uint8_t *address, int len);
+ void _FlashWritePage(int page, int offset, uint8_t *data, int length);
+
+ struct _flash_header {
+ uint32_t magic;
+ uint16_t version;
+ uint16_t sizeKB;
+ };
+
+ static const int FLASH_ENTRY_HEADER = 4;
+ static const int FLASH_ENTRY_HEADER_SHORT = 2;
+ static const int MAX_DATA_ENTRY = 256;
+
+ struct _flashEntry {
+ uint8_t key; // Property key
+ struct {
+ uint8_t deleted : 1; // this key has been deleted
+ uint8_t t_bit : 1; // contains the bool value
+ uint8_t reserv1 : 1; //
+ uint8_t reserv2 : 1; //
+ uint8_t type : 4; // NVPType
+ } t;
+ union {
+ int16_t v_16bit;
+ int8_t v_8bit;
+ struct {
+ uint8_t d_len; // data length
+ uint8_t f_reserv1 : 8;
+ } option;
+ } u;
+ union {
+ int32_t v_32bit;
+ int32_t v_64bit[2]; // use use 2 x 32-bit to avoid 64-bit struct padding
+ char v_str[MAX_DATA_ENTRY];
+ uint8_t v_blob[MAX_DATA_ENTRY];
+ } data;
+ };
+
+ _flashEntry * _GetFlashEntry(int key, uint8_t *start = NULL);
+ int _GetFlashEntryLen(_flashEntry *k);
+ int _GetFlashPaddingSize(int len);
+ _flashEntry *_lastEntry;
+ void _DumpAllEntires(void);
+ int _FlashReorgEntries(int minRequiredSpace);
+ bool _debug;
+ int _propSizekB;
+ int _pageSize;
+ int _numPages;
+ int _rowSize;
+ uint8_t *_startAddress;
+ uint8_t *_endAddress;
+
+ static const int FLASH_PROP_MAGIC = 0x4c6f5261; // "LORA"
+ static const int FLASH_PROP_VERSION = 3;
+ static const int _bootlLoaderSize = 8192;
+ static const int _lockRegions = 16; // d21 lock regions are always 16 for the entire ram
+
+
+};
+
+#endif // __NVPROPERTY_L4FLASH__
--- /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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty_L4OTP.h Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,87 @@
+/*
+ * 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
+ */
+
+#ifndef __NVPROPERTY_L4OTP__
+#define __NVPROPERTY_L4OTP__
+
+class NVProperty_L4OTP : public NVPropertyProviderInterface {
+public:
+ NVProperty_L4OTP();
+ ~NVProperty_L4OTP();
+
+ virtual int GetProperty(int key);
+ virtual int64_t GetProperty64(int key);
+ virtual int GetPropertyBlob(int key, const void *blob, int *size);
+ virtual const char *GetPropertyStr(int key);
+ virtual int SetProperty(int key, int64_t value, int type);
+ virtual int SetPropertyStr(int key, const char *value, int type);
+ virtual int SetPropertyBlob(int key, const void *blob, int size, int type);
+ virtual int EraseProperty(int key);
+ virtual int ReorgProperties(void);
+ virtual int OpenPropertyStore(bool forWrite = false);
+ virtual int ClosePropertyStore(bool flush = false);
+
+private:
+ void _FlashInititalize(void);
+ void _OTPWrite(uint8_t *address, const void *data, size_t length);
+
+ struct _flash_header {
+ uint32_t magic;
+ uint16_t version;
+ uint16_t size;
+ };
+
+ static const int FLASH_ENTRY_MIN_SIZE = 8;
+ static const int MAX_DATA_ENTRY = 256-FLASH_ENTRY_MIN_SIZE;
+ static const int FLASH_PADDING_SIZE = 8; // writes sizes and alignment must be multiple of 64-bit,
+
+ struct _flashEntry {
+ uint8_t key; // Property key
+ struct {
+ uint8_t deleted : 1; // this key has been deleted
+ uint8_t t_bit : 1; // contains the bool value
+ uint8_t reserv1 : 1; //
+ uint8_t reserv2 : 1; //
+ uint8_t type : 4; // NVPType
+ } t;
+ union {
+ int16_t v_16bit;
+ int8_t v_8bit;
+ struct {
+ uint8_t d_len; // data length
+ uint8_t f_reserv1 : 8;
+ } option;
+ } u;
+ union {
+ int32_t v_32bit;
+ int32_t v_64bit[2]; // use use 2 x 32-bit to avoid 64-bit struct padding
+ char v_str[MAX_DATA_ENTRY];
+ uint8_t v_blob[MAX_DATA_ENTRY];
+ } data;
+ };
+
+ _flashEntry * _GetFlashEntry(int key, uint8_t *start = NULL);
+ void _DumpAllEntires(void);
+ int _GetFlashEntryLen(_flashEntry *k);
+ int _GetFlashPaddingSize(int len);
+ int _FlashReorgEntries(int minRequiredSpace);
+
+ _flashEntry *_lastEntry;
+ uint8_t *_startAddress;
+ uint8_t *_endAddress;
+ bool _debug;
+ int _propSize;
+
+ static const int FLASH_PROP_MAGIC = 0x4c4f5450; // "LOTP"
+ static const int FLASH_PROP_VERSION = 1;
+};
+
+#endif // __NVPROPERTY_L4OTP__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty_SRAM.cpp Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,222 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <NVPropertyProviderInterface.h>
+#include <NVProperty.h>
+#include <NVProperty_SRAM.h>
+
+NVProperty_SRAM::NVProperty_SRAM()
+{
+
+}
+
+NVProperty_SRAM::~NVProperty_SRAM()
+{
+ std::map<int, PropertyEntry>::iterator re;
+ for(re = _props.begin(); re != _props.end(); re++) {
+ if (re->second.type == NVProperty::T_STR)
+ free(re->second.str);
+ if (re->second.type == NVProperty::T_BLOB)
+ delete[] (char *)re->second.data;
+ }
+
+ _props.clear();
+}
+
+int
+NVProperty_SRAM::GetProperty(int key)
+{
+ std::map<int, PropertyEntry>::iterator it = _props.find(key);
+ if(it != _props.end()) {
+ switch (it->second.type) {
+ case NVProperty::T_STR:
+ return NVProperty::NVP_ENOENT;
+ break;
+ case NVProperty::T_BLOB:
+ return NVProperty::NVP_ENOENT;
+ break;
+ default:
+ return it->second.val32;
+ }
+ }
+ return NVProperty::NVP_ENOENT;
+};
+
+
+int64_t
+NVProperty_SRAM::GetProperty64(int key)
+{
+ std::map<int, PropertyEntry>::iterator it = _props.find(key);
+ if(it != _props.end()) {
+ switch (it->second.type) {
+ case NVProperty::T_STR:
+ return NVProperty::NVP_ENOENT;
+ break;
+ case NVProperty::T_BLOB:
+ return NVProperty::NVP_ENOENT;
+ break;
+ default:
+ return it->second.val64;
+ }
+ }
+ return NVProperty::NVP_ENOENT;
+}
+
+const char *
+NVProperty_SRAM::GetPropertyStr(int key)
+{
+ std::map<int, PropertyEntry>::iterator it = _props.find(key);
+ if(it != _props.end()) {
+ if (it->second.type == NVProperty::T_STR) {
+ return (const char *)it->second.str;
+ }
+ }
+ return NULL;
+}
+
+int
+
+NVProperty_SRAM::GetPropertyBlob(int key, const void *blob, int *size)
+{
+ if (!blob || *size <= 0)
+ return NVProperty::NVP_INVALD_PARM;
+
+ std::map<int, PropertyEntry>::iterator it = _props.find(key);
+ if(it != _props.end()) {
+ switch (it->second.type) {
+ case NVProperty::T_BLOB:
+ *size = std::min(*size, (int)it->second.size);
+ if (blob)
+ memcpy((void *)blob, it->second.data, *size);
+ return *size;
+ break;
+ default:
+ break;
+ }
+ }
+ return NVProperty::NVP_ENOENT;
+}
+
+
+int
+NVProperty_SRAM::SetProperty(int key, int64_t value, int type)
+{
+ std::map<int, PropertyEntry>::iterator it = _props.find(key);
+ if(it != _props.end()) {
+ it->second.val32 = value;
+ return 0;
+ }
+
+ struct PropertyEntry r;
+ memset(&r, 0, sizeof(r));
+ r.key = key;
+ r.type = type;
+ if (type <= NVProperty::T_32BIT) {
+ r.size = sizeof(r.val32);
+ r.val32 = value;
+ } else if (type == NVProperty::T_64BIT) {
+ r.size = sizeof(r.val64);
+ r.val64 = value;
+ }
+
+ _props.insert(std::pair<int,PropertyEntry> (key, r));
+
+ return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_SRAM::SetPropertyStr(int key, const char *str, int type)
+{
+ std::map<int, PropertyEntry>::iterator it = _props.find(key);
+ if(it != _props.end()) {
+ if (it->second.str)
+ free(it->second.str);
+ it->second.str = strdup(str);
+ it->second.size = strlen(str)+1;
+ return NVProperty::NVP_OK;
+ }
+
+ struct PropertyEntry r;
+ memset(&r, 0, sizeof(r));
+ r.key = key;
+ r.type = type;
+ r.size = strlen(str)+1;
+ r.str = strdup(str);
+
+ _props.insert(std::pair<int,PropertyEntry> (key, r));
+
+ return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_SRAM::SetPropertyBlob(int key, const void *blob, int size, int type)
+{
+ std::map<int, PropertyEntry>::iterator it = _props.find(key);
+ if(it != _props.end()) {
+ if (it->second.data)
+ delete[] (char *)it->second.data;
+ it->second.size = size;
+ it->second.data = new char[size];
+ memcpy(it->second.data, blob, size);
+
+ return NVProperty::NVP_OK;
+ }
+
+ struct PropertyEntry r;
+ memset(&r, 0, sizeof(r));
+ r.key = key;
+ r.type = type;
+ r.size = size;
+ r.data = new char[size];
+ memcpy(r.data, blob, size);
+ _props.insert(std::pair<int,PropertyEntry> (key, r));
+
+ return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_SRAM::EraseProperty(int key)
+{
+ std::map<int, PropertyEntry>::iterator it = _props.find(key);
+ if(it == _props.end()) {
+ return NVProperty::NVP_ENOENT;
+ }
+ if (it->second.type == NVProperty::T_STR)
+ free((char *)it->second.data);
+ if (it->second.type == NVProperty::T_BLOB)
+ delete[] (char *)it->second.data;
+
+ _props.erase(it->first);
+ return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_SRAM::ReorgProperties(void)
+{
+ return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_SRAM::OpenPropertyStore(bool forWrite)
+{
+ UNUSED(forWrite);
+ return NVProperty::NVP_OK;
+}
+
+int
+NVProperty_SRAM::ClosePropertyStore(bool flush)
+{
+ UNUSED(flush);
+ return NVProperty::NVP_OK;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NVProperty_SRAM.h Thu Jan 24 14:28:11 2019 +0100
@@ -0,0 +1,55 @@
+/*
+ * 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
+ */
+
+#ifndef __NVSProperty_SRAM__
+#define __NVSProperty_SRAM__
+
+
+#ifdef ARDUINO
+#undef min
+#undef max
+#undef map
+#endif
+#include <map>
+
+
+class NVProperty_SRAM : public NVPropertyProviderInterface {
+public:
+ NVProperty_SRAM();
+ ~NVProperty_SRAM();
+ virtual int GetProperty(int key);
+ virtual int64_t GetProperty64(int key);
+ virtual const char *GetPropertyStr(int key);
+ virtual int GetPropertyBlob(int key, const void *blob, int *size);
+ virtual int SetProperty(int key, int64_t value, int type);
+ virtual int SetPropertyStr(int key, const char *value, int type);
+ virtual int SetPropertyBlob(int key, const void *blob, int size, int type);
+ virtual int EraseProperty(int key);
+ virtual int ReorgProperties(void);
+ virtual int OpenPropertyStore(bool forWrite = false);
+ virtual int ClosePropertyStore(bool flush = false);
+
+private:
+ struct PropertyEntry {
+ int key;
+ uint8_t type;
+ uint8_t size;
+ union {
+ int val32;
+ int64_t val64;
+ void *data;
+ char *str;
+ };
+ };
+ std::map<int, PropertyEntry> _props;
+};
+
+#endif // __NVSProperty_SRAM__
Helmut Tschemernjak