chen
2024-11-08 cc432b761c884a0bd8e9d83db0a4e26109fc08b1
keil/include/components/wsf/sources/port/baremetal/wsf_nvm.c
对比新文件
@@ -0,0 +1,405 @@
/*************************************************************************************************/
/*!
 *  \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);
    }
}