/*
 * 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_cfg.h"
#include "fls.h"
#include "bootloader.h"
#include "uds.h"
#include "uds_user.h"
#include "mcu.h"
#include "SEGGER_RTT.h"

/*******************************************************************************
 * the defines
 ******************************************************************************/
static bool     Bootloader_GetBootReq(void);
static uint32_t Bootloader_CalculateCheckSum(uint32_t initCrcValue, const uint8_t *dataBuffer, uint32_t bytesNum, bool inverted);
static void     Bootloader_JumpToApp(void);
static void     Bootloader_Reset(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);

extern McuType mcu;

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

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

#pragma location = ".no_init"
static uint32_t sBootloader_Req;

#pragma location                                       = ".bootloaderInfo"
__root const Bootloade_CfgInfoType sBootloader_Version = {
    .HWVersion = "HW01",
    .BLVersion = "BL0101",
    .bootBuildDate = __DATE__,
    .bootBuildTime = __TIME__,
    .ECUName = "WK-K86",
}; 

static Bootloader_CbType sBootloader_Cb;

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

static const unsigned int crc32_table[] = {
    0x00000000uL,
    0x77073096uL,
    0xEE0E612CuL,
    0x990951BAuL,
    0x076DC419uL,
    0x706AF48FuL,
    0xE963A535uL,
    0x9E6495A3uL,
    0x0EDB8832uL,
    0x79DCB8A4uL,
    0xE0D5E91EuL,
    0x97D2D988uL,
    0x09B64C2BuL,
    0x7EB17CBDuL,
    0xE7B82D07uL,
    0x90BF1D91uL,
    0x1DB71064uL,
    0x6AB020F2uL,
    0xF3B97148uL,
    0x84BE41DEuL,
    0x1ADAD47DuL,
    0x6DDDE4EBuL,
    0xF4D4B551uL,
    0x83D385C7uL,
    0x136C9856uL,
    0x646BA8C0uL,
    0xFD62F97AuL,
    0x8A65C9ECuL,
    0x14015C4FuL,
    0x63066CD9uL,
    0xFA0F3D63uL,
    0x8D080DF5uL,
    0x3B6E20C8uL,
    0x4C69105EuL,
    0xD56041E4uL,
    0xA2677172uL,
    0x3C03E4D1uL,
    0x4B04D447uL,
    0xD20D85FDuL,
    0xA50AB56BuL,
    0x35B5A8FAuL,
    0x42B2986CuL,
    0xDBBBC9D6uL,
    0xACBCF940uL,
    0x32D86CE3uL,
    0x45DF5C75uL,
    0xDCD60DCFuL,
    0xABD13D59uL,
    0x26D930ACuL,
    0x51DE003AuL,
    0xC8D75180uL,
    0xBFD06116uL,
    0x21B4F4B5uL,
    0x56B3C423uL,
    0xCFBA9599uL,
    0xB8BDA50FuL,
    0x2802B89EuL,
    0x5F058808uL,
    0xC60CD9B2uL,
    0xB10BE924uL,
    0x2F6F7C87uL,
    0x58684C11uL,
    0xC1611DABuL,
    0xB6662D3DuL,
    0x76DC4190uL,
    0x01DB7106uL,
    0x98D220BCuL,
    0xEFD5102AuL,
    0x71B18589uL,
    0x06B6B51FuL,
    0x9FBFE4A5uL,
    0xE8B8D433uL,
    0x7807C9A2uL,
    0x0F00F934uL,
    0x9609A88EuL,
    0xE10E9818uL,
    0x7F6A0DBBuL,
    0x086D3D2DuL,
    0x91646C97uL,
    0xE6635C01uL,
    0x6B6B51F4uL,
    0x1C6C6162uL,
    0x856530D8uL,
    0xF262004EuL,
    0x6C0695EDuL,
    0x1B01A57BuL,
    0x8208F4C1uL,
    0xF50FC457uL,
    0x65B0D9C6uL,
    0x12B7E950uL,
    0x8BBEB8EAuL,
    0xFCB9887CuL,
    0x62DD1DDFuL,
    0x15DA2D49uL,
    0x8CD37CF3uL,
    0xFBD44C65uL,
    0x4DB26158uL,
    0x3AB551CEuL,
    0xA3BC0074uL,
    0xD4BB30E2uL,
    0x4ADFA541uL,
    0x3DD895D7uL,
    0xA4D1C46DuL,
    0xD3D6F4FBuL,
    0x4369E96AuL,
    0x346ED9FCuL,
    0xAD678846uL,
    0xDA60B8D0uL,
    0x44042D73uL,
    0x33031DE5uL,
    0xAA0A4C5FuL,
    0xDD0D7CC9uL,
    0x5005713CuL,
    0x270241AAuL,
    0xBE0B1010uL,
    0xC90C2086uL,
    0x5768B525uL,
    0x206F85B3uL,
    0xB966D409uL,
    0xCE61E49FuL,
    0x5EDEF90EuL,
    0x29D9C998uL,
    0xB0D09822uL,
    0xC7D7A8B4uL,
    0x59B33D17uL,
    0x2EB40D81uL,
    0xB7BD5C3BuL,
    0xC0BA6CADuL,
    0xEDB88320uL,
    0x9ABFB3B6uL,
    0x03B6E20CuL,
    0x74B1D29AuL,
    0xEAD54739uL,
    0x9DD277AFuL,
    0x04DB2615uL,
    0x73DC1683uL,
    0xE3630B12uL,
    0x94643B84uL,
    0x0D6D6A3EuL,
    0x7A6A5AA8uL,
    0xE40ECF0BuL,
    0x9309FF9DuL,
    0x0A00AE27uL,
    0x7D079EB1uL,
    0xF00F9344uL,
    0x8708A3D2uL,
    0x1E01F268uL,
    0x6906C2FEuL,
    0xF762575DuL,
    0x806567CBuL,
    0x196C3671uL,
    0x6E6B06E7uL,
    0xFED41B76uL,
    0x89D32BE0uL,
    0x10DA7A5AuL,
    0x67DD4ACCuL,
    0xF9B9DF6FuL,
    0x8EBEEFF9uL,
    0x17B7BE43uL,
    0x60B08ED5uL,
    0xD6D6A3E8uL,
    0xA1D1937EuL,
    0x38D8C2C4uL,
    0x4FDFF252uL,
    0xD1BB67F1uL,
    0xA6BC5767uL,
    0x3FB506DDuL,
    0x48B2364BuL,
    0xD80D2BDAuL,
    0xAF0A1B4CuL,
    0x36034AF6uL,
    0x41047A60uL,
    0xDF60EFC3uL,
    0xA867DF55uL,
    0x316E8EEFuL,
    0x4669BE79uL,
    0xCB61B38CuL,
    0xBC66831AuL,
    0x256FD2A0uL,
    0x5268E236uL,
    0xCC0C7795uL,
    0xBB0B4703uL,
    0x220216B9uL,
    0x5505262FuL,
    0xC5BA3BBEuL,
    0xB2BD0B28uL,
    0x2BB45A92uL,
    0x5CB36A04uL,
    0xC2D7FFA7uL,
    0xB5D0CF31uL,
    0x2CD99E8BuL,
    0x5BDEAE1DuL,
    0x9B64C2B0uL,
    0xEC63F226uL,
    0x756AA39CuL,
    0x026D930AuL,
    0x9C0906A9uL,
    0xEB0E363FuL,
    0x72076785uL,
    0x05005713uL,
    0x95BF4A82uL,
    0xE2B87A14uL,
    0x7BB12BAEuL,
    0x0CB61B38uL,
    0x92D28E9BuL,
    0xE5D5BE0DuL,
    0x7CDCEFB7uL,
    0x0BDBDF21uL,
    0x86D3D2D4uL,
    0xF1D4E242uL,
    0x68DDB3F8uL,
    0x1FDA836EuL,
    0x81BE16CDuL,
    0xF6B9265BuL,
    0x6FB077E1uL,
    0x18B74777uL,
    0x88085AE6uL,
    0xFF0F6A70uL,
    0x66063BCAuL,
    0x11010B5CuL,
    0x8F659EFFuL,
    0xF862AE69uL,
    0x616BFFD3uL,
    0x166CCF45uL,
    0xA00AE278uL,
    0xD70DD2EEuL,
    0x4E048354uL,
    0x3903B3C2uL,
    0xA7672661uL,
    0xD06016F7uL,
    0x4969474DuL,
    0x3E6E77DBuL,
    0xAED16A4AuL,
    0xD9D65ADCuL,
    0x40DF0B66uL,
    0x37D83BF0uL,
    0xA9BCAE53uL,
    0xDEBB9EC5uL,
    0x47B2CF7FuL,
    0x30B5FFE9uL,
    0xBDBDF21CuL,
    0xCABAC28AuL,
    0x53B39330uL,
    0x24B4A3A6uL,
    0xBAD03605uL,
    0xCDD70693uL,
    0x54DE5729uL,
    0x23D967BFuL,
    0xB3667A2EuL,
    0xC4614AB8uL,
    0x5D681B02uL,
    0x2A6F2B94uL,
    0xB40BBE37uL,
    0xC30C8EA1uL,
    0x5A05DF1BuL,
    0x2D02EF8DuL};

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

/*! \brief Get boot request flag from app
 *
 * This function get the boot request event that is from app
 *
 * \param[in] void
 *
 * \return void
 */
static bool Bootloader_GetBootReq(void)
{
    bool ret = true;

    /* Make sure the read action for no-init ram zone is in system reset happen */
    if(ResetDrv_IsResetCaused(&mcu.resetDrv, RESETDRV_SOURCE_POR) == false)
    {
        if(ResetDrv_IsResetCaused(&mcu.resetDrv, RESETDRV_SOURCE_SYS) == true)
        {
            if(sBootloader_Req != BOOTLOADER_CFG_REQ_ACTIVE)
            {
                ret = false;
            }
            else
            {
                sBootloader_Req = 0;
            }
        }
        else
        {
            ret = false;
        }
    }
    else
    {
        ret = false;
    }

    return ret;
}

/*! \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 Calculate the app crc
 *
 * This function get the boot request event that is from app
 *
 * \param[in] initCrcValue : the old crc value
 * \param[in] dataBuffer : the start addresss to take in calculating crc
 * \param[in] bytesNum : how many bytes to be take in
 *
 * \return the calculate the crc value
 */
static uint32_t Bootloader_CalculateCheckSum(uint32_t initCrcValue, const uint8_t *dataBuffer, uint32_t bytesNum, bool inverted)
{
    uint32_t tCrc = initCrcValue;
    uint32_t i    = 0;

    for(i = 0; i < bytesNum; i++)
    {
        tCrc = crc32_table[(tCrc ^ dataBuffer[i]) & 0xff] ^ (tCrc >> 8);
    }

    if(inverted == true)
    {
        tCrc = 0xFFFFFFFF - tCrc;
    }

    return tCrc;
}

/*! \brief Jump to app
 *
 * This function jump to app from boot
 *
 * \param[in] void
 *
 * \return void
 */
static void Bootloader_JumpToApp(void)
{
    uint32_t        appEntry = 0, appStack = 0;
    static uint32_t statckPointer = 0;
    static void (*pJumpToApp)(void);

    IrqDrv_DisableGlobalInterrupt();
    FlexCanDrv_SoftReset(&mcu.flexCanDrv);
    memset(&sBootloader_Cb, 0, sizeof(sBootloader_Cb));
    /* MSP */
    appStack = *(uint32_t *)BOOTLOADER_CFG_APP_START_ADDR;
    /* PC */
    appEntry = *(uint32_t *)(BOOTLOADER_CFG_APP_START_ADDR + 4);

    pJumpToApp    = (void (*)(void))appEntry;
    statckPointer = appStack;
    /* Set the MSP */
    __set_PSP(statckPointer);
    __set_MSP(statckPointer);
    
    SCB->VTOR = (uint32_t)BOOTLOADER_CFG_APP_START_ADDR;
    /* Jump action */
    pJumpToApp();
}

/*! \brief Reset the cpu
 *
 * This function reset the cpu if get reset request
 *
 * \param[in] void
 *
 * \return void
 */
static void Bootloader_Reset(void)
{
    /* TODO */
    memset(&sBootloader_Cb, 0, sizeof(sBootloader_Cb));
    IrqDrv_DisableGlobalInterrupt();//关闭中断，不加会导致无法复位
    ResetDrv_SoftwareResetModule(&mcu.resetDrv, RESETDRV_SWRESET_SYS);
}

/*! \brief Reset the cpu
 *
 * This function reset the cpu if get reset request
 *
 * \param[in] void
 *
 * \return void
 */
static void Bootloader_HardReset(void)
{
    /* TODO */
    //SBC_Write_RegField(SBC_M_S_CTRL, SBC_M_S_CTRL_MODE_Msk, SBC_M_S_CTRL_MODE_Pos, SBC_MODE_RESET, NULL);
}

/*! \brief pgm positive respose to eol
 *
 * This function response to EOL when finished the current block data pgm
 *
 * \param[in] void
 *
 * \return void
 */
static void Bootloader_PositiveResp(void)
{
    uint8_t rspBuffer[8];

    rspBuffer[0] = 0x76;
    rspBuffer[1] = sBootloader_Cb.pgmInfo.pgmBlockSn;

    sBootloader_Cb.positiveRespFunction(sBootloader_Cb.udsObj, rspBuffer, 2);
}

/*! \brief pgm negative respose to eol
 *
 * This function response to EOL when finished the current block data pgm
 *
 * \param[in] sid : service id
 * \param[in] nrc : negative response code
 *
 * \return void
 */
static void Bootloader_NegativeResp(uint8_t sid, uint8_t nrc)
{
    sBootloader_Cb.negativeRespFunction(sBootloader_Cb.udsObj, sid, nrc);
}

/*! \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
    uint32_t localCrc = 0;
	uint8_t i = 0;
    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.startFlsPgmAddr   = *((uint32_t *)(BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR + 12));

    /* Check the current download data is for flash driver */
    if((sBootloader_Cb.pgmInfo.pgmStartAddress == BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR) && (sBootloader_Cb.pgmInfo.pgmMemSize == (BOOTLOADER_CFG_FLS_DRV_RAM_CODE_SIZE + BOOTLOADER_CFG_FLS_DRV_RAM_CRC_SIZE)))
    {
        sBootloader_Cb.pgmInfo.pgmCrc = Bootloader_CalculateCheckSum(BOOTLOADER_CFG_INIT_CRC_VALUE,
                                                                     (const uint8_t *)sBootloader_Cb.pgmInfo.pgmStartAddress,
                                                                     sBootloader_Cb.pgmInfo.pgmMemSize - BOOTLOADER_CFG_FLS_DRV_RAM_CRC_SIZE,
                                                                     true);
    }
    /*
    for(i = 0; i< 4; i++)
    {
    	localCrc <<= 8;
    	localCrc += (*((uint8_t *)(BOOTLOADER_CFG_FLS_DRV_RAM_CRC_ADDR + i)));
    }
    */
    localCrc  = (*((uint8_t *)BOOTLOADER_CFG_FLS_DRV_RAM_CRC_ADDR));

    if(sBootloader_Cb.pgmInfo.pgmCrc == localCrc)
    {
        sBootloader_Cb.flsDrvInfo.pFls_InitFunction  = (pFls_InitFunctionPtr)sBootloader_Cb.flsDrvInfo.startFlsInitAddr;
        sBootloader_Cb.flsDrvInfo.pFls_EraseFunction = (pFls_EraseFunctionPtr)sBootloader_Cb.flsDrvInfo.startFlsEraseAddr;
        sBootloader_Cb.flsDrvInfo.pFls_WriteFunction = (pFls_WriteFunctionPtr)sBootloader_Cb.flsDrvInfo.startFlsPgmAddr;

        sBootloader_Cb.flsDrvInfo.pFls_InitFunction(sBootloader_Cb.flashDrvObj);
        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.udsObj->session = UDS_SESSION_PROGRAMMING;
        }
        else
        {
            Bootloader_SetBootState(BOOTLOADER_STATE_CHECK);
        }
    }
}

/*! \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;
                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 */
                    IrqDrv_DisableGlobalInterrupt();
#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 = Fls_Erase(sBootloader_Cb.eraseInfo.eraseStartAddress, 1);
#else
                    /* Check flash driver has been download into ram */
                    if(sBootloader_Cb.flsDrvInfo.flashDriverDownloaded == true)
                    {
                        ret = sBootloader_Cb.flsDrvInfo.pFls_EraseFunction(sBootloader_Cb.eraseInfo.eraseStartAddress, 1);
                    }
                    else
                    {
                        ret = FLS_STD_RETURN_FLASH_DRIVER_INVALID;
                    }
#endif

                    if(ret == FLS_STD_RETURN_OK)
                    {
                        /* Erase other sectors */
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u
                        ret = Fls_Erase(eraseStartAddress + FLS_ERASE_SECTION_SIZE,
                                        sBootloader_Cb.eraseInfo.eraseSectionNums - 1u);
#else
                        ret = sBootloader_Cb.flsDrvInfo.pFls_EraseFunction(eraseStartAddress + FLS_ERASE_SECTION_SIZE,
                                                                           sBootloader_Cb.eraseInfo.eraseSectionNums - 1u);
#endif
                    }
#else
                    uint32_t eraseSectorNums = 0;
                    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 = Fls_Erase(eraseStartAddress,
                                    eraseSectorNums);
#else
                    /* Check flash driver has been download into ram */
                    if(sBootloader_Cb.flsDrvInfo.flashDriverDownloaded == true)
                    {
                        ret = sBootloader_Cb.flsDrvInfo.pFls_EraseFunction(eraseStartAddress,
                                                                           eraseSectorNums);
                    }
                    else
                    {
                        ret = FLS_STD_RETURN_FLASH_DRIVER_INVALID;
                    }
#endif

#endif
                    /* After erased, restored the global interrupt */
                    IrqDrv_EnableGlobalInterrupt();
                }
                else
                {
#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
                }

                if(ret == FLS_STD_RETURN_OK)
                {
#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;
#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;
                    }
                    else
                    {
                        sBootloader_Cb.bootSubState = BOOTLOADER_SUB_STATE_DOING;
                    }
#endif
                }
                else
                {
                    sBootloader_Cb.eraseInfo.eraseSectionNums = 0;
                    sBootloader_Cb.eraseInfo.eraseLength      = 0;
                    sBootloader_Cb.eraseInfo.eraseSectionCnt  = 0;
                    Bootloader_SetBootState(BOOTLOADER_STATE_ERR);
                }
            }
        }
    }
    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)
                {
                    uint8_t pgmPackageCnt = 0, i = 0;

                    pgmPackageCnt = (BOOTLOADER_CFG_PGM_BYTES_SIZE + FLS_PGM_SECTION_SIZE - 1u) / FLS_PGM_SECTION_SIZE;
                    IrqDrv_DisableGlobalInterrupt();

#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u
                    for (i = 0; i< pgmPackageCnt;i++)
                    {
                        ret = Fls_Write(sBootloader_Cb.pgmInfo.pgmStartAddress + sBootloader_Cb.pgmInfo.pgmMemCnt + i * FLS_PGM_SECTION_SIZE,
                                        (const uint8_t *)&sBootloader_Cb.pgmInfo.pgmData[i * FLS_PGM_SECTION_SIZE],
                                        FLS_PGM_SECTION_SIZE);

                        if (ret != FLS_STD_RETURN_OK)
                        {
                            break;
                        }
                    }
#else
                    /* Check flash driver has been download into ram */
                    if(sBootloader_Cb.flsDrvInfo.flashDriverDownloaded == true)
                    {
                        for(i = 0; i < pgmPackageCnt; i++)
                        {
                            ret = sBootloader_Cb.flsDrvInfo.pFls_WriteFunction(sBootloader_Cb.pgmInfo.pgmStartAddress + sBootloader_Cb.pgmInfo.pgmMemCnt + i * FLS_PGM_SECTION_SIZE,
                                            (const uint8_t *)&sBootloader_Cb.pgmInfo.pgmData[i * FLS_PGM_SECTION_SIZE],
                                            FLS_PGM_SECTION_SIZE);

                            if(ret != FLS_STD_RETURN_OK)
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        ret = FLS_STD_RETURN_FLASH_DRIVER_INVALID;
                    }
#endif
                    IrqDrv_EnableGlobalInterrupt();
                }
                else
                {
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u
                    /* Nothing to do, no need to copy flash driver from outer side */
#else
                    /* 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)
                {
                    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 */
                    Bootloader_PositiveResp();
                }
                else
                {
                    sBootloader_Cb.pgmInfo.pgmReadyEn = false;
                    sBootloader_Cb.pgmInfo.pgmMemSize = 0;
                    sBootloader_Cb.pgmInfo.pgmMemCnt  = 0;
                    Bootloader_SetBootState(BOOTLOADER_STATE_ERR);
                }

                /* Clear the download trigger event, wait for the next download data if need */
                sBootloader_Cb.pgmInfo.pgmStartTriggerEn = false;
                sBootloader_Cb.pgmInfo.pgmBlockByteCnt   = 0;
            }
        }
    }
    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
 */
static void Bootloader_CheckState(void)
{
    if((sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_NONE) || (sBootloader_Cb.bootSubState == BOOTLOADER_SUB_STATE_DOING))
    {
        sBootloader_Cb.bootSubState = BOOTLOADER_SUB_STATE_DOING;
        if((sBootloader_Cb.downloadObj == BOOLOADER_OBJECT_APP_CODE) || (sBootloader_Cb.downloadObj == BOOLOADER_OBJECT_NONE))
        {
            if((Fls_CheckWriteState(BOOTLOADER_CFG_APP_HEAD_ADDR) == FLS_STD_RETURN_OK) && (Fls_CheckWriteState(BOOTLOADER_CFG_APP_CRC_ADDR - 4u) == FLS_STD_RETURN_OK))
            {
                /* Check App Head */
                if((*((uint32_t *)BOOTLOADER_CFG_APP_HEAD_ADDR)) != BOOTLOADER_CFG_HEAD_MASK)
                {
                    /* Active mcu enter into boot */
                    sBootloader_Cb.bootActive        = true;
                    sBootloader_Cb.bootSubState      = BOOTLOADER_SUB_STATE_END;
                    sBootloader_Cb.pgmInfo.crcResult = false;
                }
                else
                {
                    uint32_t localCrc = (*((uint32_t *)BOOTLOADER_CFG_APP_CRC_ADDR));
                    uint32_t app_size = (*((uint32_t *)(BOOTLOADER_CFG_APP_CRC_ADDR-4))) - BOOTLOADER_CFG_APP_START_ADDR;
                    
                    if (app_size < 0x28000 && app_size > 0x4000)
                    {
                        sBootloader_Cb.pgmInfo.pgmCrc = Bootloader_CalculateCheckSum(BOOTLOADER_CFG_INIT_CRC_VALUE,
                                                                                 (const uint8_t *)BOOTLOADER_CFG_APP_START_ADDR,
                                                                                 app_size,
                                                                                 true);
                    }
                    else
                    {
                        sBootloader_Cb.pgmInfo.pgmCrc = 0;
                    }
                    

                    if(localCrc != sBootloader_Cb.pgmInfo.pgmCrc)
                    {
                        /* Active mcu enter into boot */
                        sBootloader_Cb.bootActive        = true;
                        sBootloader_Cb.bootSubState      = BOOTLOADER_SUB_STATE_END;
                        sBootloader_Cb.pgmInfo.crcResult = false;
                    }
                    else
                    {
                        sBootloader_Cb.pgmInfo.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;
                        }
                    }
                }
            }
            else
            {
                /* Active mcu enter into boot */
                sBootloader_Cb.bootActive        = true;
                sBootloader_Cb.bootSubState      = BOOTLOADER_SUB_STATE_END;
                sBootloader_Cb.pgmInfo.crcResult = false;
            }
        }
        else if(sBootloader_Cb.downloadObj == BOOLOADER_OBJECT_CALIBRATION_DATA)
        {
        }
#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 0u
        else if(sBootloader_Cb.downloadObj == BOOLOADER_OBJECT_FLASH_DRIVER)
        {
            Bootloader_FlsDrvUpdate();
            if(sBootloader_Cb.flsDrvInfo.flashDriverDownloaded == true)
            {
                sBootloader_Cb.pgmInfo.crcResult = true;
                sBootloader_Cb.bootSubState      = BOOTLOADER_SUB_STATE_END;
            }
            else
            {
                sBootloader_Cb.pgmInfo.crcResult = false;
                sBootloader_Cb.bootSubState      = BOOTLOADER_SUB_STATE_END;
            }
        }
#else
        else if(sBootloader_Cb.downloadObj == BOOLOADER_OBJECT_FLASH_DRIVER)
		{
        	sBootloader_Cb.pgmInfo.crcResult = true;
        	sBootloader_Cb.bootSubState      = BOOTLOADER_SUB_STATE_END;
		}
#endif
    }
    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.stayInBootTimeoutCnt >= BOOTLOADER_CFG_BOOT_DURATION_MS) && (sBootloader_Cb.bootActive == false))
        {
            SEGGER_RTT_printf(0,"-----JumpToApp-----\n");
            /* Disable wdg */

            /* Disable global interrupt */
            IrqDrv_DisableGlobalInterrupt();

            /* Jump to App */
            Bootloader_JumpToApp();
        }
        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 */
    }
}

/*! \brief Initialize bootloader control block information
 *
 * This function Initialize bootloader control block information
 *
 * \param[in] obj : flashdriver instance
 * \param[in] udsObj : uds instance
 * \param[in] positiveRespFunction : uds positve response interface
 * \param[in] negativeRespFunction : uds negative response interface
 *
 * \return void
 */
void Bootloader_Init(FlashDrvType *obj, UdsType *udsObj, pUds_PositiveResponsePtr positiveRespFunction, pUds_NegativeResponsePtr negativeRespFunction)
{
    memset(&sBootloader_Cb, 0, sizeof(sBootloader_Cb));

#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.flashDrvObj          = obj;
    sBootloader_Cb.udsObj               = udsObj;
    sBootloader_Cb.positiveRespFunction = positiveRespFunction;
    sBootloader_Cb.negativeRespFunction = negativeRespFunction;
    sBootloader_Cb.pgmInfo.pgmCrc       = 0;

#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN != 0u
    Fls_Init(obj);
#else
    /* Update the flash instance */
    *((uint32_t *)BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR) = (uint32_t)obj;
#endif

    if(Bootloader_GetBootReq() == true)
    {
        /* Emerge boot request */
        sBootloader_Cb.stayInBootTimeoutCnt = 0;
        sBootloader_Cb.bootActive           = true;
        sBootloader_Cb.udsObj->session      = UDS_SESSION_PROGRAMMING;
    }
}

/*! \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.pgmInfo.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 == (BOOTLOADER_CFG_APP_SIZE + BOOTLOADER_CFG_APP_CRC_SIZE)))
    {
        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;
    }
    else if((startAddress == BOOTLOADER_CFG_CALIBRATION_DATA_START_ADDR) && (bytesNum == (BOOTLOADER_CFG_CALIBRATION_DATA_SIZE + BOOTLOADER_CFG_CALIBRATION_DATA_CRC_SIZE)))
    {
        sBootloader_Cb.downloadObj                = BOOLOADER_OBJECT_CALIBRATION_DATA;
        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) && (bytesNum <= (BOOTLOADER_CFG_FLS_DRV_RAM_CODE_SIZE + BOOTLOADER_CFG_FLS_DRV_RAM_CRC_SIZE)))
	{
		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;

    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;
    }

    return ret;
}

/*! \brief data check request
 *
 * This function indication that data check request
 *
 * \param[in] void
 *
 * \return void
 */
void Bootloader_EventDataCheckRequest(void)
{
    sBootloader_Cb.bootSubState      = BOOTLOADER_SUB_STATE_NONE;
    sBootloader_Cb.bootState         = BOOTLOADER_STATE_CHECK;
    sBootloader_Cb.pgmInfo.crcResult = false;
    sBootloader_Cb.pgmInfo.pgmCrc    = 0;
}

/*! \brief Bootloader main state process
 *
 * This function process the bootloader variant state
 *
 * \param[in] void
 *
 * \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;
        /*//SBC RESET
        if (sBootloader_Cb.resetReqMode == 1)
        {
            Bootloader_HardReset();
            
        }
        else
        {
            Bootloader_Reset();
        }*/
        Bootloader_Reset();
    }
}
