/*
 * 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.
 */

/*! \brief can bootloader 
 */

/*******************************************************************************
 * the includes
 ******************************************************************************/

#include <stdint.h>
#include <string.h>
#include "private_driver/mcu.h"
#include "private_driver/uds/stack/uds.h"
#include "bootloader.h"
#include "fls.h"
#include "private_driver/uds/user/uds_user.h"

/*******************************************************************************
 * the defines
 ******************************************************************************/
#define UDS_PHYS_RECV_MSG_ID (0x732)
#define UDS_FUNC_RECV_MSG_ID (0x7DF)
#define UDS_PHYS_RESP_MSG_ID (0x7B2)
#define UDS_TEXT_TX_MSG_ID   (0x234)

#define UDS_RECV_BUF         (2056)
#define UDS_SEND_BUF         (512)
#define CAN_BUFF_MAX_NUM     (32)

#define CAN_DATA_BUFFER_SIZE (64u)
#define CAN_BUFFER_FIFO_SIZE (32u)

int64_t       Get_Cur_Time_Stamp(void);
static void     Bootloader_SetGlobalIsr(bool isEnabled);
static int8_t   FlexCanBoot_TxMessage(uint32_t msgId, const uint8_t *pData, uint8_t size);
static uint32_t Bootloader_CalculateCheckSum(uint32_t initCrcValue, const uint8_t *dataBuffer, uint32_t bytesNum, bool inverted);
static bool     Bootloader_GetBootReq(void);
static void     Bootloader_JumpToApp(void);
static void     Bootloader_SetSessionMode(uint8_t sessionMode);
static void     Bootloader_SwReset(void);




static void BootloaderUds_PositiveResponse(const uint8_t *data, uint16_t len);
static void BootloaderUds_NegativeResponse(uint8_t sid, uint8_t rsp_nrc);

/*******************************************************************************
 * the typedefs
 ******************************************************************************/

typedef enum
{
    UDS_MSG_IDX_STD_RX_PHYS,
    UDS_MSG_IDX_STD_RX_FUNC,
    UDS_MSG_IDX_STD_TX,
    UDS_MSG_IDX_STD_TEST_TX,
    UDS_MSG_IDX_NUM
} Uds_MsgIdIdxType;

typedef struct
{
    uint32_t id;
    uint8_t  data[CAN_DATA_BUFFER_SIZE];
    uint8_t  len;
    uint16_t timeStamp;
    uint32_t hrTimeStamp;
} FlexCan_FrameStructureType;

typedef struct
{
    FlexCan_FrameStructureType rxMsg[CAN_BUFFER_FIFO_SIZE];
    FlexCan_FrameStructureType txMsg[CAN_BUFFER_FIFO_SIZE];
    uint8_t                    wrIdx;
    uint8_t                    rdIdx;
} FlexCan_DataInfoType;

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

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

#pragma location                                       = ".bootloaderInfo"
__root const Bootloade_CfgInfoType sBootloader_Version = {
    .bootVersionGeneration = 0,
    .bootVersionMajor      = 1,
    .bootVersionMinor      = 1,
    .bootVersionRevision   = 0};

McuType                      mcu;
volatile uint32_t            gSystick1msEvent = 0, gSystick1msCnt = 0, gTestIoEn = 0;
uint32_t                     gCpuClockFrequency       = 0;
int64_t                      timer_1ms                = 0;
uint8_t                      udsSendBuf[UDS_SEND_BUF] = {0};
uint8_t                      udsRecvBuf[UDS_RECV_BUF] = {0};
UdsType                      udsObj;
FlexCan_DataInfoType         flexCan_DataInfo;
FlexCanDrv_ControllerCfgType flexCanCfg;
FlexCanDrvType              *flexCanDrv_DemoObj;
uint8_t                      flexCanBoot_EnhanceRxFFCnt = 0;

const Bootloader_CallBackFuncListType Bootloader_CallBackFuncList = {
    .setGlobalIsrFunction      = Bootloader_SetGlobalIsr,
    .calculateCheckSumFunction = Bootloader_CalculateCheckSum,
    .getBootReqFunction        = Bootloader_GetBootReq,
    .jumpFunction              = Bootloader_JumpToApp,
    .setSessionModeFunction    = Bootloader_SetSessionMode,

    .udsPositiveRespFunction = BootloaderUds_PositiveResponse,
    .udsNegativeRespFunction = BootloaderUds_NegativeResponse,
    .swResetFunction         = Bootloader_SwReset,

#if BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u
    .flsInitFunction  = Fls_Init,
    .flsEraseFunction = Fls_Erase,
    .flsWriteFunction = Fls_Write,
#endif
};

Uds_ParamsType udsParam = {
    .isotpParams.framePadding = true,
    .isotpParams.blockSize    = 0,
    .isotpParams.recvPhysId   = UDS_PHYS_RECV_MSG_ID,
    .isotpParams.recvFuncId   = UDS_FUNC_RECV_MSG_ID,
    .isotpParams.sendid       = UDS_PHYS_RESP_MSG_ID,
    .isotpParams.sendBuf      = udsSendBuf,
    .isotpParams.sendBufSize  = UDS_SEND_BUF,
    .isotpParams.recvBuf      = udsRecvBuf,
    .isotpParams.recvBufSize  = UDS_RECV_BUF,
    .isotpParams.debug        = NULL,
    .isotpParams.sendCanMsg   = FlexCanBoot_TxMessage,
    .isotpParams.getTimeMs    = Get_Cur_Time_Stamp,
    .p2Server_ms              = 50,
    .p2xServer_10ms           = 500,
    .s3Server_ms              = 5000,
};

const FlexCanDrv_MsgCfgType msgCfgObj[UDS_MSG_IDX_NUM] = {
    {UDS_MSG_IDX_STD_RX_PHYS, 1, UDS_PHYS_RECV_MSG_ID, false, FLEXCANDRV_MSGTYPE_RX, DLC_BYTE_8, false, true,  0xFFFFFFFF}, /* CAN_MSGOBJ_STD_RX_PHYS */
    {UDS_MSG_IDX_STD_RX_FUNC, 1, UDS_FUNC_RECV_MSG_ID, false, FLEXCANDRV_MSGTYPE_RX, DLC_BYTE_8, false, true,  0xFFFFFFFF}, /* CAN_MSGOBJ_STD_RX_FUNC */
    {UDS_MSG_IDX_STD_TX,      1, UDS_PHYS_RESP_MSG_ID, false, FLEXCANDRV_MSGTYPE_TX, DLC_BYTE_8, false, false, 0xFFFFFFFF}, /* CAN_MSGOBJ_STD_TX */
    {UDS_MSG_IDX_STD_TEST_TX, 1, UDS_TEXT_TX_MSG_ID,   false, FLEXCANDRV_MSGTYPE_TX, DLC_BYTE_8, false, false, 0xFFFFFFFF}, /* CAN_MSGOBJ_STD_TX */
};

const uint32_t 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
 ******************************************************************************/

int64_t Get_Cur_Time_Stamp(void)
{
    return timer_1ms;
}

void SysTick_Handler(void)
{
    gSystick1msEvent++;
    timer_1ms++;
    Uds_Tick(&udsObj);
}

void CAN0_ERxFIFO_Handler(void)
{
    if(FlexCanDrv_GetEnhanceRxFFIsrFlag(flexCanDrv_DemoObj, FLEXCANDRV_ENHANCERXFF_ISR_SRC_DAIE) == true)
    {
        if(FlexCanDrv_GetEnhanceRxFFIsFull(flexCanDrv_DemoObj) == false)
        {
        }

        if(FlexCanDrv_GetEnhanceRxFFIsEmpty(flexCanDrv_DemoObj) == false)
        {
            FlexCanDrv_MsgObjType rxMsgObj;

            flexCanBoot_EnhanceRxFFCnt = FlexCanDrv_GetEnhanceRxFFMsgNums(flexCanDrv_DemoObj);

            if(flexCanBoot_EnhanceRxFFCnt > 0)
            {
                FlexCanDrv_GetEnhanceRxFifoMsg(flexCanDrv_DemoObj, &rxMsgObj);
                memcpy(flexCan_DataInfo.rxMsg[flexCan_DataInfo.wrIdx].data, rxMsgObj.data, rxMsgObj.dlc);
                flexCan_DataInfo.rxMsg[flexCan_DataInfo.wrIdx].id  = rxMsgObj.msgId;
                flexCan_DataInfo.rxMsg[flexCan_DataInfo.wrIdx].len = rxMsgObj.dlc;
                flexCan_DataInfo.wrIdx++;
                if(flexCan_DataInfo.wrIdx >= CAN_BUFFER_FIFO_SIZE)
                {
                    flexCan_DataInfo.wrIdx = 0;
                }
            }
        }
    }

    if(FlexCanDrv_GetEnhanceRxFFIsrFlag(flexCanDrv_DemoObj, FLEXCANDRV_ENHANCERXFF_ISR_SRC_WMMIE) == true)
    {
        FlexCanDrv_ClearEnhanceRxFFIsrFlag(flexCanDrv_DemoObj, FLEXCANDRV_ENHANCERXFF_ISR_SRC_WMMIE);
    }

    if(FlexCanDrv_GetEnhanceRxFFIsrFlag(flexCanDrv_DemoObj, FLEXCANDRV_ENHANCERXFF_ISR_SRC_OVFIE) == true)
    {
        FlexCanDrv_ClearEnhanceRxFFIsrFlag(flexCanDrv_DemoObj, FLEXCANDRV_ENHANCERXFF_ISR_SRC_OVFIE);
    }

    if(FlexCanDrv_GetEnhanceRxFFIsrFlag(flexCanDrv_DemoObj, FLEXCANDRV_ENHANCERXFF_ISR_SRC_UFWIE) == true)
    {
        FlexCanDrv_ClearEnhanceRxFFIsrFlag(flexCanDrv_DemoObj, FLEXCANDRV_ENHANCERXFF_ISR_SRC_UFWIE);
    }
}

bool FlexCanBoot_ReadoutMsg(FlexCan_FrameStructureType *pRxMsgObj)
{
    bool ret = false;

    if(flexCan_DataInfo.wrIdx != flexCan_DataInfo.rdIdx)
    {
        memcpy(pRxMsgObj, &flexCan_DataInfo.rxMsg[flexCan_DataInfo.rdIdx], sizeof(FlexCan_FrameStructureType));
        flexCan_DataInfo.rdIdx++;
        if(flexCan_DataInfo.rdIdx >= CAN_BUFFER_FIFO_SIZE)
        {
            flexCan_DataInfo.rdIdx = 0;
        }

        ret = true;
    }

    return ret;
}

static int8_t FlexCanBoot_TxMessage(uint32_t msgId, const uint8_t *pData, uint8_t size)
{
    FlexCanDrv_MsgObjType txMsgObj;
    uint8_t               msgIdx = 0, i = 0;

    for(i = 0; i < flexCanCfg.msgNum; i++)
    {
        if(msgId == flexCanCfg.msgCfg[i].msgId)
        {
            msgIdx = i;
            break;
        }
    }

    txMsgObj.msgBufId = flexCanCfg.msgCfg[msgIdx].msgBufId;
    txMsgObj.dlc      = size;
    txMsgObj.msgId    = flexCanCfg.msgCfg[msgIdx].msgId;
    memcpy(&txMsgObj.data[0], pData, size);

    FlexCanDrv_SetTxMsg(flexCanDrv_DemoObj, &txMsgObj);
    /* transmit standard CAN Tx message */
    FlexCanDrv_TransmitMsg(flexCanDrv_DemoObj, &txMsgObj);

    return 0;
}

void FlexCanBoot_Init(void)
{
    uint32_t busClockFreq = 0;

    flexCanDrv_DemoObj = &mcu.flexCanDrv0;

    /* set PTC1 MUX as GPIO */
    PinsDrv_SetMuxModeSel(&mcu.ptc, 1, PINSDRV_MUX_AS_GPIO);
    /* set PTC1 as GPIO output */
    PinsDrv_SetPinDirection(&mcu.ptc, 1, 1);
    /* set PTC1 as high to control CAN transceiver STB */
    PinsDrv_WritePin(&mcu.ptc, 1, 0);

    /* set PTC1 MUX as GPIO */
    PinsDrv_SetMuxModeSel(&mcu.ptd, 1, PINSDRV_MUX_AS_GPIO);
    /* set PTC1 as GPIO output */
    PinsDrv_SetPinDirection(&mcu.ptd, 1, 1);
    /* set PTC1 as high to control CAN transceiver STB */
    PinsDrv_WritePin(&mcu.ptd, 1, 0);

    /* set PTC1 MUX as GPIO */
    PinsDrv_SetMuxModeSel(&mcu.pte, 7, PINSDRV_MUX_AS_GPIO);
    /* set PTC1 as GPIO output */
    PinsDrv_SetPinDirection(&mcu.pte, 7, 1);
    /* set PTC1 as high to control CAN transceiver STB */
    PinsDrv_WritePin(&mcu.pte, 7, 0);

    /* set PTE4 as MUX 5 - CAN0.RX */
    PinsDrv_SetMuxModeSel(&mcu.pte, 4, PINSDRV_MUX_ALT5);

    /* set PTE5 as MUX 5 - CAN0.TX */
    PinsDrv_SetMuxModeSel(&mcu.pte, 5, PINSDRV_MUX_ALT5);

    flexCanCfg.clkSrc               = FLEXCANDRV_CLKSRC_CHICLK;
    flexCanCfg.fdEnable             = false;
    flexCanCfg.fdISOEnable          = false;
    flexCanCfg.enhancefifoEnable    = true;
    flexCanCfg.msgBufDataLenSel     = FLEXCANDRV_MB_SIZE_BYTE_8;
    flexCanCfg.individualMaskEnable = true;

    if(flexCanCfg.clkSrc == FLEXCANDRV_CLKSRC_CHICLK)
    {
        ClockDrv_GetFreq(&mcu.clockDrv, CLOCKDRV_APB, &busClockFreq);
    }
    else
    {
        ClockDrv_GetFreq(&mcu.clockDrv, CLOCKDRV_SOSC_DIV2, &busClockFreq);
    }

    if(flexCanCfg.fdEnable == true)
    {
        FlexCanDrv_BitTimingCalc(&flexCanCfg.fdBitTiming,
                                 busClockFreq, /* module clock source: 16M */
                                 2000000,      /* baudrate: 2M */
                                 7500,         /* sample point: 75% */
                                 2000,         /* SJW: 20% */
                                 1);           /* FD bit timing */
    }
    
    FlexCanDrv_BitTimingCalc(&flexCanCfg.bitTiming,
                             busClockFreq, /* module clock source: 16M */
                             500000,       /* baudrate: 500K */
                             7500,         /* sample point: 75% */
                             2500,         /* SJW: 20% */
                             0);           /* classic CAN bit timing */

    /* initialize CAN module */
    FlexCanDrv_Configure(flexCanDrv_DemoObj, &flexCanCfg);

    /* enable enhance rx fifo interrupt */
    FlexCanDrv_SetEnhanceRxFFIsr(flexCanDrv_DemoObj, FLEXCANDRV_ENHANCERXFF_ISR_SRC_DAIE, true);
    IrqDrv_EnableIrq(CAN0_ERxFIFO_IRQn);
}

/*! \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 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
            {
                /* Clear the no-init ram */
                sBootloader_Req = 0;
            }
        }
        else
        {
            ret = false;
        }
    }
    else
    {
        ret = false;
    }

    return ret;
}

/*! \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();

    /* 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_MSP(statckPointer);
    __set_PSP(statckPointer);

    SCB->VTOR = (uint32_t)BOOTLOADER_CFG_APP_START_ADDR;
    /* Jump action */
    pJumpToApp();
}

static void Bootloader_SetSessionMode(uint8_t sessionMode)
{
    udsObj.session = sessionMode;
}

static void Bootloader_SwReset(void)
{
    ResetDrv_SoftwareResetModule(&mcu.resetDrv, RESETDRV_SWRESET_SYS);
}

static void BootloaderUds_PositiveResponse(const uint8_t *data, uint16_t len)
{
    Uds_PositiveResponse(&udsObj, data, len);
}

static void BootloaderUds_NegativeResponse(uint8_t sid, uint8_t rsp_nrc)
{
    Uds_NegativeResponse(&udsObj, sid, rsp_nrc);
}

static void Bootloader_SetGlobalIsr(bool isEnabled)
{
    if (isEnabled == true)
    {
        IrqDrv_EnableGlobalInterrupt();
    }
    else
    {
        IrqDrv_DisableGlobalInterrupt();
    }
}

uint8_t testdata[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
int main(void)
{
    FlexCan_FrameStructureType rxMsg;
    /* Setup the clock */
    ClockDrv_ModuleClkConfigType clockConfig;

    IrqDrv_DisableGlobalInterrupt();

    /* Initialize all MCU drivers: flash drv included */
    Mcu_Init(&mcu);

    WdgDrv_Disable(&mcu.wdgDrv);

    /* CAN init */
    memset(&flexCan_DataInfo, 0, sizeof(flexCan_DataInfo));
    memset(&flexCanCfg, 0, sizeof(flexCanCfg));

    /* Setup the Pll div2 clock */
    clockConfig.gating = true;
    clockConfig.source = CLOCKDRV_PLL;
    clockConfig.div    = 1;
    ClockDrv_ConfigureClock(&mcu.clockDrv, CLOCKDRV_PLL_DIV2, &clockConfig);

    /* Enable the clock for all port peripheral */
    clockConfig.gating = true;
    clockConfig.div    = 1;
    ClockDrv_ConfigureClock(&mcu.clockDrv, CLOCKDRV_PORTA, &clockConfig);
    ClockDrv_ConfigureClock(&mcu.clockDrv, CLOCKDRV_PORTB, &clockConfig);
    ClockDrv_ConfigureClock(&mcu.clockDrv, CLOCKDRV_PORTC, &clockConfig);
    ClockDrv_ConfigureClock(&mcu.clockDrv, CLOCKDRV_PORTD, &clockConfig);
    ClockDrv_ConfigureClock(&mcu.clockDrv, CLOCKDRV_PORTE, &clockConfig);

    /* get CAN controller default configuration */
    FlexCanDrv_GetDefaultCfg(&flexCanCfg);
    flexCanCfg.msgNum = sizeof(msgCfgObj) / sizeof(FlexCanDrv_MsgCfgType);
    flexCanCfg.msgCfg = msgCfgObj;
    FlexCanBoot_Init();
    PinsDrv_SetMuxModeSel(&mcu.ptd, 1, PINSDRV_MUX_AS_GPIO);
    PinsDrv_SetPinDirection(&mcu.ptd, 1, 1);
    PinsDrv_SetPullSel(&mcu.ptd, 1, PINSDRV_INTERNAL_PULL_UP);
    PinsDrv_WritePin(&mcu.ptd, 1, 0);

    /* UDS init */
    Uds_UserInit(&udsObj, &udsParam);

    Bootloader_Init((uint32_t)(&mcu.flashDrv), &Bootloader_CallBackFuncList);

    /* Set system tick clock, 1ms event */
    ClockDrv_GetFreq(&mcu.clockDrv, CLOCKDRV_SYS, &gCpuClockFrequency);
    SysTick_Config(gCpuClockFrequency / 1000u);

    IrqDrv_EnableIrq(SysTick_IRQn);

    IrqDrv_EnableGlobalInterrupt();

    while(1)
    {
        if(gSystick1msEvent > 0u)
        {
            gSystick1msEvent = 0;
            gSystick1msCnt++;

            if(gSystick1msCnt >= 500)
            {
                gSystick1msCnt = 0;
                /* Test Io */
                if(gTestIoEn == 0)
                {
                    gTestIoEn = 1;
                    PinsDrv_WritePin(&mcu.ptd, 1, 1);
                }
                else
                {
                    gTestIoEn = 0;
                    PinsDrv_WritePin(&mcu.ptd, 1, 0);
                }
                //FlexCanBoot_TxMessage(UDS_TEXT_TX_MSG_ID,testdata,8);
            }

            /* Handler user routine */
            if(FlexCanBoot_ReadoutMsg(&rxMsg) == true)
            {
                if((rxMsg.id == UDS_PHYS_RECV_MSG_ID) || (rxMsg.id == UDS_FUNC_RECV_MSG_ID))
                {
                    IsoTp_HandleIncomingCanMsg(&udsObj.isotp, rxMsg.id, rxMsg.data, rxMsg.len);
                }
            }

            Bootloader_TimingProcess(1);
            Uds_Run(&udsObj);
            Bootloader_StateProc();
        }
    }
}
