/*
 * Copyright (c) 2022, Shenzhen CVA Innovation CO.,LTD
 * All rights reserved.
 *
 * Shenzhen CVA Innovation CO.,LTD (CVA chip) is supplying this file for use
 * exclusively with CVA's microcontroller products. This file can be freely
 * distributed within development tools that are supporting such microcontroller
 * products.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * CVA SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 */

/*******************************************************************************
 * the includes
 ******************************************************************************/
#include <stdint.h>
#include <string.h>
#include "bootloader.h"

/*******************************************************************************
 * the defines
 ******************************************************************************/
static void Bootloader_DeInit(void);
static void Bootloader_InitState(void);
static void Bootloader_EraseState(void);
static void Bootloader_PgmState(void);
static void Bootloader_CheckState(void);
static void Bootloader_ExitState(void);
static void Bootloader_ErrorState(void);

/*******************************************************************************
 * the typedefs
 ******************************************************************************/
typedef void (*pBootloader_StateFun)(void);

/*******************************************************************************
 * the globals
 ******************************************************************************/

static Bootloader_CbType sBootloader_Cb;

pBootloader_StateFun sBootloader_StateFuncTbl[BOOTLOADER_STATE_NUM] = {
    Bootloader_InitState,
    Bootloader_EraseState,
    Bootloader_PgmState,
    Bootloader_ErrorState,
    Bootloader_CheckState,
    Bootloader_ExitState};

/*******************************************************************************
 * the functions
 ******************************************************************************/

/*! \brief Get boot block size bytes
 *
 * This function get the pgm block byte size
 *
 * \param[in] void
 *
 * \return void
 */
uint32_t Bootloader_GetPgmBlockSize(void)
{
    return BOOTLOADER_CFG_PGM_BYTES_SIZE;
}

/*! \brief Reset the cpu
 *
 * This function reset the cpu if get reset request
 *
 * \param[in] void
 *
 * \return void
 */
static void Bootloader_DeInit(void)
{
    /* Clear flash driver in ram */
    memset((uint8_t *)BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR, 0, BOOTLOADER_CFG_FLS_DRV_RAM_CODE_SIZE + BOOTLOADER_CFG_FLS_DRV_RAM_CRC_SIZE);
}

/*! \brief Get the pgm condition
 *
 * This function get the pgm condition
 *
 * \param[in] void
 *
 * \return voltage state
 *         0: voltage normal
 *         1: voltage low
 *         2: voltage high
 */
uint8_t Bootloader_GetPgmCondition(void)
{
    uint8_t rtn = false;

    rtn = sBootloader_Cb.pCallBackFunctionList->pgmConditionChecFunction();
    return rtn;
}

/*! \brief Update the flash driver if the flash driver is from outside
 *
 * This function update the flash driver interface address and init the flash module
 *
 * \param[in] void
 *
 * \return void
 */
void Bootloader_FlsDrvUpdate(void)
{
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 0u
    sBootloader_Cb.flsDrvInfo.flashDriverDownloaded = false;
    /* Read out the flash driver first 4words data */
    sBootloader_Cb.flsDrvInfo.startFlsInitAddr  = *((uint32_t *)(BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR + 4));
    sBootloader_Cb.flsDrvInfo.startFlsEraseAddr = *((uint32_t *)(BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR + 8));
    sBootloader_Cb.flsDrvInfo.startFlsWriteAddr = *((uint32_t *)(BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR + 12));
    sBootloader_Cb.flsDrvInfo.startFlsReadAddr  = *((uint32_t *)(BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR + 16));

    sBootloader_Cb.flsDrvInfo.flsInitFunction  = (pFls_InitFunctionPtr)sBootloader_Cb.flsDrvInfo.startFlsInitAddr;
    sBootloader_Cb.flsDrvInfo.flsEraseFunction = (pFls_EraseFunctionPtr)sBootloader_Cb.flsDrvInfo.startFlsEraseAddr;
    sBootloader_Cb.flsDrvInfo.flsWriteFunction = (pFls_WriteFunctionPtr)sBootloader_Cb.flsDrvInfo.startFlsWriteAddr;
    sBootloader_Cb.flsDrvInfo.flsReadFunction  = (pFls_ReadFunctionPtr)sBootloader_Cb.flsDrvInfo.startFlsReadAddr;

    sBootloader_Cb.flsDrvInfo.flsInitFunction(sBootloader_Cb.flashInstanceAddress);
    sBootloader_Cb.flsDrvInfo.flashDriverDownloaded = true;
#endif
}

/*! \brief Bootloader init state process
 *
 * This function check the boot request is existed or not
 *
 * \param[in] void
 *
 * \return void
 */
static void Bootloader_InitState(void)
{
    if(sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_NONE)
    {
        if((sBootloader_Cb.emergeBootReqEn == true) || (sBootloader_Cb.bootActive == true))
        {
            /* Emerge boot request */
            sBootloader_Cb.stayInBootTimeoutCnt = 0;
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 0u
            sBootloader_Cb.flsDrvInfo.flashDriverDownloaded = false;
#endif
            sBootloader_Cb.bootActive   = true;
            sBootloader_Cb.bootSubState = BOOTLOADER_SUB_STATE_END;
            sBootloader_Cb.pCallBackFunctionList->setSessionModeFunction(2);
        }
        else
        {
            Bootloader_SetBootState(BOOTLOADER_STATE_CHECK);
            sBootloader_Cb.checkInfo.activeCheckReq = true;
            sBootloader_Cb.checkInfo.checkObj       = BOOLOADER_OBJECT_APP_CODE;
        }
    }
}

/*! \brief Bootloader erase state process
 *
 * This function excute the action if bootloader is in erase flash state
 *
 * \param[in] void
 *
 * \return void
 */
static void Bootloader_EraseState(void)
{
    if((sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_NONE) || (sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_DOING))
    {
        if(sBootloader_Cb.eraseInfo.eraseSectionNums > 0u)
        {
            if(sBootloader_Cb.eraseInfo.eraseSectionCnt < sBootloader_Cb.eraseInfo.eraseSectionNums)
            {
                Fls_StdRtnType ret = FLS_STD_RETURN_OK;
#if BOOTLOADER_CFG_ERASE_SECTOR_NUM != BOOTLOADER_CFG_ERASE_ALL
                uint32_t eraseSectorNums = 0;
#endif
                uint32_t eraseStartAddress = sBootloader_Cb.eraseInfo.eraseStartAddress;

                if(sBootloader_Cb.downloadObj != BOOLOADER_OBJECT_FLASH_DRIVER)
                {
                    /* Make sure no interrupt during erasing action, so disable global */
                    sBootloader_Cb.pCallBackFunctionList->setGlobalIsrFunction(false);

#if BOOTLOADER_CFG_ERASE_SECTOR_NUM == BOOTLOADER_CFG_ERASE_ALL
                    /* First erase App Head then erased other sectors */
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u
                    ret = sBootloader_Cb.pCallBackFunctionList->flsEraseFunction(sBootloader_Cb.eraseInfo.eraseStartAddress + FLS_ERASE_SECTION_SIZE, 1);
#else
                    /* Check flash driver has been download into ram */
                    if(sBootloader_Cb.flsDrvInfo.flashDriverDownloaded == true)
                    {
                        ret = sBootloader_Cb.flsDrvInfo.flsEraseFunction(sBootloader_Cb.eraseInfo.eraseStartAddress + FLS_ERASE_SECTION_SIZE, 1);
                    }
                    else
                    {
                        ret = FLS_STD_RETURN_FLASH_DRIVER_INVALID;
                    }
#endif

                    if(ret == FLS_STD_RETURN_OK)
                    {
                        /* Pending */
                        sBootloader_Cb.pCallBackFunctionList->udsNegativeRespFunction(0x31, 0x78);

#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u
                        /* Erase Vector sector */
                        ret = sBootloader_Cb.pCallBackFunctionList->flsEraseFunction(sBootloader_Cb.eraseInfo.eraseStartAddress, 1);
#else
                        /* Erase Vector sector */
                        ret = sBootloader_Cb.flsDrvInfo.flsEraseFunction(sBootloader_Cb.eraseInfo.eraseStartAddress, 1);
#endif
                    }

                    if(ret == FLS_STD_RETURN_OK)
                    {
                        /* Pending */
                        sBootloader_Cb.pCallBackFunctionList->udsNegativeRespFunction(0x31, 0x78);

                        /* Erase other sectors */
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u
                        ret = sBootloader_Cb.pCallBackFunctionList->flsEraseFunction(eraseStartAddress + 2u * FLS_ERASE_SECTION_SIZE,
                                                                                     sBootloader_Cb.eraseInfo.eraseSectionNums - 2u);
#else
                        ret = sBootloader_Cb.flsDrvInfo.flsEraseFunction(eraseStartAddress + 2u * FLS_ERASE_SECTION_SIZE,
                                                                         sBootloader_Cb.eraseInfo.eraseSectionNums - 2u);
#endif
                    }

#else
                    eraseStartAddress = sBootloader_Cb.eraseInfo.eraseStartAddress + (sBootloader_Cb.eraseInfo.eraseSectionCnt * FLS_ERASE_SECTION_SIZE);

                    /* Check out the rest sector nums is more than the once-erased nums, if not, erased the rest sector nums */
                    eraseSectorNums = sBootloader_Cb.eraseInfo.eraseSectionNums - sBootloader_Cb.eraseInfo.eraseSectionCnt;
                    if(eraseSectorNums > BOOTLOADER_CFG_ERASE_SECTOR_NUM)
                    {
                        eraseSectorNums = BOOTLOADER_CFG_ERASE_SECTOR_NUM;
                    }

#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u
                    ret = sBootloader_Cb.pCallBackFunctionList->flsEraseFunction(eraseStartAddress,
                                                                                 eraseSectorNums);
#else
                    /* Check flash driver has been download into ram */
                    if(sBootloader_Cb.flsDrvInfo.flashDriverDownloaded == true)
                    {
                        ret = sBootloader_Cb.flsDrvInfo.flsEraseFunction(eraseStartAddress,
                                                                         eraseSectorNums);
                    }
                    else
                    {
                        ret = FLS_STD_RETURN_FLASH_DRIVER_INVALID;
                    }
#endif

#endif
                    /* After erased, restored the global interrupt */
                    sBootloader_Cb.pCallBackFunctionList->setGlobalIsrFunction(true);
                }
                else
                {
                }

                if(ret == FLS_STD_RETURN_OK)
                {
                    uint8_t rspBuffer[8] = {0x71, 0x01, 0xFF, 0x00, 0x00};
#if BOOTLOADER_CFG_ERASE_SECTOR_NUM == BOOTLOADER_CFG_ERASE_ALL
                    /* Erased action has been finished */
                    sBootloader_Cb.bootSubState               = BOOTLOADER_SUB_STATE_END;
                    sBootloader_Cb.eraseInfo.eraseSectionNums = 0;
                    sBootloader_Cb.eraseInfo.eraseLength      = 0;
                    sBootloader_Cb.eraseInfo.eraseSectionCnt  = 0;

                    /* 0x01: failed   0x00: success */
                    rspBuffer[4] = 0x00;
                    sBootloader_Cb.pCallBackFunctionList->udsPositiveRespFunction(rspBuffer, 5);
#else
                    sBootloader_Cb.eraseInfo.eraseSectionCnt += BOOTLOADER_CFG_ERASE_SECTOR_NUM;
                    if(sBootloader_Cb.eraseInfo.eraseSectionCnt >= sBootloader_Cb.eraseInfo.eraseSectionNums)
                    {
                        /* Erased action has been finished */
                        sBootloader_Cb.bootSubState               = BOOTLOADER_SUB_STATE_END;
                        sBootloader_Cb.eraseInfo.eraseSectionNums = 0;
                        sBootloader_Cb.eraseInfo.eraseLength      = 0;
                        sBootloader_Cb.eraseInfo.eraseSectionCnt  = 0;

                        /* 0x01: failed   0x00: success */
                        rspBuffer[4] = 0x00;
                        sBootloader_Cb.pCallBackFunctionList->udsPositiveRespFunction(rspBuffer, 5);
                    }
                    else
                    {
                        sBootloader_Cb.bootSubState = BOOTLOADER_SUB_STATE_DOING;
                        /* Pending */
                        sBootloader_Cb.pCallBackFunctionList->udsNegativeRespFunction(0x31, 0x78);
                    }
#endif
                }
                else
                {
                    uint8_t rspBuffer[8] = {0x71, 0x01, 0xFF, 0x00};

                    sBootloader_Cb.eraseInfo.eraseSectionNums = 0;
                    sBootloader_Cb.eraseInfo.eraseLength      = 0;
                    sBootloader_Cb.eraseInfo.eraseSectionCnt  = 0;
                    Bootloader_SetBootState(BOOTLOADER_STATE_ERR);

                    /* 0x01: failed   0x00: success */
                    rspBuffer[4] = 0x01;
                    sBootloader_Cb.pCallBackFunctionList->udsPositiveRespFunction(rspBuffer, 5);
                }
            }
        }
    }
    else
    {
        /* Nothing to do , just wait for the next event */
    }
}

/*! \brief Bootloader program flash state process
 *
 * This function excute the action if bootloader is in programming flash state
 *
 * \param[in] void
 *
 * \return void
 */
static void Bootloader_PgmState(void)
{
    if(((sBootloader_Cb.pgmInfo.pgmReadyEn == true) && ((sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_NONE) || (sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_DOING))))
    {
        /* if received one frame datas to be need to download to mcu */
        if(sBootloader_Cb.pgmInfo.pgmStartTriggerEn == true)
        {
            if(sBootloader_Cb.pgmInfo.pgmMemCnt < sBootloader_Cb.pgmInfo.pgmMemSize)
            {
                Fls_StdRtnType ret = FLS_STD_RETURN_OK;

                if(sBootloader_Cb.downloadObj != BOOLOADER_OBJECT_FLASH_DRIVER)
                {
                    sBootloader_Cb.pCallBackFunctionList->setGlobalIsrFunction(false);

#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u
                    ret = sBootloader_Cb.pCallBackFunctionList->flsWriteFunction(sBootloader_Cb.pgmInfo.pgmStartAddress + sBootloader_Cb.pgmInfo.pgmMemCnt,
                                                                                 (const uint8_t *)sBootloader_Cb.pgmInfo.pgmData,
                                                                                 BOOTLOADER_CFG_PGM_BYTES_SIZE);
#else
                    /* Check flash driver has been download into ram */
                    if(sBootloader_Cb.flsDrvInfo.flashDriverDownloaded == true)
                    {
                        ret = sBootloader_Cb.flsDrvInfo.flsWriteFunction(sBootloader_Cb.pgmInfo.pgmStartAddress + sBootloader_Cb.pgmInfo.pgmMemCnt,
                                                                         (const uint8_t *)sBootloader_Cb.pgmInfo.pgmData,
                                                                         BOOTLOADER_CFG_PGM_BYTES_SIZE);
                    }
                    else
                    {
                        ret = FLS_STD_RETURN_FLASH_DRIVER_INVALID;
                    }
#endif
                    sBootloader_Cb.pCallBackFunctionList->setGlobalIsrFunction(true);
                }
                else
                {
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 0u
                    /* Copy flash driver into ram */
                    memcpy((uint8_t *)(sBootloader_Cb.pgmInfo.pgmStartAddress + sBootloader_Cb.pgmInfo.pgmMemCnt),
                           (const uint8_t *)sBootloader_Cb.pgmInfo.pgmData,
                           BOOTLOADER_CFG_PGM_BYTES_SIZE);
#endif
                }

                if(ret == FLS_STD_RETURN_OK)
                {
                    uint8_t rspBuffer[8] = {0};

                    sBootloader_Cb.pgmInfo.pgmMemCnt += BOOTLOADER_CFG_PGM_BYTES_SIZE;

                    if(sBootloader_Cb.pgmInfo.pgmMemCnt >= sBootloader_Cb.pgmInfo.pgmMemSize)
                    {
                        sBootloader_Cb.pgmInfo.pgmReadyEn = false;
                        sBootloader_Cb.bootSubState       = BOOTLOADER_SUB_STATE_END;
                    }
                    else
                    {
                        sBootloader_Cb.bootSubState = BOOTLOADER_SUB_STATE_DOING;
                    }

                    /* Indicate the EOL thant it can transfer the next block data to be written */
                    rspBuffer[0] = 0x76;
                    rspBuffer[1] = sBootloader_Cb.pgmInfo.pgmBlockSn;

                    sBootloader_Cb.pCallBackFunctionList->udsPositiveRespFunction(rspBuffer, 2);
                }
                else
                {
                    sBootloader_Cb.pgmInfo.pgmReadyEn      = false;
                    sBootloader_Cb.pgmInfo.pgmMemSize      = 0;
                    sBootloader_Cb.pgmInfo.pgmMemCnt       = 0;
                    sBootloader_Cb.pgmInfo.pgmBlockSn      = 0;
                    sBootloader_Cb.pgmInfo.pgmBlockByteCnt = 0;
                    Bootloader_SetBootState(BOOTLOADER_STATE_ERR);
                }

                /* Clear the download trigger event, wait for the next download data if need */
                sBootloader_Cb.pgmInfo.pgmStartTriggerEn = false;
            }
        }
    }
    else
    {
        /* Nothing to do , just wait for the next event */
    }
}

/*! \brief Bootloader check state process
 *
 *  This function excute the action if bootloader is in check state
 *
 * \param[in] void
 *
 * \return void
 */

volatile uint32_t calculateCrc = 0;
static void       Bootloader_CheckState(void)
{
    if((sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_NONE) || (sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_DOING))
    {
        if(sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_NONE)
        {
            /* Only in the state of power on or reset */
            if(sBootloader_Cb.bootActive == false)
            {
                uint32_t last_addr;
                last_addr = *((uint32_t *)(BOOTLOADER_CFG_APP_CRC_ADDR-BOOTLOADER_CFG_APP_CRC_SIZE));
                if (last_addr > 0x10000 && last_addr < 0x37000)
                {
                    Bootloader_EventDataCheckRequest(BOOTLOADER_CFG_APP_START_ADDR, BOOTLOADER_CFG_APP_SIZE, (*((uint32_t *)BOOTLOADER_CFG_APP_CRC_ADDR)));
                }
                else
                {
                    /* Active mcu enter into boot */
                    sBootloader_Cb.bootActive          = true;
                    sBootloader_Cb.bootSubState        = BOOTLOADER_SUB_STATE_END;
                    sBootloader_Cb.checkInfo.crcResult = false;
                    sBootloader_Cb.checkInfo.checkObj  = BOOLOADER_OBJECT_NONE;
                }
                
            }
        }
        else if(sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_DOING)
        {
            if(sBootloader_Cb.checkInfo.checkObj == BOOLOADER_OBJECT_APP_CODE)
            {

                if(sBootloader_Cb.bootActive == true)
                {
                    /* Calculate all app code, include crc self */
                    calculateCrc = sBootloader_Cb.pCallBackFunctionList->calculateCheckSumFunction(BOOTLOADER_CFG_INIT_CRC_VALUE,
                                                                                                   (const uint8_t *)sBootloader_Cb.checkInfo.CheckStartAddress,
                                                                                                   sBootloader_Cb.checkInfo.CheckmemSize,
                                                                                                   true);
                    //SEGGER_RTT_printf(0,"calculateCrc=0x%X\n",calculateCrc);
                    //SEGGER_RTT_printf(0,"checkCrc=0x%X\n",sBootloader_Cb.checkInfo.checkCrc);
                    if(calculateCrc == sBootloader_Cb.checkInfo.checkCrc)
                    {
                        uint32_t *pTailData,addr,TailData=0xfffffffful;
                        addr = *((uint32_t *)BOOTLOADER_CFG_APP_TAIL_ADDR);
                        if (addr > 0x10000ul && addr < 0x38000ul)
                        {
                            pTailData = (uint32_t *)(addr - 4);
                            TailData = *pTailData;
                        }
                        /* Check App Head and Tail and Crc */
                        if(((*((uint32_t *)BOOTLOADER_CFG_APP_HEAD_ADDR)) != BOOTLOADER_CFG_HEAD_MASK) || (TailData != BOOTLOADER_CFG_TAIL_MASK) || ((*((uint32_t *)BOOTLOADER_CFG_APP_CRC_ADDR)) == BOOTLOADER_CFG_FLASH_FILL_PAD_VALUE))
                        {
                            /* Active mcu enter into boot */
                            sBootloader_Cb.bootActive          = true;
                            sBootloader_Cb.checkInfo.crcResult = false;
                            sBootloader_Cb.bootSubState        = BOOTLOADER_SUB_STATE_END;
                            sBootloader_Cb.checkInfo.checkObj  = BOOLOADER_OBJECT_NONE;
                        }
                        else
                        {
                            sBootloader_Cb.checkInfo.crcResult = true;
                            /* If the current app is ok and no bootloader process action, then enter into exit state */
                            if(sBootloader_Cb.bootActive == false)
                            {
                                Bootloader_SetBootState(BOOTLOADER_STATE_EXIT);
                            }
                            else
                            {
                                /* Stay in check state, wait for next command */
                                sBootloader_Cb.bootActive         = true;
                                sBootloader_Cb.bootSubState       = BOOTLOADER_SUB_STATE_END;
                                sBootloader_Cb.checkInfo.checkObj = BOOLOADER_OBJECT_NONE;
                            }
                        }
                    }
                    else
                    {
                        /* Active mcu enter into boot */
                        sBootloader_Cb.bootActive          = true;
                        sBootloader_Cb.bootSubState        = BOOTLOADER_SUB_STATE_END;
                        sBootloader_Cb.checkInfo.crcResult = false;
                        sBootloader_Cb.checkInfo.checkObj  = BOOLOADER_OBJECT_NONE;
                    }
                }
                else
                {
                    uint32_t *pTailData,addr,TailData=0xfffffffful;
                    addr = *((uint32_t *)BOOTLOADER_CFG_APP_TAIL_ADDR);
                    if (addr > 0x10000ul && addr < 0x38000ul)
                    {
                        pTailData = (uint32_t *)(addr - 4);
                        TailData = *pTailData;
                    }
                    /* Check App Head and Tail and Crc */
                    if(((*((uint32_t *)BOOTLOADER_CFG_APP_HEAD_ADDR)) != BOOTLOADER_CFG_HEAD_MASK) || (TailData != BOOTLOADER_CFG_TAIL_MASK) || ((*((uint32_t *)BOOTLOADER_CFG_APP_CRC_ADDR)) == BOOTLOADER_CFG_FLASH_FILL_PAD_VALUE))
                    {
                        /* Active mcu enter into boot */
                        sBootloader_Cb.bootActive          = true;
                        sBootloader_Cb.checkInfo.crcResult = false;
                        sBootloader_Cb.bootSubState        = BOOTLOADER_SUB_STATE_END;
                        sBootloader_Cb.checkInfo.checkObj  = BOOLOADER_OBJECT_NONE;
                    }
                    else
                    {
                        sBootloader_Cb.checkInfo.crcResult = false;
                        /* If the current app is ok and no bootloader process action, then enter into exit state */
                        if(sBootloader_Cb.bootActive == false)
                        {
                            Bootloader_SetBootState(BOOTLOADER_STATE_EXIT);
                        }
                        else
                        {
                            /* Stay in check state, wait for next command */
                            sBootloader_Cb.bootActive         = true;
                            sBootloader_Cb.bootSubState       = BOOTLOADER_SUB_STATE_END;
                            sBootloader_Cb.checkInfo.checkObj = BOOLOADER_OBJECT_NONE;
                        }
                    }
                }
            }
            else if(sBootloader_Cb.checkInfo.checkObj == BOOLOADER_OBJECT_FLASH_DRIVER)
            {
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 0u
                calculateCrc = sBootloader_Cb.pCallBackFunctionList->calculateCheckSumFunction(BOOTLOADER_CFG_INIT_CRC_VALUE,
                                                                                               (const uint8_t *)sBootloader_Cb.checkInfo.CheckStartAddress,
                                                                                               sBootloader_Cb.checkInfo.CheckmemSize,
                                                                                               true);

                if(calculateCrc == sBootloader_Cb.checkInfo.checkCrc)
                {
                    Bootloader_FlsDrvUpdate();
                    if(sBootloader_Cb.flsDrvInfo.flashDriverDownloaded == true)
                    {
                        sBootloader_Cb.checkInfo.crcResult = true;
                        sBootloader_Cb.bootSubState        = BOOTLOADER_SUB_STATE_END;
                        sBootloader_Cb.checkInfo.checkObj  = BOOLOADER_OBJECT_NONE;
                    }
                    else
                    {
                        sBootloader_Cb.checkInfo.crcResult = false;
                        sBootloader_Cb.bootSubState        = BOOTLOADER_SUB_STATE_END;
                        sBootloader_Cb.checkInfo.checkObj  = BOOLOADER_OBJECT_NONE;
                    }
                }
#else
                Bootloader_FlsDrvUpdate();
                sBootloader_Cb.checkInfo.crcResult = true;
                sBootloader_Cb.bootSubState        = BOOTLOADER_SUB_STATE_END;
                sBootloader_Cb.checkInfo.checkObj  = BOOLOADER_OBJECT_NONE;
#endif
            }
            else
            {
            }

            /* Check the action of trigger event, if event is from EOL, then should response the result after finished check action */
            if(sBootloader_Cb.checkInfo.activeCheckReq == false)
            {
                uint8_t rspBuffer[8] = {0x71, 0x01, 0x02, 0x02};

                if(sBootloader_Cb.checkInfo.crcResult == true)
                {
                    rspBuffer[4] = 0x00;
                }
                else
                {
                    rspBuffer[4] = 0x01;
                }

                sBootloader_Cb.pCallBackFunctionList->udsPositiveRespFunction(rspBuffer, 5);

                sBootloader_Cb.bootSubState = BOOTLOADER_SUB_STATE_END;
            }
            else
            {
                sBootloader_Cb.checkInfo.activeCheckReq = false;
            }
        }
    }
    else
    {
        /* Nothing to do , just wait for the next event */
    }
}

/*! \brief Bootloader exit state process
 *
 * This function excute the action if bootloader is in exist state
 *
 * \param[in] void
 *
 * \return void
 */
static void Bootloader_ExitState(void)
{
    if((sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_NONE) || (sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_DOING))
    {
        if(sBootloader_Cb.resetActionReady == true)
        {
            Bootloader_DeInit();

            /* Reset */
            sBootloader_Cb.pCallBackFunctionList->swResetFunction();
        }
        else
        {
            if((sBootloader_Cb.stayInBootTimeoutCnt >= BOOTLOADER_CFG_BOOT_DURATION_MS) && (sBootloader_Cb.bootActive == false))
            {
                Bootloader_DeInit();

                /* Jump to App */
                sBootloader_Cb.pCallBackFunctionList->jumpFunction();
            }
            else
            {
                sBootloader_Cb.bootSubState = BOOTLOADER_SUB_STATE_DOING;
            }
        }
    }
    else
    {
        /* Nothing to do , just wait for the next event */
    }
}

/*! \brief Bootloader error state process
 *
 * This function excute the action if bootloader is in errored state
 *
 * \param[in] void
 *
 * \return void
 */
static void Bootloader_ErrorState(void)
{
    /* TODO: */
    if((sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_NONE) || (sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_DOING))
    {
        /* TODO: */
    }
    else
    {
        /* Nothing to do , just wait for the next event */
    }
}

void Bootloader_CallBackFunctionInstall()
{
}

/*! \brief Initialize bootloader control block information
 *
 * This function Initialize bootloader control block information
 *
 * \param[in] flashInstanceAddress : flashdriver instance address
 * \param[in] pCallbackFuncList : call back function list
 *
 * \return void
 */
void Bootloader_Init(uint32_t flashInstanceAddress, const Bootloader_CallBackFuncListType *pCallbackFuncList)
{
    memset(&sBootloader_Cb, 0, sizeof(sBootloader_Cb));

    sBootloader_Cb.pCallBackFunctionList = pCallbackFuncList;

#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 0u
    /* Clear flash driver in ram */
    memset((uint8_t *)BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR, 0, BOOTLOADER_CFG_FLS_DRV_RAM_CODE_SIZE + BOOTLOADER_CFG_FLS_DRV_RAM_CRC_SIZE);
#endif

    sBootloader_Cb.flashInstanceAddress = flashInstanceAddress;
    sBootloader_Cb.pgmInfo.pgmCrc       = 0;

#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN != 0u
    sBootloader_Cb.pCallBackFunctionList->flsInitFunction(flashInstanceAddress);
#else
    /* Update the flash instance */
    *((uint32_t *)BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR) = flashInstanceAddress;
#endif
    /* TODO: DEBUG FLASH DRIVER, recover it after debug, 2024/04/25 */
    sBootloader_Cb.bootActive = sBootloader_Cb.pCallBackFunctionList->getBootReqFunction();
    if(sBootloader_Cb.bootActive == true)
    {
        /* Emerge boot request */
        sBootloader_Cb.stayInBootTimeoutCnt = 0;
        sBootloader_Cb.pCallBackFunctionList->setSessionModeFunction(2);
    }
}

/*! \brief Set the bootloader into the target state
 *
 * This function Set the bootloader into the target state
 *
 * \param[in] state : the target state to be switch to
 *
 * \return void
 */
void Bootloader_SetBootState(Bootloader_StateType state)
{
    sBootloader_Cb.bootSubState         = BOOTLOADER_SUB_STATE_NONE;
    sBootloader_Cb.bootState            = state;
    sBootloader_Cb.stayInBootTimeoutCnt = 0;
}

/*! \brief Get the bootloader current state
 *
 * This function Get the bootloader current state
 *
 * \param[in] void
 *
 * \return : return the current state
 */
Bootloader_StateType Bootloader_GetBootState(void)
{
    return sBootloader_Cb.bootState;
}

/*! \brief Get the bootloader current sub state
 *
 * This function Get the bootloader current sub state
 *
 * \param[in] void
 *
 * \return : return the current sub state
 */
Bootloader_SubStateType Bootloader_GetBootSubState(void)
{
    return sBootloader_Cb.bootSubState;
}

/*! \brief Get the bootloader current crc result
 *
 * This function Get the bootloader current crc result
 *
 * \param[in] void
 *
 * \return : return the current crc result
 */
bool Bootloader_GetCrcResult(void)
{
    return sBootloader_Cb.checkInfo.crcResult;
}

/*! \brief Set emerge boot request
 *
 * This function Set emerge boot request
 *
 * \param[in] void
 *
 * \return void
 */
void Bootloader_EventEmergeBootRequest(bool requestEn)
{
    if(sBootloader_Cb.bootActive == false)
    {
        sBootloader_Cb.emergeBootReqEn      = requestEn;
        sBootloader_Cb.stayInBootTimeoutCnt = 0;
        sBootloader_Cb.bootActive           = true;
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 0u
        sBootloader_Cb.flsDrvInfo.flashDriverDownloaded = false;
#endif
    }

    /* Set the current state into end sub state */
    sBootloader_Cb.bootSubState = BOOTLOADER_SUB_STATE_END;
}

/*! \brief Set the reset cup request
 *
 * This function Set the reset cup request
 *
 * \param[in] void
 *
 * \return void
 */
void Bootloader_EventResetRequest(bool requestEn,uint8_t resetMode)
{
    sBootloader_Cb.resetReqEn = requestEn;
    sBootloader_Cb.resetReqMode = resetMode;
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 0u
    sBootloader_Cb.flsDrvInfo.flashDriverDownloaded = false;
#endif
}

/*! \brief Set the erase information
 *
 * This function set the erase information about where to be start to be erased and how many bytes to be erased
 *
 * \param[in] startAddress : where to be start to be erased
 * \param[in] bytesNum : how many bytes to be erased
 *
 * \return void
 */
void Bootloader_EventEraseRequest(uint32_t startAddress, uint32_t bytesNum)
{
    sBootloader_Cb.eraseInfo.eraseLength       = bytesNum;
    sBootloader_Cb.eraseInfo.eraseStartAddress = startAddress;
    sBootloader_Cb.pgmInfo.pgmStartTriggerEn   = false;
    sBootloader_Cb.pgmInfo.pgmReadyEn          = false;

    /* Update the dowmloader object */
    if((startAddress == BOOTLOADER_CFG_APP_START_ADDR) && (bytesNum == 0x28000))
    {
        sBootloader_Cb.downloadObj                = BOOLOADER_OBJECT_APP_CODE;
        sBootloader_Cb.eraseInfo.eraseSectionNums = (sBootloader_Cb.eraseInfo.eraseLength + FLS_ERASE_SECTION_ADDR_ALIGNED_MASK) / FLS_ERASE_SECTION_SIZE;
    }
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 0u
    else if((startAddress == BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR) && (bytesNum <= (BOOTLOADER_CFG_FLS_DRV_RAM_CODE_SIZE + BOOTLOADER_CFG_FLS_DRV_RAM_CRC_SIZE)))
    {
        sBootloader_Cb.downloadObj                      = BOOLOADER_OBJECT_FLASH_DRIVER;
        sBootloader_Cb.flsDrvInfo.flashDriverDownloaded = false;
        sBootloader_Cb.eraseInfo.eraseSectionNums       = ((BOOTLOADER_CFG_FLS_DRV_RAM_CODE_SIZE + BOOTLOADER_CFG_FLS_DRV_RAM_CRC_SIZE) + FLS_ERASE_SECTION_ADDR_ALIGNED_MASK) / FLS_ERASE_SECTION_SIZE;
    }
#else
    else if(startAddress == BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR)
    {
        sBootloader_Cb.downloadObj                = BOOLOADER_OBJECT_FLASH_DRIVER;
        sBootloader_Cb.eraseInfo.eraseSectionNums = ((BOOTLOADER_CFG_FLS_DRV_RAM_CODE_SIZE + BOOTLOADER_CFG_FLS_DRV_RAM_CRC_SIZE) + FLS_ERASE_SECTION_ADDR_ALIGNED_MASK) / FLS_ERASE_SECTION_SIZE;
    }

#endif

    Bootloader_SetBootState(BOOTLOADER_STATE_ERASE);
}

/*! \brief Set program information
 *
 * This function Set program information about where to be start to be write and how many bytes to be write
 *
 * \param[in] startAddress : where to be start to be write
 * \param[in] bytesNum : how many bytes to be write
 *
 * \return void
 */
void Bootloader_EventPgmReady(uint32_t startAddress, uint32_t bytesNum)
{
    sBootloader_Cb.pgmInfo.pgmReadyEn        = true;
    sBootloader_Cb.pgmInfo.pgmStartTriggerEn = false;
    sBootloader_Cb.pgmInfo.pgmStartAddress   = startAddress;
    sBootloader_Cb.pgmInfo.pgmMemSize        = bytesNum;
    sBootloader_Cb.pgmInfo.pgmMemCnt         = 0;
    sBootloader_Cb.pgmInfo.pgmBlockByteCnt   = 0;
    sBootloader_Cb.pgmInfo.pgmBlockSn        = 0;
    /* Update the dowmloader object */
    if((startAddress == BOOTLOADER_CFG_APP_START_ADDR) && (bytesNum >= 0x6000 && bytesNum <= 0x28000))
    {
        sBootloader_Cb.downloadObj = BOOLOADER_OBJECT_APP_CODE;
    }
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 0u
    else if((startAddress == BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR) && (bytesNum <= (BOOTLOADER_CFG_FLS_DRV_RAM_CODE_SIZE + BOOTLOADER_CFG_FLS_DRV_RAM_CRC_SIZE)))
    {
        sBootloader_Cb.downloadObj = BOOLOADER_OBJECT_FLASH_DRIVER;
    }
#else
    else if(startAddress == BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR)
    {
        sBootloader_Cb.downloadObj = BOOLOADER_OBJECT_FLASH_DRIVER;
    }

#endif

    Bootloader_SetBootState(BOOTLOADER_STATE_PGM);
}

/*! \brief Trigger the pgm start
 *
 * This function indication that can be write datas into flash
 *
 * \param[in] pDatabuffer : stored the datas to be written into flash
 * \param[in] bytesNum : how many bytes to be write into local buffer
 * \param[in] blockSn : current block sn number
 *
 * \return true: ecu start to pgm, false: ecu don't start to pgm
 */
bool Bootloader_EventPgmData(const uint8_t *pDatabuffer, uint16_t bytesNum, uint8_t blockSn)
{
    uint32_t i   = 0;
    bool     ret = false;

    /* Copy data into local */
    for(i = 0; i < bytesNum; i++)
    {
        sBootloader_Cb.pgmInfo.pgmData[sBootloader_Cb.pgmInfo.pgmBlockByteCnt + i] = pDatabuffer[i];
    }

    sBootloader_Cb.pgmInfo.pgmBlockByteCnt += bytesNum;
    sBootloader_Cb.pgmInfo.pgmBlockSn = blockSn;
    if(sBootloader_Cb.pgmInfo.pgmBlockByteCnt >= BOOTLOADER_CFG_PGM_BYTES_SIZE)
    {
        sBootloader_Cb.pgmInfo.pgmStartTriggerEn = true;
        ret                                      = true;
        sBootloader_Cb.pgmInfo.pgmBlockByteCnt   = 0;
    }

    return ret;
}

/*! \brief data check request
 *
 * This function indication that data check request
 *
 * \param[in] memStart : memory start address
 * \param[in] memSize : memory size
 * \param[in] crc : crc result from EOL
 *
 * \return void
 */
void Bootloader_EventDataCheckRequest(uint32_t memStart, uint32_t memSize, uint32_t crc)
{
    sBootloader_Cb.bootSubState = BOOTLOADER_SUB_STATE_DOING;
    sBootloader_Cb.bootState    = BOOTLOADER_STATE_CHECK;

    sBootloader_Cb.checkInfo.checkCrc          = crc;
    sBootloader_Cb.checkInfo.CheckmemSize      = memSize;
    sBootloader_Cb.checkInfo.CheckStartAddress = memStart;
    sBootloader_Cb.checkInfo.crcResult         = false;

    if((memStart == BOOTLOADER_CFG_APP_START_ADDR) && (memSize >= 0x6000 && memSize <= 0x27000))
    {
        sBootloader_Cb.checkInfo.checkObj = BOOLOADER_OBJECT_APP_CODE;
    }
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 0u
    else if((memStart == BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR) && (memSize == 0xc00))
    {
        sBootloader_Cb.checkInfo.checkObj = BOOLOADER_OBJECT_FLASH_DRIVER;
    }
#else
    else if(memStart == BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR)
    {
        sBootloader_Cb.checkInfo.checkObj = BOOLOADER_OBJECT_FLASH_DRIVER;
    }
#endif
}

/*! \brief Bootloader main state process
 *
 * This function process the bootloader variant state
 *
 * \param[in] void0
 *
 * \return void
 */
void Bootloader_StateProc(void)
{
    if(sBootloader_Cb.bootState < BOOTLOADER_STATE_NUM)
    {
        if(sBootloader_StateFuncTbl[sBootloader_Cb.bootState] != 0u)
        {
            sBootloader_StateFuncTbl[sBootloader_Cb.bootState]();
        }
    }
}

/*! \brief Calculate the stay in boot duration
 *
 * This function calculate the timeout when before jump to app if app is ok
 *
 * \param[in] periodms : add in every period
 *
 * \return void
 */
void Bootloader_TimingProcess(uint8_t periodms)
{
    if((sBootloader_Cb.bootState == BOOTLOADER_STATE_INIT) || (sBootloader_Cb.bootState == BOOTLOADER_STATE_EXIT))
    {
        sBootloader_Cb.stayInBootTimeoutCnt += periodms;
    }

    if(sBootloader_Cb.resetReqEn == true)
    {
        sBootloader_Cb.resetReqEn       = false;
        sBootloader_Cb.resetActionReady = true;
        Bootloader_SetBootState(BOOTLOADER_STATE_EXIT);
    }
}
