对比新文件 |
| | |
| | | /*************************************************************************************************/ |
| | | /*! |
| | | * \file wsf_nvm.c |
| | | * |
| | | * \brief NVM service. |
| | | * |
| | | * Copyright (c) 2019 Arm Ltd. All Rights Reserved. |
| | | * |
| | | * Copyright (c) 2019-2020 Packetcraft, Inc. |
| | | * |
| | | * Licensed under the Apache License, Version 2.0 (the "License"); |
| | | * you may not use this file except in compliance with the License. |
| | | * You may obtain a copy of the License at |
| | | * |
| | | * http://www.apache.org/licenses/LICENSE-2.0 |
| | | * |
| | | * Unless required by applicable law or agreed to in writing, software |
| | | * distributed under the License is distributed on an "AS IS" BASIS, |
| | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| | | * See the License for the specific language governing permissions and |
| | | * limitations under the License. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | |
| | | #include "wsf_types.h" |
| | | #include "wsf_nvm.h" |
| | | #include "wsf_assert.h" |
| | | #include "pal_flash.h" |
| | | #include "crc.h" |
| | | |
| | | /************************************************************************************************** |
| | | Macros |
| | | **************************************************************************************************/ |
| | | |
| | | /*! NVM data start address. */ |
| | | #define WSF_NVM_START_ADDR 0x0000 |
| | | |
| | | /*! Reserved filecode. */ |
| | | #define WSF_NVM_RESERVED_FILECODE ((uint16_t)0) |
| | | |
| | | /* Unused (erased) filecode. */ |
| | | /* TODO: May depend on flash type */ |
| | | #define WSF_NVM_UNUSED_FILECODE ((uint16_t)0xFFFF) |
| | | |
| | | /*! Flash word size. */ |
| | | #define WSF_FLASH_WORD_SIZE 1U |
| | | |
| | | /*! 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_CRC_INIT_VALUE 0xFEDCBA98 |
| | | |
| | | /************************************************************************************************** |
| | | Data Types |
| | | **************************************************************************************************/ |
| | | |
| | | /*! \brief Header. */ |
| | | typedef struct |
| | | { |
| | | uint16_t id; /*!< Stored data ID. */ |
| | | uint16_t len; /*!< Stored data length. */ |
| | | uint16_t headerCrc; /*!< CRC of this header. */ |
| | | uint16_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. */ |
| | | } wsfNvmCb; |
| | | |
| | | /************************************************************************************************** |
| | | Global Functions |
| | | **************************************************************************************************/ |
| | | |
| | | /*************************************************************************************************/ |
| | | /*! |
| | | * \brief Initialize the WSF NVM. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | void WsfNvmInit(void) |
| | | { |
| | | PalFlashInit(NULL); |
| | | wsfNvmCb.totalSize = PalNvmGetTotalSize(); |
| | | wsfNvmCb.sectorSize = PalNvmGetSectorSize(); |
| | | |
| | | WsfNvmHeader_t header; |
| | | uint32_t storageAddr = WSF_NVM_START_ADDR; |
| | | uint16_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; |
| | | } |
| | | |
| | | /* 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 = crc16((uint8_t *)&header, sizeof(header.id) + sizeof(header.len)); |
| | | |
| | | 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); |
| | | WSF_ASSERT((storageAddr - WSF_NVM_START_ADDR) < wsfNvmCb.totalSize); |
| | | |
| | | } while ((storageAddr - WSF_NVM_START_ADDR) < wsfNvmCb.totalSize); |
| | | |
| | | 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); |
| | | |
| | | if (header.id == WSF_NVM_UNUSED_FILECODE) |
| | | { |
| | | break; |
| | | } |
| | | |
| | | 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); |
| | | |
| | | /* Erase all data. */ |
| | | WsfNvmEraseDataAll(NULL); |
| | | } |
| | | } |
| | | |
| | | /*************************************************************************************************/ |
| | | /*! |
| | | * \brief Read data. |
| | | * |
| | | * \param id Stored data ID. |
| | | * \param pData Buffer to read to. |
| | | * \param len Data length to read. |
| | | * \param compCback Read callback. |
| | | * |
| | | * \return TRUE if NVM operation is successful, FALSE otherwise. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | bool_t WsfNvmReadData(uint16_t id, uint8_t *pData, uint16_t len, WsfNvmCompEvent_t compCback) |
| | | { |
| | | WsfNvmHeader_t header; |
| | | uint16_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))); |
| | | |
| | | /* 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; |
| | | } |
| | | |
| | | /* 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 = crc16((uint8_t *)&header, sizeof(header.id) + sizeof(header.len)); |
| | | |
| | | 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 = crc16(pData, header.len); |
| | | if (dataCrc == header.dataCrc) |
| | | { |
| | | findId = 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); |
| | | |
| | | if (compCback) |
| | | { |
| | | compCback(findId); |
| | | } |
| | | return findId; |
| | | } |
| | | |
| | | /*************************************************************************************************/ |
| | | /*! |
| | | * \brief Write data. |
| | | * |
| | | * \param id Stored data ID. |
| | | * \param pData Buffer to write. |
| | | * \param len Data length to write. |
| | | * \param compCback Write callback. |
| | | * |
| | | * \return TRUE if NVM operation is successful, FALSE otherwise. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | bool_t WsfNvmWriteData(uint16_t id, const uint8_t *pData, uint16_t len, WsfNvmCompEvent_t compCback) |
| | | { |
| | | WsfNvmHeader_t header; |
| | | uint16_t headerCrc, dataCrc; |
| | | uint32_t storageAddr = WSF_NVM_START_ADDR; |
| | | |
| | | WSF_ASSERT(!((id == WSF_NVM_RESERVED_FILECODE) || (id == WSF_NVM_UNUSED_FILECODE))); |
| | | WSF_ASSERT((wsfNvmCb.availAddr - WSF_NVM_START_ADDR) <= wsfNvmCb.totalSize); |
| | | |
| | | /* 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; |
| | | } |
| | | |
| | | /* 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 = crc16((uint8_t *)&header, sizeof(header.id) + sizeof(header.len)); |
| | | |
| | | if (headerCrc != header.headerCrc) |
| | | { |
| | | /* Corrupt header. */ |
| | | /* TODO: Catastrophic failure? */ |
| | | break; |
| | | } |
| | | else if (header.id == id) |
| | | { |
| | | dataCrc = crc16(pData, len); |
| | | 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((const uint8_t *)&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); |
| | | |
| | | /* After cycling through all headers, create a new stored data header and store data */ |
| | | header.id = id; |
| | | header.len = len; |
| | | header.headerCrc = crc16((uint8_t *)&header, sizeof(header.id) + sizeof(header.len)); |
| | | header.dataCrc = crc16(pData, len); |
| | | |
| | | PalFlashWrite((const uint8_t *)&header, sizeof(header), storageAddr); |
| | | PalFlashWrite(pData, len, storageAddr + sizeof(header)); |
| | | |
| | | /* 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); |
| | | } |
| | | |
| | | return TRUE; |
| | | } |
| | | |
| | | /*************************************************************************************************/ |
| | | /*! |
| | | * \brief Erase data. |
| | | * |
| | | * \param id Erase ID. |
| | | * \param compCback Write callback. |
| | | * |
| | | * \return TRUE if NVM operation is successful, FALSE otherwise. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | 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; |
| | | |
| | | WSF_ASSERT(!((id == WSF_NVM_RESERVED_FILECODE) || (id == WSF_NVM_UNUSED_FILECODE))); |
| | | |
| | | /* 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; |
| | | } |
| | | |
| | | /* Iterate through stored data headers, looking for existing matching stored data header. */ |
| | | if (header.id != WSF_NVM_RESERVED_FILECODE) |
| | | { |
| | | headerCrc = crc16((uint8_t *)&header, sizeof(header.id) + sizeof(header.len)); |
| | | |
| | | 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((const uint8_t *)&header, sizeof(header), storageAddr); |
| | | erased = TRUE; |
| | | } |
| | | } |
| | | |
| | | /* 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; |
| | | } |
| | | |
| | | /*************************************************************************************************/ |
| | | /*! |
| | | * \brief Erase all data located in NVM storage. |
| | | * |
| | | * \param compCback Erase callback. |
| | | * |
| | | * \note Security Risk Warning. NVM storage could be shared by multiple Apps. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | void WsfNvmEraseDataAll(WsfNvmCompEvent_t compCback) |
| | | { |
| | | 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); |
| | | } |
| | | } |