From 1644294b063209d500fdae0b1398d845b0ea9ecf Mon Sep 17 00:00:00 2001 From: EricB-ADI <122300463+EricB-ADI@users.noreply.github.com> Date: Wed, 10 Apr 2024 11:53:12 -0500 Subject: [PATCH] feat(BLE): WSF-NVM Storage Full Error Handling (#975) Co-authored-by: EricB-ADI --- .../Cordio/ble-profiles/include/app_db.h | 20 +- .../ble-profiles/sources/af/common/app_db.c | 153 ++++- .../maxim/max32655/sources/pal_flash.c | 4 +- .../maxim/max32665/sources/pal_flash.c | 2 +- Libraries/Cordio/wsf/include/wsf_nvm.h | 27 + .../wsf/sources/targets/baremetal/wsf_nvm.c | 604 ++++++++++-------- .../wsf/sources/targets/freertos/wsf_nvm.c | 131 +++- 7 files changed, 638 insertions(+), 303 deletions(-) diff --git a/Libraries/Cordio/ble-profiles/include/app_db.h b/Libraries/Cordio/ble-profiles/include/app_db.h index 3e657a49a24..a185903b37e 100644 --- a/Libraries/Cordio/ble-profiles/include/app_db.h +++ b/Libraries/Cordio/ble-profiles/include/app_db.h @@ -526,7 +526,7 @@ void AppDbSetPeerRpao(appDbHdl_t hdl, bool_t peerRpao); * \return None. */ /*************************************************************************************************/ -void AppDbNvmStorePeerRpao(appDbHdl_t hdl); +bool_t AppDbNvmStorePeerRpao(appDbHdl_t hdl); /*************************************************************************************************/ /*! @@ -537,7 +537,7 @@ void AppDbNvmStorePeerRpao(appDbHdl_t hdl); * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreCccTbl(appDbHdl_t hdl); +bool_t AppDbNvmStoreCccTbl(appDbHdl_t hdl); /*************************************************************************************************/ /*! @@ -548,7 +548,7 @@ void AppDbNvmStoreCccTbl(appDbHdl_t hdl); * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreHdlList(appDbHdl_t hdl); +bool_t AppDbNvmStoreHdlList(appDbHdl_t hdl); /*************************************************************************************************/ /*! @@ -559,7 +559,7 @@ void AppDbNvmStoreHdlList(appDbHdl_t hdl); * \return None. */ /*************************************************************************************************/ -void AppDbNvmStorePeerSignCounter(appDbHdl_t hdl); +bool_t AppDbNvmStorePeerSignCounter(appDbHdl_t hdl); /*************************************************************************************************/ /*! @@ -570,7 +570,7 @@ void AppDbNvmStorePeerSignCounter(appDbHdl_t hdl); * \return None. */ /*************************************************************************************************/ -void AppDbNvmStorePeerAddrRes(appDbHdl_t hdl); +bool_t AppDbNvmStorePeerAddrRes(appDbHdl_t hdl); /*************************************************************************************************/ /*! @@ -581,7 +581,7 @@ void AppDbNvmStorePeerAddrRes(appDbHdl_t hdl); * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreChangeAwareState(appDbHdl_t hdl); +bool_t AppDbNvmStoreChangeAwareState(appDbHdl_t hdl); /*************************************************************************************************/ /*! @@ -592,7 +592,7 @@ void AppDbNvmStoreChangeAwareState(appDbHdl_t hdl); * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreCsfRecord(appDbHdl_t hdl); +bool_t AppDbNvmStoreCsfRecord(appDbHdl_t hdl); /*************************************************************************************************/ /*! @@ -603,7 +603,7 @@ void AppDbNvmStoreCsfRecord(appDbHdl_t hdl); * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreCacheByHash(appDbHdl_t hdl); +bool_t AppDbNvmStoreCacheByHash(appDbHdl_t hdl); /*************************************************************************************************/ /*! @@ -614,7 +614,7 @@ void AppDbNvmStoreCacheByHash(appDbHdl_t hdl); * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreDbHash(appDbHdl_t hdl); +bool_t AppDbNvmStoreDbHash(appDbHdl_t hdl); /*************************************************************************************************/ /*! @@ -625,7 +625,7 @@ void AppDbNvmStoreDbHash(appDbHdl_t hdl); * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreBond(appDbHdl_t hdl); +bool_t AppDbNvmStoreBond(appDbHdl_t hdl); /*************************************************************************************************/ /*! diff --git a/Libraries/Cordio/ble-profiles/sources/af/common/app_db.c b/Libraries/Cordio/ble-profiles/sources/af/common/app_db.c index 789f4c9bac2..d954e1bcebe 100644 --- a/Libraries/Cordio/ble-profiles/sources/af/common/app_db.c +++ b/Libraries/Cordio/ble-profiles/sources/af/common/app_db.c @@ -953,7 +953,7 @@ void AppDbSetPeerRpao(appDbHdl_t hdl, bool_t peerRpao) * \return None. */ /*************************************************************************************************/ -void AppDbNvmStorePeerRpao(appDbHdl_t hdl) +bool_t AppDbNvmStorePeerRpao(appDbHdl_t hdl) { uint8_t recIndex = appDbFindIndx(hdl); @@ -962,7 +962,11 @@ void AppDbNvmStorePeerRpao(appDbHdl_t hdl) appDbRec_t *pRec = &appDb.rec[recIndex]; uint32_t nvmId = DBNV_ID(APP_DB_NVM_PEER_RAPO_ID, recIndex); - WsfNvmWriteData(nvmId, &pRec->peerRpao, sizeof(bool_t), NULL); + return WsfNvmWriteData(nvmId, &pRec->peerRpao, sizeof(bool_t), NULL); + }else + { + APP_TRACE_INFO0("Invalid record index!"); + return FALSE; } } @@ -975,16 +979,20 @@ void AppDbNvmStorePeerRpao(appDbHdl_t hdl) * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreCccTbl(appDbHdl_t hdl) +bool_t AppDbNvmStoreCccTbl(appDbHdl_t hdl) { uint8_t recIndex = appDbFindIndx(hdl); - + if (recIndex != APP_DB_INDEX_INVALID) { appDbRec_t *pRec = &appDb.rec[recIndex]; uint32_t nvmId = DBNV_ID(APP_DB_NVM_CCC_TBL_ID, recIndex); - WsfNvmWriteData(nvmId, (uint8_t*) &pRec->cccTbl, sizeof(uint16_t) * APP_DB_NUM_CCCD, NULL); + return WsfNvmWriteData(nvmId, (uint8_t*) &pRec->cccTbl, sizeof(uint16_t) * APP_DB_NUM_CCCD, NULL); + }else + { + APP_TRACE_INFO0("Invalid record index!"); + return FALSE; } } @@ -997,7 +1005,7 @@ void AppDbNvmStoreCccTbl(appDbHdl_t hdl) * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreHdlList(appDbHdl_t hdl) +bool_t AppDbNvmStoreHdlList(appDbHdl_t hdl) { uint8_t recIndex = appDbFindIndx(hdl); @@ -1006,11 +1014,18 @@ void AppDbNvmStoreHdlList(appDbHdl_t hdl) appDbRec_t *pRec = &appDb.rec[recIndex]; uint32_t nvmId = DBNV_ID(APP_DB_NVM_HDL_LIST_ID, recIndex); - WsfNvmWriteData(nvmId, (uint8_t*) &pRec->hdlList, sizeof(uint16_t) * APP_DB_HDL_LIST_LEN, NULL); + bool_t writeOk = WsfNvmWriteData(nvmId, (uint8_t*) &pRec->hdlList, sizeof(uint16_t) * APP_DB_HDL_LIST_LEN, NULL); + if(!writeOk){ + return FALSE; + } nvmId = DBNV_ID(APP_DB_NVM_DISC_STATUS_ID, recIndex); - WsfNvmWriteData(nvmId, &pRec->discStatus, sizeof(uint8_t), NULL); + return WsfNvmWriteData(nvmId, &pRec->discStatus, sizeof(uint8_t), NULL); + }else + { + APP_TRACE_INFO0("Invalid record index!"); + return FALSE; } } @@ -1023,7 +1038,7 @@ void AppDbNvmStoreHdlList(appDbHdl_t hdl) * \return None. */ /*************************************************************************************************/ -void AppDbNvmStorePeerSignCounter(appDbHdl_t hdl) +bool_t AppDbNvmStorePeerSignCounter(appDbHdl_t hdl) { uint8_t recIndex = appDbFindIndx(hdl); @@ -1032,7 +1047,12 @@ void AppDbNvmStorePeerSignCounter(appDbHdl_t hdl) appDbRec_t *pRec = &appDb.rec[recIndex]; uint32_t nvmId = DBNV_ID(APP_DB_NVM_PEER_SIGN_CTR_ID, recIndex); - WsfNvmWriteData(nvmId, (uint8_t*) &pRec->peerSignCounter, sizeof(uint32_t), NULL); + return WsfNvmWriteData(nvmId, (uint8_t*) &pRec->peerSignCounter, sizeof(uint32_t), NULL); + } + else + { + APP_TRACE_INFO0("Invalid record index!"); + return FALSE; } } @@ -1045,7 +1065,7 @@ void AppDbNvmStorePeerSignCounter(appDbHdl_t hdl) * \return None. */ /*************************************************************************************************/ -void AppDbNvmStorePeerAddrRes(appDbHdl_t hdl) +bool_t AppDbNvmStorePeerAddrRes(appDbHdl_t hdl) { uint8_t recIndex = appDbFindIndx(hdl); @@ -1054,7 +1074,12 @@ void AppDbNvmStorePeerAddrRes(appDbHdl_t hdl) appDbRec_t *pRec = &appDb.rec[recIndex]; uint32_t nvmId = DBNV_ID(APP_DB_NVM_PEER_ADDR_RES_ID, recIndex); - WsfNvmWriteData(nvmId, (uint8_t*) &pRec->peerAddrRes, sizeof(bool_t), NULL); + return WsfNvmWriteData(nvmId, (uint8_t*) &pRec->peerAddrRes, sizeof(bool_t), NULL); + } + else + { + APP_TRACE_INFO0("Invalid record index!"); + return FALSE; } } @@ -1067,7 +1092,7 @@ void AppDbNvmStorePeerAddrRes(appDbHdl_t hdl) * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreChangeAwareState(appDbHdl_t hdl) +bool_t AppDbNvmStoreChangeAwareState(appDbHdl_t hdl) { uint8_t recIndex = appDbFindIndx(hdl); @@ -1076,7 +1101,11 @@ void AppDbNvmStoreChangeAwareState(appDbHdl_t hdl) appDbRec_t *pRec = &appDb.rec[recIndex]; uint32_t nvmId = DBNV_ID(APP_DB_NVM_CAS_ID, recIndex); - WsfNvmWriteData(nvmId, (uint8_t*) &pRec->changeAwareState, sizeof(uint8_t), NULL); + return WsfNvmWriteData(nvmId, (uint8_t*) &pRec->changeAwareState, sizeof(uint8_t), NULL); + }else + { + APP_TRACE_INFO0("Invalid record index!"); + return FALSE; } } @@ -1089,7 +1118,7 @@ void AppDbNvmStoreChangeAwareState(appDbHdl_t hdl) * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreCsfRecord(appDbHdl_t hdl) +bool_t AppDbNvmStoreCsfRecord(appDbHdl_t hdl) { uint8_t recIndex = appDbFindIndx(hdl); @@ -1098,7 +1127,12 @@ void AppDbNvmStoreCsfRecord(appDbHdl_t hdl) appDbRec_t *pRec = &appDb.rec[recIndex]; uint32_t nvmId = DBNV_ID(APP_DB_NVM_CSF_ID, recIndex); - WsfNvmWriteData(nvmId, (uint8_t*) pRec->csf, ATT_CSF_LEN, NULL); + return WsfNvmWriteData(nvmId, (uint8_t*) pRec->csf, ATT_CSF_LEN, NULL); + } + else + { + APP_TRACE_INFO0("Invalid record index!"); + return FALSE; } } @@ -1111,7 +1145,7 @@ void AppDbNvmStoreCsfRecord(appDbHdl_t hdl) * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreCacheByHash(appDbHdl_t hdl) +bool_t AppDbNvmStoreCacheByHash(appDbHdl_t hdl) { uint8_t recIndex = appDbFindIndx(hdl); @@ -1120,7 +1154,12 @@ void AppDbNvmStoreCacheByHash(appDbHdl_t hdl) appDbRec_t *pRec = &appDb.rec[recIndex]; uint32_t nvmId = DBNV_ID(APP_DB_NVM_CACHE_HASH_ID, recIndex); - WsfNvmWriteData(nvmId, (uint8_t*) &pRec->cacheByHash, sizeof(bool_t), NULL); + return WsfNvmWriteData(nvmId, (uint8_t*) &pRec->cacheByHash, sizeof(bool_t), NULL); + } + else + { + APP_TRACE_INFO0("Invalid record index!"); + return FALSE; } } @@ -1133,7 +1172,7 @@ void AppDbNvmStoreCacheByHash(appDbHdl_t hdl) * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreDbHash(appDbHdl_t hdl) +bool_t AppDbNvmStoreDbHash(appDbHdl_t hdl) { uint8_t recIndex = appDbFindIndx(hdl); @@ -1142,7 +1181,12 @@ void AppDbNvmStoreDbHash(appDbHdl_t hdl) appDbRec_t *pRec = &appDb.rec[recIndex]; uint32_t nvmId = DBNV_ID(APP_DB_NVM_HASH_ID, recIndex); - WsfNvmWriteData(nvmId, pRec->dbHash, ATT_DATABASE_HASH_LEN, NULL); + return WsfNvmWriteData(nvmId, pRec->dbHash, ATT_DATABASE_HASH_LEN, NULL); + } + else + { + APP_TRACE_INFO0("Invalid record index!"); + return FALSE; } } @@ -1155,7 +1199,7 @@ void AppDbNvmStoreDbHash(appDbHdl_t hdl) * \return None. */ /*************************************************************************************************/ -void AppDbNvmStoreBond(appDbHdl_t hdl) +bool_t AppDbNvmStoreBond(appDbHdl_t hdl) { uint8_t i = appDbFindIndx(hdl); @@ -1169,41 +1213,82 @@ void AppDbNvmStoreBond(appDbHdl_t hdl) /* Protect against corrupt bond state due to incomplete writes (power failure, crash, etc.). */ /* - First ensure valid FALSE before writing parameters. */ - WsfNvmWriteData(DBNV_ID(APP_DB_NVM_VALID_ID, i), &valid, sizeof(bool_t), NULL); + bool_t writeOk = WsfNvmWriteData(DBNV_ID(APP_DB_NVM_VALID_ID, i), &valid, sizeof(bool_t), NULL); + + if(!writeOk){ + return FALSE; + } + /* Write record parameters. */ - WsfNvmWriteData(DBNV_ID(APP_DB_NVM_KV_MASK_ID, i), &pRec->keyValidMask, sizeof(uint8_t), NULL); + writeOk = WsfNvmWriteData(DBNV_ID(APP_DB_NVM_KV_MASK_ID, i), &pRec->keyValidMask, sizeof(uint8_t), NULL); + if(!writeOk){ + return FALSE; + } if (pRec->keyValidMask & DM_KEY_LOCAL_LTK) { - WsfNvmWriteData(DBNV_ID(APP_DB_NVM_LOCAL_LTK_ID, i), (uint8_t*) &pRec->localLtk, sizeof(dmSecLtk_t), NULL); - WsfNvmWriteData(DBNV_ID(APP_DB_NVM_LOCAL_SEC_LVL_ID, i), &pRec->localLtkSecLevel, sizeof(uint8_t), NULL); + writeOk = WsfNvmWriteData(DBNV_ID(APP_DB_NVM_LOCAL_LTK_ID, i), (uint8_t*) &pRec->localLtk, sizeof(dmSecLtk_t), NULL); + if(!writeOk){ + return FALSE; + } + writeOk = WsfNvmWriteData(DBNV_ID(APP_DB_NVM_LOCAL_SEC_LVL_ID, i), &pRec->localLtkSecLevel, sizeof(uint8_t), NULL); + if(!writeOk){ + return FALSE; + } } if (pRec->keyValidMask & DM_KEY_PEER_LTK) { - WsfNvmWriteData(DBNV_ID(APP_DB_NVM_PEER_LTK_ID, i), (uint8_t*) &pRec->peerLtk, sizeof(dmSecLtk_t), NULL); - WsfNvmWriteData(DBNV_ID(APP_DB_NVM_PEER_SEC_LVL_ID, i), &pRec->peerLtkSecLevel, sizeof(uint8_t), NULL); + writeOk = WsfNvmWriteData(DBNV_ID(APP_DB_NVM_PEER_LTK_ID, i), (uint8_t*) &pRec->peerLtk, sizeof(dmSecLtk_t), NULL); + if(!writeOk){ + return FALSE; + } + writeOk = WsfNvmWriteData(DBNV_ID(APP_DB_NVM_PEER_SEC_LVL_ID, i), &pRec->peerLtkSecLevel, sizeof(uint8_t), NULL); + if(!writeOk){ + return FALSE; + } } if (pRec->keyValidMask & DM_KEY_IRK) { - WsfNvmWriteData(DBNV_ID(APP_DB_NVM_PEER_IRK_ID, i), (uint8_t*) &pRec->peerIrk, sizeof(dmSecIrk_t), NULL); + writeOk = WsfNvmWriteData(DBNV_ID(APP_DB_NVM_PEER_IRK_ID, i), (uint8_t*) &pRec->peerIrk, sizeof(dmSecIrk_t), NULL); + if(!writeOk){ + return FALSE; + } } if (pRec->keyValidMask & DM_KEY_CSRK) { - WsfNvmWriteData(DBNV_ID(APP_DB_NVM_PEER_CSRK_ID, i), (uint8_t*) &pRec->peerCsrk, sizeof(dmSecCsrk_t), NULL); + writeOk = WsfNvmWriteData(DBNV_ID(APP_DB_NVM_PEER_CSRK_ID, i), (uint8_t*) &pRec->peerCsrk, sizeof(dmSecCsrk_t), NULL); + if(!writeOk){ + return FALSE; + } } - WsfNvmWriteData(DBNV_ID(APP_DB_NVM_PEER_ADDR_ID, i), pRec->peerAddr, sizeof(bdAddr_t), NULL); - WsfNvmWriteData(DBNV_ID(APP_DB_NVM_ADDR_TYPE_ID, i), &pRec->addrType, sizeof(uint8_t), NULL); - WsfNvmWriteData(DBNV_ID(APP_DB_NVM_CACHE_HASH_ID, i), &pRec->cacheByHash, sizeof(bool_t), NULL); - + writeOk = WsfNvmWriteData(DBNV_ID(APP_DB_NVM_PEER_ADDR_ID, i), pRec->peerAddr, sizeof(bdAddr_t), NULL); + if(!writeOk){ + return FALSE; + } + writeOk = WsfNvmWriteData(DBNV_ID(APP_DB_NVM_ADDR_TYPE_ID, i), &pRec->addrType, sizeof(uint8_t), NULL); + if(!writeOk){ + return FALSE; + } + writeOk = WsfNvmWriteData(DBNV_ID(APP_DB_NVM_CACHE_HASH_ID, i), &pRec->cacheByHash, sizeof(bool_t), NULL); + if(!writeOk){ + return FALSE; + } /* Protect against corrupt bond state due to incomplete writes (power failure, crash, etc.). */ /* - Second set valid TRUE after writing parameters. */ - WsfNvmWriteData(DBNV_ID(APP_DB_NVM_VALID_ID, i), &pRec->valid, sizeof(bool_t), NULL); + return WsfNvmWriteData(DBNV_ID(APP_DB_NVM_VALID_ID, i), &pRec->valid, sizeof(bool_t), NULL); } + + return TRUE; + } + else + { + APP_TRACE_INFO0("Invalid record index!"); + return FALSE; } } diff --git a/Libraries/Cordio/platform/targets/maxim/max32655/sources/pal_flash.c b/Libraries/Cordio/platform/targets/maxim/max32655/sources/pal_flash.c index 784502229ca..838864c326a 100644 --- a/Libraries/Cordio/platform/targets/maxim/max32655/sources/pal_flash.c +++ b/Libraries/Cordio/platform/targets/maxim/max32655/sources/pal_flash.c @@ -121,7 +121,7 @@ PalFlashState_t PalNvmGetState(void) /*************************************************************************************************/ uint32_t PalNvmGetTotalSize(void) { - return &__pal_nvm_db_end__ - &__pal_nvm_db_start__; + return (uint32_t)&__pal_nvm_db_end__ - (uint32_t)&__pal_nvm_db_start__; } /*************************************************************************************************/ @@ -265,4 +265,4 @@ void PalFlashEraseChip(void) startAddr += MXC_FLASH_PAGE_SIZE; size -= MXC_FLASH_PAGE_SIZE; } -} \ No newline at end of file +} diff --git a/Libraries/Cordio/platform/targets/maxim/max32665/sources/pal_flash.c b/Libraries/Cordio/platform/targets/maxim/max32665/sources/pal_flash.c index a442d0d6122..3710fb72c27 100644 --- a/Libraries/Cordio/platform/targets/maxim/max32665/sources/pal_flash.c +++ b/Libraries/Cordio/platform/targets/maxim/max32665/sources/pal_flash.c @@ -120,7 +120,7 @@ PalFlashState_t PalNvmGetState(void) /*************************************************************************************************/ uint32_t PalNvmGetTotalSize(void) { - return &__pal_nvm_db_end__ - &__pal_nvm_db_start__; + return (uint32_t)&__pal_nvm_db_end__ - (uint32_t)&__pal_nvm_db_start__; } /*************************************************************************************************/ diff --git a/Libraries/Cordio/wsf/include/wsf_nvm.h b/Libraries/Cordio/wsf/include/wsf_nvm.h index f058f0bc774..4d0cd340675 100644 --- a/Libraries/Cordio/wsf/include/wsf_nvm.h +++ b/Libraries/Cordio/wsf/include/wsf_nvm.h @@ -57,6 +57,21 @@ static inline uint64_t WsfNvmConvertChar8to64Bit(char *charId) } return retValue; } +/*************************************************************************************************/ +/*! + * \brief Get remaining space in WSF Allcoated NVM. + * \return Bytes left. + */ +/*************************************************************************************************/ +uint32_t WsfNvmGetRemainingSpace(void); + +/*************************************************************************************************/ +/*! + * \brief Check if NVM space is full. + * \return TRUE if all NVM space is taken, FALSE otherwise. + */ +/*************************************************************************************************/ +bool_t WsfNvmIsFull(void); /*************************************************************************************************/ /*! @@ -116,6 +131,18 @@ bool_t WsfNvmEraseData(uint64_t id, WsfNvmCompEvent_t compCback); /*************************************************************************************************/ void WsfNvmEraseDataAll(WsfNvmCompEvent_t compCback); +/*************************************************************************************************/ +/*! + * \brief Defragment NVM + * \param copyBuf Scratch buffer to temporarily copy NVM stored data. + * \param size Size of copyBuf in bytes. + * \return TRUE if defragment successful. FALSE otherwise + * + * \note Defragment should only be called when the storage is full and a record has been invalidated. + * Furthermore, copyBuf must be at least the size of WSF NVM allocated flash. + */ +/*************************************************************************************************/ +bool_t WsfNvmDefragment(uint8_t *copyBuf, uint32_t size); /*! \} */ /* WSF_NVM_API */ #ifdef __cplusplus diff --git a/Libraries/Cordio/wsf/sources/targets/baremetal/wsf_nvm.c b/Libraries/Cordio/wsf/sources/targets/baremetal/wsf_nvm.c index 0fa1fc6b5f7..7fbbdeb4cdf 100644 --- a/Libraries/Cordio/wsf/sources/targets/baremetal/wsf_nvm.c +++ b/Libraries/Cordio/wsf/sources/targets/baremetal/wsf_nvm.c @@ -21,59 +21,98 @@ * limitations under the License. */ /*************************************************************************************************/ +#include +#include +#include "wsf_cs.h" #include "wsf_types.h" #include "wsf_nvm.h" #include "wsf_assert.h" #include "pal_flash.h" #include "util/crc32.h" +#include "wsf_buf.h" +#include "wsf_queue.h" +#include "mxc_device.h" /************************************************************************************************** Macros **************************************************************************************************/ /*! NVM data start address. */ -#define WSF_NVM_START_ADDR 0x0000 +#define WSF_NVM_START_ADDR 0x0000 /*! Reserved filecode. */ -#define WSF_NVM_RESERVED_FILECODE ((uint64_t)0) +#define WSF_NVM_RESERVED_FILECODE ((uint64_t)0) /* Unused (erased) filecode. */ /* TODO: May depend on flash type */ -#define WSF_NVM_UNUSED_FILECODE ((uint64_t)0xFFFFFFFFFFFFFFFF) +#define WSF_NVM_UNUSED_FILECODE ((uint64_t)0xFFFFFFFFFFFFFFFF) /*! Flash word size. */ -#define WSF_FLASH_WORD_SIZE 4 +#define WSF_FLASH_WORD_SIZE 4 /*! Align value to word boundary. */ -#define WSF_NVM_WORD_ALIGN(x) (((x) + (WSF_FLASH_WORD_SIZE - 1)) & \ - ~(WSF_FLASH_WORD_SIZE - 1)) +#define WSF_NVM_WORD_ALIGN(x) (((x) + (WSF_FLASH_WORD_SIZE - 1)) & ~(WSF_FLASH_WORD_SIZE - 1)) -#define WSF_NVM_CRC_INIT_VALUE 0xFEDCBA98 +#define WSF_NVM_CRC_INIT_VALUE 0xFEDCBA98 /************************************************************************************************** Data Types **************************************************************************************************/ /*! \brief Header. */ -typedef struct -{ - uint64_t id; /*!< Stored data ID. */ - uint32_t len; /*!< Stored data length. */ - uint32_t headerCrc; /*!< CRC of this header. */ - uint32_t dataCrc; /*!< CRC of subsequent data. */ + +typedef struct { + uint64_t id; /*!< Stored data ID. */ + uint32_t len; /*!< Stored data length. */ + uint32_t headerCrc; /*!< CRC of this header. */ + uint32_t dataCrc; /*!< CRC of subsequent data. */ } WsfNvmHeader_t; -static struct -{ - uint32_t availAddr; /*!< Next available address for NVM write. */ - uint32_t sectorSize; /*!< Size of erase sector. */ - uint32_t totalSize; /*!< Total size of NVM storage. */ +#define WSF_NVM_HEADER_SIZE sizeof(WsfNvmHeader_t) +#define WSF_NVM_FILE_SIZE(header_len) (WSF_NVM_HEADER_SIZE + WSF_NVM_WORD_ALIGN(header_len)) +static struct { + uint32_t availAddr; /*!< Next available address for NVM write. */ + uint32_t sectorSize; /*!< Size of erase sector. */ + uint32_t totalSize; /*!< Total size of NVM storage. */ } wsfNvmCb; +extern uint32_t __pal_nvm_db_start__, __pal_nvm_db_end__; + /************************************************************************************************** Global Functions **************************************************************************************************/ +/*************************************************************************************************/ +/*! + * \brief Get remaining space in WSF Allcoated NVM. + * \return Bytes left. + */ +/*************************************************************************************************/ +uint32_t WsfNvmGetRemainingSpace(void) +{ + const int32_t bytesLeft = wsfNvmCb.totalSize - (wsfNvmCb.availAddr - WSF_NVM_START_ADDR); + + + WSF_ASSERT(bytesLeft >= 0); + + if(bytesLeft < 0) + { + return 0; + } + + + return (uint32_t)bytesLeft; +} +/*************************************************************************************************/ +/*! + * \brief Check if NVM space is full. + * \return TRUE if all NVM space is taken, FALSE otherwise. + */ +/*************************************************************************************************/ +bool_t WsfNvmIsFull(void) +{ + return WsfNvmGetRemainingSpace() <= 0; +} /*************************************************************************************************/ /*! @@ -82,80 +121,75 @@ static struct /*************************************************************************************************/ void WsfNvmInit(void) { - PalFlashInit(NULL); - wsfNvmCb.totalSize = PalNvmGetTotalSize(); - wsfNvmCb.sectorSize = PalNvmGetSectorSize(); - - WsfNvmHeader_t header; - uint32_t storageAddr = WSF_NVM_START_ADDR; - uint32_t headerCrc; - bool_t corruptData = FALSE; - - do - { - /* Read header. */ - PalFlashRead(&header, sizeof(header), storageAddr); + PalFlashInit(NULL); + wsfNvmCb.totalSize = PalNvmGetTotalSize(); + wsfNvmCb.sectorSize = PalNvmGetSectorSize(); + + WsfNvmHeader_t header; + uint32_t storageAddr = WSF_NVM_START_ADDR; + uint32_t headerCrc; + bool_t corruptData = FALSE; + + do { + /* Read header. */ + PalFlashRead(&header, sizeof(header), storageAddr); + + if (header.id == WSF_NVM_UNUSED_FILECODE) { + /* Found unused entry at end of used storage. */ + break; + } - if (header.id == WSF_NVM_UNUSED_FILECODE) - { - /* Found unused entry at end of used storage. */ - break; - } + /* Iterate through stored data headers, looking for existing matching stored data header. */ + if (header.id != WSF_NVM_RESERVED_FILECODE) { + /* Calculate CRC of header itself. */ + headerCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, sizeof(header.id) + sizeof(header.len), + (uint8_t *)&header); + + if (headerCrc != header.headerCrc) { + /* Corrupt header. */ + corruptData = TRUE; + break; + } + } else { + if ((header.headerCrc != 0) || (header.dataCrc != 0)) { + /* Corrupt header. */ + corruptData = TRUE; + break; + } + } - /* Iterate through stored data headers, looking for existing matching stored data header. */ - if (header.id != WSF_NVM_RESERVED_FILECODE) - { - /* Calculate CRC of header itself. */ - headerCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, sizeof(header.id) + sizeof(header.len), - (uint8_t *)&header); - - if (headerCrc != header.headerCrc) - { - /* Corrupt header. */ - corruptData = TRUE; - break; - } - } - else - { - if ((header.headerCrc != 0) || (header.dataCrc !=0)) - { - /* Corrupt header. */ - corruptData = TRUE; - break; - } - } + /* Move to next stored data block and read header. */ + storageAddr += WSF_NVM_WORD_ALIGN(header.len) + sizeof(header); - /* Move to next stored data block and read header. */ - storageAddr += WSF_NVM_WORD_ALIGN(header.len) + sizeof(header); - WSF_ASSERT((storageAddr - WSF_NVM_START_ADDR) < wsfNvmCb.totalSize); + if (!((storageAddr - WSF_NVM_START_ADDR) < wsfNvmCb.totalSize)) { + WSF_TRACE_INFO0("WSF -> NVM IS FULL!"); + storageAddr = WSF_NVM_START_ADDR + wsfNvmCb.totalSize; + break; + } - } while ((storageAddr - WSF_NVM_START_ADDR) < wsfNvmCb.totalSize); + } while ((storageAddr - WSF_NVM_START_ADDR) < wsfNvmCb.totalSize); - wsfNvmCb.availAddr = storageAddr; + wsfNvmCb.availAddr = storageAddr; - /* Check for corrupt data. */ - if (corruptData == TRUE) - { - /* Search for the first available location */ - while ((storageAddr - WSF_NVM_START_ADDR) < wsfNvmCb.totalSize) - { - PalFlashRead(&header.id, sizeof(header.id), storageAddr); + /* Check for corrupt data. */ + if (corruptData == TRUE) { + /* Search for the first available location */ + while ((storageAddr - WSF_NVM_START_ADDR) < wsfNvmCb.totalSize) { + PalFlashRead(&header.id, sizeof(header.id), storageAddr); - if (header.id == WSF_NVM_UNUSED_FILECODE) - { - break; - } + if (header.id == WSF_NVM_UNUSED_FILECODE) { + break; + } - storageAddr += sizeof(header.id); - } + storageAddr += sizeof(header.id); + } - /* Update the address of the first available location. align to sector boundary. */ - wsfNvmCb.availAddr = (storageAddr + wsfNvmCb.sectorSize - 1) & ~(wsfNvmCb.sectorSize - 1); + /* Update the address of the first available location. align to sector boundary. */ + wsfNvmCb.availAddr = (storageAddr + wsfNvmCb.sectorSize - 1) & ~(wsfNvmCb.sectorSize - 1); - /* Erase all data. */ - WsfNvmEraseDataAll(NULL); - } + /* Erase all data. */ + WsfNvmEraseDataAll(NULL); + } } /*************************************************************************************************/ @@ -172,63 +206,72 @@ void WsfNvmInit(void) /*************************************************************************************************/ bool_t WsfNvmReadData(uint64_t id, uint8_t *pData, uint16_t len, WsfNvmCompEvent_t compCback) { - WsfNvmHeader_t header; - uint32_t headerCrc, dataCrc; - uint32_t storageAddr = WSF_NVM_START_ADDR; - bool_t findId = FALSE; + WsfNvmHeader_t header; + uint32_t headerCrc, dataCrc; + uint32_t storageAddr = WSF_NVM_START_ADDR; + bool_t findId = FALSE; - WSF_ASSERT(!((id == WSF_NVM_RESERVED_FILECODE) || (id == WSF_NVM_UNUSED_FILECODE))); + WSF_ASSERT(!((id == WSF_NVM_RESERVED_FILECODE) || (id == WSF_NVM_UNUSED_FILECODE))); - /* Read first header. */ - PalFlashRead(&header, sizeof(header), storageAddr); + /* Read first header. */ + PalFlashRead(&header, sizeof(header), storageAddr); - do - { - if (header.id == WSF_NVM_UNUSED_FILECODE) - { - /* Found unused entry at end of used storage. */ - break; - } + do { + if (header.id == WSF_NVM_UNUSED_FILECODE) { + /* Found unused entry at end of used storage. */ + break; + } - /* Iterate through stored data headers, looking for existing matching stored data header. */ - if (header.id != WSF_NVM_RESERVED_FILECODE) - { - /* Calculate CRC of header itself. */ - headerCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, sizeof(header.id) + sizeof(header.len), - (uint8_t *)&header); - - if (headerCrc != header.headerCrc) - { - /* Corrupt header. */ - /* TODO: Catastrophic failure? */ - break; - } - else if ((header.id == id) && (header.len == len)) - { - /* Valid header and matching ID - read data after header. */ - storageAddr += sizeof(header); - PalFlashRead(pData, header.len, storageAddr); - dataCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, header.len, pData); - if (dataCrc == header.dataCrc) - { - findId = TRUE; + /* Iterate through stored data headers, looking for existing matching stored data header. */ + if (header.id != WSF_NVM_RESERVED_FILECODE) { + /* Calculate CRC of header itself. */ + headerCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, sizeof(header.id) + sizeof(header.len), + (uint8_t *)&header); + + if (headerCrc != header.headerCrc) { + /* Corrupt header. */ + /* TODO: Catastrophic failure? */ + break; + } else if ((header.id == id) && (header.len == len)) { + /* Valid header and matching ID - read data after header. */ + storageAddr += sizeof(header); + PalFlashRead(pData, header.len, storageAddr); + dataCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, header.len, pData); + if (dataCrc == header.dataCrc) { + findId = TRUE; + } + break; + } } - break; - } - } - /* Move to next stored data block and read header. */ - storageAddr += WSF_NVM_WORD_ALIGN(header.len) + sizeof(header); - PalFlashRead(&header, sizeof(header), storageAddr); - } while(1); + /* Move to next stored data block and read header. */ + storageAddr += WSF_NVM_WORD_ALIGN(header.len) + sizeof(header); + PalFlashRead(&header, sizeof(header), storageAddr); + } while (1); - if (compCback) - { - compCback(findId); - } - return findId; + if (compCback) { + compCback(findId); + } + return findId; } +/*************************************************************************************************/ +/*! + * \brief Check whether or not the amount of data has space to fit into NVM + * + * \param lenNeeded Number of bytes wanting to store + * + * \return TRUE if NVM operation is successful, FALSE otherwise. + */ +/*************************************************************************************************/ +static inline bool_t wsfNvmHaveEnoughSpace(uint32_t lenNeeded) +{ + if (WSF_NVM_FILE_SIZE(lenNeeded) > WsfNvmGetRemainingSpace()) { + return FALSE; + } else { + return TRUE; + } +} /*************************************************************************************************/ /*! * \brief Write data. @@ -243,88 +286,81 @@ bool_t WsfNvmReadData(uint64_t id, uint8_t *pData, uint16_t len, WsfNvmCompEvent /*************************************************************************************************/ bool_t WsfNvmWriteData(uint64_t id, const uint8_t *pData, uint16_t len, WsfNvmCompEvent_t compCback) { - WsfNvmHeader_t header; - uint32_t headerCrc, dataCrc; - uint32_t storageAddr = WSF_NVM_START_ADDR; + WsfNvmHeader_t header; + uint32_t headerCrc, dataCrc; + uint32_t storageAddr = WSF_NVM_START_ADDR; - if(wsfNvmCb.totalSize == 0) { - return FALSE; - } - - WSF_ASSERT(!((id == WSF_NVM_RESERVED_FILECODE) || (id == WSF_NVM_UNUSED_FILECODE))); - WSF_ASSERT((wsfNvmCb.availAddr - WSF_NVM_START_ADDR) <= wsfNvmCb.totalSize); + if (wsfNvmCb.totalSize == 0) { + return FALSE; + } - /* Read first header. */ - PalFlashRead(&header, sizeof(header), storageAddr); + WSF_ASSERT(!((id == WSF_NVM_RESERVED_FILECODE) || (id == WSF_NVM_UNUSED_FILECODE))); - do - { - if (header.id == WSF_NVM_UNUSED_FILECODE) - { - /* Found unused entry at end of used storage. */ - break; + if (!wsfNvmHaveEnoughSpace(len)) { + WSF_TRACE_INFO0("WsfNvm: Failed to write flash! Out of space."); + return FALSE; } - /* Iterate through stored data headers, looking for existing matching stored data header. */ - if (header.id != WSF_NVM_RESERVED_FILECODE) - { - /* Calculate CRC of header itself. */ - headerCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, sizeof(header.id) + sizeof(header.len), - (uint8_t *)&header); - - if (headerCrc != header.headerCrc) - { - /* Corrupt header. */ - /* TODO: Catastrophic failure? */ - break; - } - else if (header.id == id) - { - dataCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, len, pData); - if (dataCrc == header.dataCrc) - { - if (compCback) - { - compCback(TRUE); - } - return TRUE; + /* Read first header. */ + PalFlashRead(&header, sizeof(header), storageAddr); + + do { + if (header.id == WSF_NVM_UNUSED_FILECODE) { + /* Found unused entry at end of used storage. */ + break; } - else - { - /* Valid header and matching ID - scratch header out. */ - header.id = WSF_NVM_RESERVED_FILECODE; - header.headerCrc = 0; - header.dataCrc = 0; - PalFlashWrite(&header, sizeof(header), storageAddr); + + /* Iterate through stored data headers, looking for existing matching stored data header. */ + if (header.id != WSF_NVM_RESERVED_FILECODE) { + /* Calculate CRC of header itself. */ + headerCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, sizeof(header.id) + sizeof(header.len), + (uint8_t *)&header); + + if (headerCrc != header.headerCrc) { + /* Corrupt header. */ + /* TODO: Catastrophic failure? */ + break; + } else if (header.id == id) { + dataCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, len, pData); + if (dataCrc == header.dataCrc) { + if (compCback) { + compCback(TRUE); + } + return TRUE; + } else { + /* Valid header and matching ID - scratch header out. */ + header.id = WSF_NVM_RESERVED_FILECODE; + header.headerCrc = 0; + header.dataCrc = 0; + PalFlashWrite(&header, sizeof(header), storageAddr); + } + } } - } - } - /* Move to next stored data block and read header. */ - storageAddr += WSF_NVM_WORD_ALIGN(header.len) + sizeof(header); - PalFlashRead(&header, sizeof(header), storageAddr); - } while(1); + /* Move to next stored data block and read header. */ + storageAddr += WSF_NVM_WORD_ALIGN(header.len) + sizeof(header); + PalFlashRead(&header, sizeof(header), storageAddr); + } while (1); - /* After cycling through all headers, create a new stored data header and store data */ - header.id = id; - header.len = len; - header.headerCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, sizeof(header.id) + sizeof(header.len), - (uint8_t *)&header); - header.dataCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, len, pData); + /* After cycling through all headers, create a new stored data header and store data */ + header.id = id; + header.len = len; + header.headerCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, sizeof(header.id) + sizeof(header.len), + (uint8_t *)&header); + header.dataCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, len, pData); - PalFlashWrite(&header, sizeof(header), storageAddr); - PalFlashWrite((void *)pData, len, storageAddr + sizeof(header)); + PalFlashWrite(&header, sizeof(header), storageAddr); + PalFlashWrite((void *)pData, len, storageAddr + sizeof(header)); - /* Move to next empty flash. */ - storageAddr += WSF_NVM_WORD_ALIGN(header.len) + sizeof(header); - wsfNvmCb.availAddr = storageAddr; + /* Move to next empty flash. */ + storageAddr += WSF_NVM_WORD_ALIGN(header.len) + sizeof(header); + wsfNvmCb.availAddr = storageAddr; - if (compCback) - { - compCback((wsfNvmCb.availAddr - WSF_NVM_START_ADDR) <= wsfNvmCb.totalSize); - } + if (compCback) { + compCback((wsfNvmCb.availAddr - WSF_NVM_START_ADDR) <= wsfNvmCb.totalSize); + } - return TRUE; + return TRUE; } /*************************************************************************************************/ @@ -339,56 +375,50 @@ bool_t WsfNvmWriteData(uint64_t id, const uint8_t *pData, uint16_t len, WsfNvmCo /*************************************************************************************************/ bool_t WsfNvmEraseData(uint64_t id, WsfNvmCompEvent_t compCback) { - WsfNvmHeader_t header; - uint32_t headerCrc; - uint32_t storageAddr = WSF_NVM_START_ADDR; - bool_t erased = FALSE; + WsfNvmHeader_t header; + uint32_t headerCrc; + uint32_t storageAddr = WSF_NVM_START_ADDR; + bool_t erased = FALSE; - WSF_ASSERT(!((id == WSF_NVM_RESERVED_FILECODE) || (id == WSF_NVM_UNUSED_FILECODE))); + WSF_ASSERT(!((id == WSF_NVM_RESERVED_FILECODE) || (id == WSF_NVM_UNUSED_FILECODE))); - /* Read first header. */ - PalFlashRead(&header, sizeof(header), storageAddr); + /* Read first header. */ + PalFlashRead(&header, sizeof(header), storageAddr); - do - { - if (header.id == WSF_NVM_UNUSED_FILECODE) - { - /* Found unused entry at end of used storage. */ - break; - } + do { + if (header.id == WSF_NVM_UNUSED_FILECODE) { + /* Found unused entry at end of used storage. */ + break; + } - /* Iterate through stored data headers, looking for existing matching stored data header. */ - if (header.id != WSF_NVM_RESERVED_FILECODE) - { - headerCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, sizeof(header.id) + sizeof(header.len), - (uint8_t *)&header); - - if (headerCrc != header.headerCrc) - { - /* Corrupt header. */ - /* TODO: Catastrophic failure? */ - break; - } - else if (header.id == id) - { - header.id = WSF_NVM_RESERVED_FILECODE; - header.headerCrc = 0; - header.dataCrc = 0; - PalFlashWrite(&header, sizeof(header), storageAddr); - erased = TRUE; - } - } + /* Iterate through stored data headers, looking for existing matching stored data header. */ + if (header.id != WSF_NVM_RESERVED_FILECODE) { + headerCrc = CalcCrc32(WSF_NVM_CRC_INIT_VALUE, sizeof(header.id) + sizeof(header.len), + (uint8_t *)&header); + + if (headerCrc != header.headerCrc) { + /* Corrupt header. */ + /* TODO: Catastrophic failure? */ + break; + } else if (header.id == id) { + header.id = WSF_NVM_RESERVED_FILECODE; + header.headerCrc = 0; + header.dataCrc = 0; + PalFlashWrite(&header, sizeof(header), storageAddr); + erased = TRUE; + break; + } + } - /* Move to next stored data block and read header. */ - storageAddr += WSF_NVM_WORD_ALIGN(header.len) + sizeof(header); - PalFlashRead(&header, sizeof(header), storageAddr); - } while(1); + /* Move to next stored data block and read header. */ + storageAddr += WSF_NVM_WORD_ALIGN(header.len) + sizeof(header); + PalFlashRead(&header, sizeof(header), storageAddr); + } while (1); - if (compCback) - { - compCback(erased); - } - return erased; + if (compCback) { + compCback(erased); + } + return erased; } /*************************************************************************************************/ @@ -402,14 +432,82 @@ bool_t WsfNvmEraseData(uint64_t id, WsfNvmCompEvent_t compCback) /*************************************************************************************************/ void WsfNvmEraseDataAll(WsfNvmCompEvent_t compCback) { - for (uint32_t eraseAddr = WSF_NVM_START_ADDR; eraseAddr < wsfNvmCb.availAddr; eraseAddr += wsfNvmCb.sectorSize) - { - PalFlashEraseSector(wsfNvmCb.availAddr, eraseAddr); - } - wsfNvmCb.availAddr = WSF_NVM_START_ADDR; - - if (compCback) - { - compCback(TRUE); - } + for (uint32_t eraseAddr = WSF_NVM_START_ADDR; eraseAddr < wsfNvmCb.availAddr; + eraseAddr += wsfNvmCb.sectorSize) { + PalFlashEraseSector(1, eraseAddr); + } + wsfNvmCb.availAddr = WSF_NVM_START_ADDR; + + if (compCback) { + compCback(TRUE); + } +} + +/*************************************************************************************************/ +/*! + * \brief Defragment NVM + * \param copyBuf Scratch buffer to temporarily copy NVM stored data. + * \param size Size of copyBuf in bytes. + * \return TRUE if defragment successful. FALSE otherwise + * + * \note Defragment should only be called when the storage is full and a record has been invalidated. + * Furthermore, copyBuf must be at least the size of WSF NVM allocated flash. + */ +/*************************************************************************************************/ +bool_t WsfNvmDefragment(uint8_t *copyBuf, uint32_t size) +{ + + WSF_ASSERT(copyBuf && size >= wsfNvmCb.totalSize); + + if (!copyBuf || size < wsfNvmCb.totalSize) { + WSF_TRACE_INFO0("Not enough memory given to defragment NVM"); + return FALSE; + } + + + + WsfCsEnter(); + + + bool_t defragPossible = FALSE; + uint32_t currentOffset = WSF_NVM_START_ADDR; + uint32_t copyOffset = 0; + WsfNvmHeader_t header = { 0 }; + + /* Get the first header*/ + PalFlashRead(&header, WSF_NVM_HEADER_SIZE, currentOffset); + + while (currentOffset < wsfNvmCb.availAddr && header.id != WSF_NVM_UNUSED_FILECODE) { + const uint32_t fileSize = WSF_NVM_FILE_SIZE(header.len); + + /*If the entry is valid copy it into the defragmentation buffer, if its reserved we can defrag it*/ + if (header.id != WSF_NVM_RESERVED_FILECODE) { + PalFlashRead(©Buf[copyOffset], fileSize, currentOffset); + copyOffset += WSF_NVM_FILE_SIZE(header.len); + } else { + defragPossible = TRUE; + } + + currentOffset += fileSize; + + if(currentOffset + WSF_NVM_HEADER_SIZE <= WSF_NVM_START_ADDR + wsfNvmCb.totalSize) + { + PalFlashRead(&header, WSF_NVM_HEADER_SIZE, currentOffset); + } + } + + /* + If we defragged anything clear the sector and rewrite flash with defragmented data + */ + if (defragPossible) { + WsfNvmEraseDataAll(NULL); + PalFlashWrite(copyBuf, copyOffset, wsfNvmCb.availAddr); + wsfNvmCb.availAddr += copyOffset; + } else { + WSF_TRACE_INFO0("No unused memory for defragementation!"); + } + + WsfCsExit(); + + return defragPossible; } diff --git a/Libraries/Cordio/wsf/sources/targets/freertos/wsf_nvm.c b/Libraries/Cordio/wsf/sources/targets/freertos/wsf_nvm.c index 74e64bfb4d6..5c38da7dd0e 100644 --- a/Libraries/Cordio/wsf/sources/targets/freertos/wsf_nvm.c +++ b/Libraries/Cordio/wsf/sources/targets/freertos/wsf_nvm.c @@ -22,8 +22,10 @@ */ /*************************************************************************************************/ +#include #include "wsf_types.h" #include "wsf_nvm.h" +#include "wsf_cs.h" #include "wsf_assert.h" #include "pal_flash.h" #include "util/crc32.h" @@ -63,6 +65,9 @@ typedef struct { uint32_t dataCrc; /*!< CRC of subsequent data. */ } WsfNvmHeader_t; +#define WSF_NVM_HEADER_SIZE sizeof(WsfNvmHeader_t) +#define WSF_NVM_FILE_SIZE(header_len) (WSF_NVM_HEADER_SIZE + WSF_NVM_WORD_ALIGN(header_len)) + static struct { uint32_t availAddr; /*!< Next available address for NVM write. */ uint32_t sectorSize; /*!< Size of erase sector. */ @@ -72,6 +77,36 @@ static struct { /************************************************************************************************** Global Functions **************************************************************************************************/ +/*************************************************************************************************/ +/*! + * \brief Get remaining space in WSF Allcoated NVM. + * \return Bytes left. + */ +/*************************************************************************************************/ +uint32_t WsfNvmGetRemainingSpace(void) +{ + const int32_t bytesLeft = wsfNvmCb.totalSize - (wsfNvmCb.availAddr - WSF_NVM_START_ADDR); + + WSF_ASSERT(bytesLeft >= 0); + + if(bytesLeft < 0) + { + return 0; + } + + + return (uint32_t)bytesLeft; +} +/*************************************************************************************************/ +/*! + * \brief Check if NVM space is full. + * \return TRUE if all NVM space is taken, FALSE otherwise. + */ +/*************************************************************************************************/ +bool_t WsfNvmIsFull(void) +{ + return WsfNvmGetRemainingSpace() <= 0; +} /*************************************************************************************************/ /*! @@ -119,7 +154,11 @@ void WsfNvmInit(void) /* Move to next stored data block and read header. */ storageAddr += WSF_NVM_WORD_ALIGN(header.len) + sizeof(header); - WSF_ASSERT((storageAddr - WSF_NVM_START_ADDR) < wsfNvmCb.totalSize); + if (!((storageAddr - WSF_NVM_START_ADDR) < wsfNvmCb.totalSize)) { + WSF_TRACE_INFO0("WSF -> NVM IS FULL!"); + storageAddr = WSF_NVM_START_ADDR + wsfNvmCb.totalSize; + break; + } } while ((storageAddr - WSF_NVM_START_ADDR) < wsfNvmCb.totalSize); @@ -208,7 +247,23 @@ bool_t WsfNvmReadData(uint64_t id, uint8_t *pData, uint16_t len, WsfNvmCompEvent } return findId; } - +/*************************************************************************************************/ +/*! + * \brief Check whether or not the amount of data has space to fit into NVM + * + * \param lenNeeded Number of bytes wanting to store + * + * \return TRUE if NVM operation is successful, FALSE otherwise. + */ +/*************************************************************************************************/ +static inline bool_t wsfNvmHaveEnoughSpace(uint32_t lenNeeded) +{ + if (WSF_NVM_FILE_SIZE(lenNeeded) > WsfNvmGetRemainingSpace()) { + return FALSE; + } else { + return TRUE; + } +} /*************************************************************************************************/ /*! * \brief Write data. @@ -232,7 +287,11 @@ bool_t WsfNvmWriteData(uint64_t id, const uint8_t *pData, uint16_t len, WsfNvmCo } WSF_ASSERT(!((id == WSF_NVM_RESERVED_FILECODE) || (id == WSF_NVM_UNUSED_FILECODE))); - WSF_ASSERT((wsfNvmCb.availAddr - WSF_NVM_START_ADDR) <= wsfNvmCb.totalSize); + + if (!wsfNvmHaveEnoughSpace(len)) { + WSF_TRACE_INFO0("WsfNvm: Failed to write flash! Out of space."); + return FALSE; + } /* Read first header. */ PalFlashRead(&header, sizeof(header), storageAddr); @@ -373,3 +432,69 @@ void WsfNvmEraseDataAll(WsfNvmCompEvent_t compCback) compCback(TRUE); } } + +/*************************************************************************************************/ +/*! + * \brief Defragment NVM + * \param copyBuf Scratch buffer to temporarily copy NVM stored data. + * \param size Size of copyBuf in bytes. + * \return TRUE if defragment successful. FALSE otherwise + * + * \note Defragment should only be called when the storage is full and a record has been invalidated. + * Furthermore, copyBuf must be at least the size of WSF NVM allocated flash. + */ +/*************************************************************************************************/ +bool_t WsfNvmDefragment(uint8_t *copyBuf, uint32_t size) +{ + + WSF_ASSERT(copyBuf && size >= wsfNvmCb.totalSize); + + if (!copyBuf || size < wsfNvmCb.totalSize) { + WSF_TRACE_INFO0("Not enough memory given to defragment NVM"); + return FALSE; + } + + + WsfCsEnter(); + + bool_t defragPossible = FALSE; + uint32_t currentOffset = WSF_NVM_START_ADDR; + uint32_t copyOffset = 0; + WsfNvmHeader_t header = { 0 }; + + /* Get the first header*/ + PalFlashRead(&header, WSF_NVM_HEADER_SIZE, currentOffset); + while (currentOffset < wsfNvmCb.availAddr && header.id != WSF_NVM_UNUSED_FILECODE) { + const uint32_t fileSize = WSF_NVM_FILE_SIZE(header.len); + + /*If the entry is valid copy it into the defragmentation buffer, if its reserved we can defrag it*/ + if (header.id != WSF_NVM_RESERVED_FILECODE) { + PalFlashRead(©Buf[copyOffset], fileSize, currentOffset); + copyOffset += WSF_NVM_FILE_SIZE(header.len); + } else { + defragPossible = TRUE; + } + + currentOffset += fileSize; + + if(currentOffset + WSF_NVM_HEADER_SIZE <= WSF_NVM_START_ADDR + wsfNvmCb.totalSize) + { + PalFlashRead(&header, WSF_NVM_HEADER_SIZE, currentOffset); + } + } + + /* + If we defragged anything clear the sector and rewrite flash with defragmented data + */ + if (defragPossible) { + WsfNvmEraseDataAll(NULL); + PalFlashWrite(copyBuf, copyOffset, wsfNvmCb.availAddr); + wsfNvmCb.availAddr += copyOffset; + } else { + WSF_TRACE_INFO0("No unused memory for defragementation!"); + } + + WsfCsExit(); + + return defragPossible; +}