#include "stddef.h"
#include "stdint.h"
#include "Emulated_EEPROM_Access.h"

typedef union
{
    uint32_t    u32Word[1U];

    struct
    {
        uint16_t  u16Index;
        uint16_t  u16IndexInv;
    } stField;
} EEPROM_Ctrl_Data_un_t;

typedef struct
{
    uint8_t    u8State;
    uint16_t   u16BlockNum;
    uint16_t   u16ProgLen;
    uint32_t   u32ProgAddr;
    uint32_t*  pu32ProgData;
} EEPROM_Access_Ctrl_st_t;





#define   EEPROM_BLANK_CHECK_RECORD_NUM     (3U)
#define   EEPROM_OVER_SEARCH_RECORD_NUM     (3U)

#define   EEPROM_WR_STATE_IDLE              (0U)
#define   EEPROM_WR_STATE_ERASE             (1U)
#define   EEPROM_WR_STATE_PROG              (2U)



// --------------------------------------------------------------
//      CRC16计算方法1:使用2个256长度的校验表
// --------------------------------------------------------------
static const uint8_t g_u8EEPROMCRCTableH[] =
{
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x00U, 0xC1U, 0x81U, 0x40U, 0x01U, 0xC0U, 0x80U, 0x41U,
    0x01U, 0xC0U, 0x80U, 0x41U, 0x00U, 0xC1U, 0x81U, 0x40U,
};

static const uint8_t g_u8EEPROMCRCTableL[] =
{
    0x00U, 0xC0U, 0xC1U, 0x01U, 0xC3U, 0x03U, 0x02U, 0xC2U,
    0xC6U, 0x06U, 0x07U, 0xC7U, 0x05U, 0xC5U, 0xC4U, 0x04U,
    0xCCU, 0x0CU, 0x0DU, 0xCDU, 0x0FU, 0xCFU, 0xCEU, 0x0EU,
    0x0AU, 0xCAU, 0xCBU, 0x0BU, 0xC9U, 0x09U, 0x08U, 0xC8U,
    0xD8U, 0x18U, 0x19U, 0xD9U, 0x1BU, 0xDBU, 0xDAU, 0x1AU,
    0x1EU, 0xDEU, 0xDFU, 0x1FU, 0xDDU, 0x1DU, 0x1CU, 0xDCU,
    0x14U, 0xD4U, 0xD5U, 0x15U, 0xD7U, 0x17U, 0x16U, 0xD6U,
    0xD2U, 0x12U, 0x13U, 0xD3U, 0x11U, 0xD1U, 0xD0U, 0x10U,
    0xF0U, 0x30U, 0x31U, 0xF1U, 0x33U, 0xF3U, 0xF2U, 0x32U,
    0x36U, 0xF6U, 0xF7U, 0x37U, 0xF5U, 0x35U, 0x34U, 0xF4U,
    0x3CU, 0xFCU, 0xFDU, 0x3DU, 0xFFU, 0x3FU, 0x3EU, 0xFEU,
    0xFAU, 0x3AU, 0x3BU, 0xFBU, 0x39U, 0xF9U, 0xF8U, 0x38U,
    0x28U, 0xE8U, 0xE9U, 0x29U, 0xEBU, 0x2BU, 0x2AU, 0xEAU,
    0xEEU, 0x2EU, 0x2FU, 0xEFU, 0x2DU, 0xEDU, 0xECU, 0x2CU,
    0xE4U, 0x24U, 0x25U, 0xE5U, 0x27U, 0xE7U, 0xE6U, 0x26U,
    0x22U, 0xE2U, 0xE3U, 0x23U, 0xE1U, 0x21U, 0x20U, 0xE0U,
    0xA0U, 0x60U, 0x61U, 0xA1U, 0x63U, 0xA3U, 0xA2U, 0x62U,
    0x66U, 0xA6U, 0xA7U, 0x67U, 0xA5U, 0x65U, 0x64U, 0xA4U,
    0x6CU, 0xACU, 0xADU, 0x6DU, 0xAFU, 0x6FU, 0x6EU, 0xAEU,
    0xAAU, 0x6AU, 0x6BU, 0xABU, 0x69U, 0xA9U, 0xA8U, 0x68U,
    0x78U, 0xB8U, 0xB9U, 0x79U, 0xBBU, 0x7BU, 0x7AU, 0xBAU,
    0xBEU, 0x7EU, 0x7FU, 0xBFU, 0x7DU, 0xBDU, 0xBCU, 0x7CU,
    0xB4U, 0x74U, 0x75U, 0xB5U, 0x77U, 0xB7U, 0xB6U, 0x76U,
    0x72U, 0xB2U, 0xB3U, 0x73U, 0xB1U, 0x71U, 0x70U, 0xB0U,
    0x50U, 0x90U, 0x91U, 0x51U, 0x93U, 0x53U, 0x52U, 0x92U,
    0x96U, 0x56U, 0x57U, 0x97U, 0x55U, 0x95U, 0x94U, 0x54U,
    0x9CU, 0x5CU, 0x5DU, 0x9DU, 0x5FU, 0x9FU, 0x9EU, 0x5EU,
    0x5AU, 0x9AU, 0x9BU, 0x5BU, 0x99U, 0x59U, 0x58U, 0x98U,
    0x88U, 0x48U, 0x49U, 0x89U, 0x4BU, 0x8BU, 0x8AU, 0x4AU,
    0x4EU, 0x8EU, 0x8FU, 0x4FU, 0x8DU, 0x4DU, 0x4CU, 0x8CU,
    0x44U, 0x84U, 0x85U, 0x45U, 0x87U, 0x47U, 0x46U, 0x86U,
    0x82U, 0x42U, 0x43U, 0x83U, 0x41U, 0x81U, 0x80U, 0x40U,
};

/*static const uint8_t g_u8EEPROMCRCTableH[] =
{
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
};

static const uint8_t g_u8EEPROMCRCTableL[] =
{
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
    0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U, 0x55U,
};*/

static uint16_t EEPROM_CRC_Calc(uint32_t * pu32Data, uint16_t u16Len);

EEPROM_Access_Ctrl_st_t         g_stEEPROMAccess;
EEPROM_Block_Access_st_t *      g_pstEEPROMBlock;

EEPROM_Mem_Erase_Func_ptr_t     g_pfnEEPROMMemErase;
EEPROM_Mem_Blank_Chk_Func_ptr_t g_pfnEEPROMMemBlankCheck;
EEPROM_Mem_Read_Func_ptr_t      g_pfnEEPROMMemRead;
EEPROM_Mem_Write_Func_ptr_t     g_pfnEEPROMMemWrite;

void EEPROM_Access_Init(const  EEPROM_Block_st_t * pstBlockTable,
                        EEPROM_Block_Access_st_t * pstBlockAccess,
                        EEPROM_Media_Access_st_t * pstMediaAccess,
                        uint16_t u16BlockNum)
{
    uint16_t  i;

    uint8_t   u8Loop;
    uint8_t   u8Ready;
    uint16_t  u16Index;
    uint16_t  u16IndexBkup;
    uint16_t  u16RecordNum;
    uint16_t  u16MaxRecordNum[2U];
    uint16_t  u16WordNum;
    uint16_t  u16Result;
    uint32_t  u32DataAddr;
    uint32_t  u32DataAddrBase;
    uint32_t  u32DataAddrEnd;

    EEPROM_Ctrl_Data_un_t unCtrlData;

    if ((pstBlockTable != NULL) && (pstBlockAccess != NULL) && \
            (pstMediaAccess != NULL) && (u16BlockNum != 0U))
    {
        /*** Step 1 : Setup media(data flash) access functions ***/
        g_pfnEEPROMMemErase      = pstMediaAccess->pfnMemErase;
        g_pfnEEPROMMemBlankCheck = pstMediaAccess->pfnMemBlankChk;
        g_pfnEEPROMMemRead       = pstMediaAccess->pfnMemRead;
        g_pfnEEPROMMemWrite      = pstMediaAccess->pfnMemWrite;

        /*** Step 2 : Generate flash read / write control data ***/
        g_stEEPROMAccess.u8State     = EEPROM_WR_STATE_IDLE;
        g_stEEPROMAccess.u16BlockNum = u16BlockNum;

        g_pstEEPROMBlock = pstBlockAccess;

        for (i = 0U; i < g_stEEPROMAccess.u16BlockNum; i++)
        {
            u32DataAddr = pstBlockTable[i].u32EndAddr - pstBlockTable[i].u32StartAddr + 1UL;

            g_pstEEPROMBlock[i].u8Status         = EEPROM_BLOCK_BLANK;
            g_pstEEPROMBlock[i].u32BaseAddr      = pstBlockTable[i].u32StartAddr;
            g_pstEEPROMBlock[i].u16SectorSize    = (uint16_t)(u32DataAddr / 2UL);
            g_pstEEPROMBlock[i].u8SectorOffset   = 0U;
            g_pstEEPROMBlock[i].u16Index         = 0U;
            g_pstEEPROMBlock[i].u16BlockSize     = (uint16_t)(pstBlockTable[i].u32DataSize);
            g_pstEEPROMBlock[i].u16MaxRecordNum  = g_pstEEPROMBlock[i].u16SectorSize / g_pstEEPROMBlock[i].u16BlockSize;
            g_pstEEPROMBlock[i].u16RecordNum     = 0U;
            g_pstEEPROMBlock[i].pu32Data         = pstBlockTable[i].pu32DataBuffer;
        }


        /*** Step 3 : Blank check and find start record ***/
        for (i = 0U; i < g_stEEPROMAccess.u16BlockNum; i++)
        {
            /***
            Step 3.1 Blank check and find start record
              - Check if the logic data block related memory is blank.
              - Then find out which logic sector contains the most recent record.
              - The found sector will define the starting search point of step 3.2.
            ***/

            /* Search sector +0 */
            u32DataAddrBase  = g_pstEEPROMBlock[i].u32BaseAddr;
            u32DataAddrEnd   = u32DataAddrBase + (uint32_t)(g_pstEEPROMBlock[i].u16SectorSize) - 1UL;
            u32DataAddr      = g_pfnEEPROMMemBlankCheck(u32DataAddrBase, u32DataAddrEnd);
            if (u32DataAddr == 0x00000000UL)
            {
                u8Loop = 0U;
                u16MaxRecordNum[0U] = 0U;
            }
            else if (u32DataAddr == 0xFFFFFFFFUL)
            {
                u8Loop = 1U;
                u16MaxRecordNum[0U] = g_pstEEPROMBlock[i].u16MaxRecordNum;
            }
            else
            {
                u8Loop = 1U;
                u16MaxRecordNum[0U] = (uint16_t)u32DataAddr / g_pstEEPROMBlock[i].u16BlockSize;
            }

            u16RecordNum = 0U;
            u8Ready      = 0U;

            while (u8Loop)
            {
                /* Fetch block control data */
                u32DataAddr = u32DataAddrBase + (uint32_t)u16RecordNum * (uint32_t)g_pstEEPROMBlock[i].u16BlockSize;
                g_pfnEEPROMMemRead(u32DataAddr, unCtrlData.u32Word, 1UL);

                if (unCtrlData.stField.u16Index + unCtrlData.stField.u16IndexInv == 0xFFFFU)
                {
                    u8Loop     = 0U;
                    u8Ready    = 1U;

                    g_pstEEPROMBlock[i].u8Status        = EEPROM_BLOCK_ACTIVE;
                    g_pstEEPROMBlock[i].u8SectorOffset  = 0U;
                    g_pstEEPROMBlock[i].u16RecordNum    = u16RecordNum;
                    g_pstEEPROMBlock[i].u16Index        = unCtrlData.stField.u16Index;
                    u16IndexBkup                        = unCtrlData.stField.u16Index;
                }
                else
                {
                    u16RecordNum++;
                    if ((u16RecordNum >= u16MaxRecordNum[0U]) || \
                            (u16RecordNum >= EEPROM_BLANK_CHECK_RECORD_NUM))
                    {
                        u8Loop = 0U;
                    }
                }
            }

            /* Search sector +1 */
            u32DataAddrBase += (uint32_t)(g_pstEEPROMBlock[i].u16SectorSize);
            u32DataAddrEnd  += (uint32_t)(g_pstEEPROMBlock[i].u16SectorSize);
            u32DataAddr      = g_pfnEEPROMMemBlankCheck(u32DataAddrBase, u32DataAddrEnd);
            if (u32DataAddr == 0x00000000UL)
            {
                u8Loop = 0U;
                u16MaxRecordNum[1U] = 0U;
            }
            else if (u32DataAddr == 0xFFFFFFFFUL)
            {
                u8Loop = 1U;
                u16MaxRecordNum[1U] = g_pstEEPROMBlock[i].u16MaxRecordNum;
            }
            else
            {
                u8Loop = 1U;
                u16MaxRecordNum[1U] = (uint16_t)u32DataAddr / g_pstEEPROMBlock[i].u16BlockSize;
            }

            u16RecordNum = 0U;

            while (u8Loop)
            {
                /* Fetch block control data */
                u32DataAddr = u32DataAddrBase + (uint32_t)u16RecordNum * (uint32_t)g_pstEEPROMBlock[i].u16BlockSize;
                g_pfnEEPROMMemRead(u32DataAddr, unCtrlData.u32Word, 1UL);

                if (unCtrlData.stField.u16Index + unCtrlData.stField.u16IndexInv == 0xFFFFU)
                {
                    u8Loop     = 0U;

                    if (u8Ready == 0U)
                    {
                        u8Ready    = 1U;
                        g_pstEEPROMBlock[i].u8Status        = EEPROM_BLOCK_ACTIVE;
                        g_pstEEPROMBlock[i].u8SectorOffset  = 1U;
                        g_pstEEPROMBlock[i].u16RecordNum    = u16RecordNum;
                        g_pstEEPROMBlock[i].u16Index        = unCtrlData.stField.u16Index;
                        u16IndexBkup                        = unCtrlData.stField.u16Index;
                    }
                    else
                    {
                        if (unCtrlData.stField.u16Index >= u16IndexBkup)
                        {
                            u16Index = unCtrlData.stField.u16Index - u16IndexBkup;
                        }
                        else
                        {
                            u16Index = unCtrlData.stField.u16Index + (0xFFFFU - u16IndexBkup) + 1U;
                        }

                        if (u16Index < g_pstEEPROMBlock[i].u16MaxRecordNum + EEPROM_BLANK_CHECK_RECORD_NUM)
                        {
                            g_pstEEPROMBlock[i].u8SectorOffset  = 1U;
                            g_pstEEPROMBlock[i].u16RecordNum    = u16RecordNum;
                            g_pstEEPROMBlock[i].u16Index        = unCtrlData.stField.u16Index;
                        }
                    }
                }
                else
                {
                    u16RecordNum++;
                    if ((u16RecordNum >= u16MaxRecordNum[1U]) || \
                            (u16RecordNum >= EEPROM_BLANK_CHECK_RECORD_NUM))
                    {
                        u8Loop = 0U;
                    }
                }
            }

            /***
            Step 3.2 Search for the most recent record and load data
              - Find the last record's address
              - Load data form the address
            ***/
            if (g_pstEEPROMBlock[i].u8Status == EEPROM_BLOCK_ACTIVE)
            {
                /* Find the last record by index */
                u32DataAddrBase  = g_pstEEPROMBlock[i].u32BaseAddr;
                u32DataAddrBase += (uint32_t)(g_pstEEPROMBlock[i].u16SectorSize * (uint16_t)g_pstEEPROMBlock[i].u8SectorOffset);
                u16Index         = g_pstEEPROMBlock[i].u16Index;
                u16RecordNum     = g_pstEEPROMBlock[i].u16RecordNum;
                u8Loop           = 1U;

                while (u8Loop)
                {
                    u16RecordNum++;
                    u16Index++;
                    if ((u16RecordNum >= u16MaxRecordNum[g_pstEEPROMBlock[i].u8SectorOffset]) || \
                            (u16RecordNum  - g_pstEEPROMBlock[i].u16RecordNum >= EEPROM_OVER_SEARCH_RECORD_NUM))
                    {
                        u8Loop = 0U;
                    }
                    else
                    {
                        /* Fetch block control data */
                        u32DataAddr = u32DataAddrBase + (uint32_t)u16RecordNum * (uint32_t)g_pstEEPROMBlock[i].u16BlockSize;
                        g_pfnEEPROMMemRead(u32DataAddr, unCtrlData.u32Word, 1UL);

                        if (unCtrlData.stField.u16Index + unCtrlData.stField.u16IndexInv == 0xFFFFU)
                        {
                            if (unCtrlData.stField.u16Index == u16Index)
                            {
                                g_pstEEPROMBlock[i].u16RecordNum = u16RecordNum;
                                g_pstEEPROMBlock[i].u16Index     = unCtrlData.stField.u16Index;
                            }
                        }
                    }
                }

                /* Load data */
                u16RecordNum    = g_pstEEPROMBlock[i].u16RecordNum;
                u16Index        = g_pstEEPROMBlock[i].u16Index;
                u16IndexBkup    = u16Index;
                u8Loop = 1U;

                do
                {
                    u32DataAddr = u32DataAddrBase + (uint32_t)u16RecordNum * (uint32_t)g_pstEEPROMBlock[i].u16BlockSize;
                    g_pfnEEPROMMemRead(u32DataAddr, unCtrlData.u32Word, 1UL);

                    if (unCtrlData.stField.u16Index + unCtrlData.stField.u16IndexInv == 0xFFFFU)
                    {
                        if (u16Index == unCtrlData.stField.u16Index)
                        {
                            u16IndexBkup = u16Index;
                            u16WordNum   = g_pstEEPROMBlock[i].u16BlockSize / 4U;

                            /* Load data */
                            g_pfnEEPROMMemRead(u32DataAddr, g_pstEEPROMBlock[i].pu32Data, (uint32_t)u16WordNum);
                            u16Result = EEPROM_CRC_Calc(g_pstEEPROMBlock[i].pu32Data, u16WordNum - 1U);

                            if ((uint16_t)(g_pstEEPROMBlock[i].pu32Data[u16WordNum - 1U]) == u16Result)
                            {
                                u8Loop = 0U;
                            }
                        }
                    }

                    if (u8Loop)     /* CRC check fail */
                    {
                        u16Index--;   /* Go to previous record */

                        if (u16IndexBkup >= u16Index)
                        {
                            u16Result = u16IndexBkup - u16Index;
                        }
                        else
                        {
                            u16Result = u16IndexBkup + (0xFFFFU - u16Index) + 1U;
                        }

                        if ((u16Result <= u16MaxRecordNum[0U] + u16MaxRecordNum[1U]) && \
                                (u16Result <= EEPROM_OVER_SEARCH_RECORD_NUM))
                        {
                            if (u16RecordNum == 0U)   /* Cross sector access */
                            {
                                if (g_pstEEPROMBlock[i].u8SectorOffset)
                                {
                                    u32DataAddrBase -= (uint32_t)g_pstEEPROMBlock[i].u16BlockSize;
                                    u16RecordNum     = u16MaxRecordNum[0U];
                                    if (u16RecordNum == 0U)
                                    {
                                        u8Loop = 0U;
                                        g_pstEEPROMBlock[i].u8Status = EEPROM_BLOCK_ERROR;
                                    }
                                    else
                                    {
                                        u16RecordNum -= 1U;
                                    }
                                }
                                else
                                {
                                    u32DataAddrBase += g_pstEEPROMBlock[i].u16BlockSize;
                                    u16RecordNum     = u16MaxRecordNum[1U];
                                    if (u16RecordNum == 0U)
                                    {
                                        u8Loop = 0U;
                                        g_pstEEPROMBlock[i].u8Status = EEPROM_BLOCK_ERROR;
                                    }
                                    else
                                    {
                                        u16RecordNum -= 1U;
                                    }
                                }
                            }
                            else
                            {
                                u16RecordNum--;
                            }
                        }
                        else
                        {
                            /* No correct data found, set error flag */
                            u8Loop = 0U;
                            g_pstEEPROMBlock[i].u8Status = EEPROM_BLOCK_ERROR;
                        }
                    }
                }
                while (u8Loop);
            }
        }
    }
}

uint8_t EEPROM_Access_Busy(void)
{
    return g_stEEPROMAccess.u8State;
}

uint8_t EEPROM_Block_Access_Status(uint16_t u16BlockID)
{
    uint8_t u8Status;

    u8Status = EEPROM_BLOCK_ERROR;
    if (u16BlockID < g_stEEPROMAccess.u16BlockNum)
    {
        u8Status = g_pstEEPROMBlock[u16BlockID].u8Status;
    }

    return u8Status;
}

/* u16Len : DWord(32-bit) number to be read */
/* Retval 0 - OK     1 - Error */
uint8_t EEPROM_Read_Block_Data(uint16_t u16BlockID, uint32_t u32Data[], uint16_t u16Len)
{
    uint16_t i;
    uint8_t  u8Error;

    u8Error = 1U;
    if ((u16BlockID < g_stEEPROMAccess.u16BlockNum) && (u32Data != NULL) && (u16Len != 0U))
    {
        if ((g_pstEEPROMBlock[u16BlockID].u8Status == EEPROM_BLOCK_ACTIVE) && \
                (u16Len <= (g_pstEEPROMBlock[u16BlockID].u16BlockSize - EEPROM_BLOCK_CTRL_BYTE_SIZE) / 4U))
        {
            for (i = 0U; i < u16Len; i++)
            {
                u32Data[i] = g_pstEEPROMBlock[u16BlockID].pu32Data[i + 1U];
            }

            u8Error = 0U;
        }
    }

    return u8Error;
}

/* u16Len : DWord(32-bit) number to be written */
/* Retval 0 - OK     1 - Error */
uint8_t EEPROM_Write_Block_Data(uint16_t u16BlockID, uint32_t u32Data[], uint16_t u16Len)
{
    uint16_t  i;

    uint8_t   u8Error;
    uint16_t  u16WordNum;
    uint16_t  u16Result;
    uint32_t  u32EndAddr;

    EEPROM_Ctrl_Data_un_t unCtrlData;

    u8Error = 1U;
    if ((g_stEEPROMAccess.u8State == EEPROM_WR_STATE_IDLE) && \
            (g_stEEPROMAccess.u16BlockNum > u16BlockID) && (u32Data != NULL) && (u16Len != 0U))
    {
        if (u16Len <= (g_pstEEPROMBlock[u16BlockID].u16BlockSize - EEPROM_BLOCK_CTRL_BYTE_SIZE) / 4U)
        {
            g_stEEPROMAccess.u8State = EEPROM_WR_STATE_PROG;
            u16WordNum = g_pstEEPROMBlock[u16BlockID].u16BlockSize / 4U - 1U;     /* CRC checksum is ignored */

            if (g_pstEEPROMBlock[u16BlockID].u8Status == EEPROM_BLOCK_ACTIVE)
            {
                g_pstEEPROMBlock[u16BlockID].u16RecordNum++;
                if (g_pstEEPROMBlock[u16BlockID].u16RecordNum >= g_pstEEPROMBlock[u16BlockID].u16MaxRecordNum)
                {
                    g_stEEPROMAccess.u8State = EEPROM_WR_STATE_ERASE;
                    g_pstEEPROMBlock[u16BlockID].u16RecordNum = 0U;
                    if (g_pstEEPROMBlock[u16BlockID].u8SectorOffset == 0U)
                    {
                        g_pstEEPROMBlock[u16BlockID].u8SectorOffset = 1U;
                    }
                    else
                    {
                        g_pstEEPROMBlock[u16BlockID].u8SectorOffset = 0U;
                    }
                }

                g_pstEEPROMBlock[u16BlockID].u16Index++;
                unCtrlData.stField.u16Index = g_pstEEPROMBlock[u16BlockID].u16Index;
                unCtrlData.stField.u16IndexInv = 0xFFFFU - unCtrlData.stField.u16Index;
                g_pstEEPROMBlock[u16BlockID].pu32Data[0U] = unCtrlData.u32Word[0U];
            }
            else
            {
                g_stEEPROMAccess.u8State = EEPROM_WR_STATE_ERASE;
                g_pstEEPROMBlock[u16BlockID].u16RecordNum    = 0U;
                g_pstEEPROMBlock[u16BlockID].u8SectorOffset  = 0U;
                g_pstEEPROMBlock[u16BlockID].u16Index        = 0U;
                g_pstEEPROMBlock[u16BlockID].pu32Data[0U]    = 0xFFFF0000UL;

                for (i = 1U; i < u16WordNum; i++)
                {
                    g_pstEEPROMBlock[u16BlockID].pu32Data[i] = 0x00000000UL;
                }
                g_pstEEPROMBlock[u16BlockID].u8Status = EEPROM_BLOCK_ACTIVE;
            }

            u16Len += 1U;
            for (i = 1U; i < u16Len; i++)
            {
                g_pstEEPROMBlock[u16BlockID].pu32Data[i] = u32Data[i - 1U];
            }

            u16Result = EEPROM_CRC_Calc(g_pstEEPROMBlock[u16BlockID].pu32Data, u16WordNum);
            g_pstEEPROMBlock[u16BlockID].pu32Data[u16WordNum] = (uint32_t)u16Result;

            g_stEEPROMAccess.u32ProgAddr  = g_pstEEPROMBlock[u16BlockID].u32BaseAddr;
            g_stEEPROMAccess.u32ProgAddr += (uint32_t)g_pstEEPROMBlock[u16BlockID].u16SectorSize * (uint32_t)g_pstEEPROMBlock[u16BlockID].u8SectorOffset;
            g_stEEPROMAccess.u32ProgAddr += (uint32_t)g_pstEEPROMBlock[u16BlockID].u16BlockSize * (uint32_t)g_pstEEPROMBlock[u16BlockID].u16RecordNum;
            g_stEEPROMAccess.u16ProgLen   = g_pstEEPROMBlock[u16BlockID].u16BlockSize / 4U;
            g_stEEPROMAccess.pu32ProgData = g_pstEEPROMBlock[u16BlockID].pu32Data;

            if (g_stEEPROMAccess.u8State == EEPROM_WR_STATE_ERASE)
            {
                u32EndAddr = g_stEEPROMAccess.u32ProgAddr + (uint32_t)g_pstEEPROMBlock[u16BlockID].u16SectorSize - 1UL;
                g_pfnEEPROMMemErase(g_stEEPROMAccess.u32ProgAddr, u32EndAddr);
            }
            else
            {
                g_pfnEEPROMMemWrite(g_stEEPROMAccess.u32ProgAddr, g_stEEPROMAccess.pu32ProgData, (uint32_t)g_stEEPROMAccess.u16ProgLen);
            }

            u8Error = 0U;
        }
    }

    return u8Error;
}

void EEPROM_Mem_Access_Complete_Callback(void)
{
    if (g_stEEPROMAccess.u8State == EEPROM_WR_STATE_ERASE)
    {
        g_stEEPROMAccess.u8State = EEPROM_WR_STATE_PROG;
        g_pfnEEPROMMemWrite(g_stEEPROMAccess.u32ProgAddr, g_stEEPROMAccess.pu32ProgData, (uint32_t)g_stEEPROMAccess.u16ProgLen);
    }
    else
    {
        g_stEEPROMAccess.u8State = EEPROM_WR_STATE_IDLE;
    }
}

static uint16_t EEPROM_CRC_Calc(uint32_t * pu32Data, uint16_t u16Len)
{
    uint16_t i;

    uint8_t  u8CRCHi;
    uint8_t  u8CRCLo;
    uint8_t  u8Index;
    uint8_t *pu8DataByte;
    uint16_t u16CRCResult;

    u16CRCResult = 0xFFFFU;
    if (pu32Data != NULL)
    {
        u8CRCHi     = 0xFFU;
        u8CRCLo     = 0xFFU;
        u16Len    <<= 2U;      /* u16Len = u16Len * 4U */
        pu8DataByte = (uint8_t *)pu32Data;

        for (i = 0; i < u16Len; i++)
        {
            u8Index  = u8CRCLo ^ pu8DataByte[i];
            u8CRCLo  = u8CRCHi ^ g_u8EEPROMCRCTableH[u8Index];
            u8CRCHi  = g_u8EEPROMCRCTableL[u8Index];
        }

        u16CRCResult   = (uint16_t)u8CRCHi;
        u16CRCResult <<= 8U;
        u16CRCResult  |= (uint16_t)u8CRCLo;
    }

    return u16CRCResult;
}
