/*
 * Copyright (c) 2015, Freescale Semiconductor, Inc.
 * Copyright 2016-2018 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_debug_console.h"
#include "fsl_flexcan.h"
#include "board.h"

#include "fsl_device_registers.h"
#include "fsl_common.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "TimersManager.h"
#include "FunctionLib.h"
#include "SecLib.h"
#include "RNG_Interface.h"

#if OTA_BY_CAN_LIN
#include "Eeprom.h"
#include "OtaSupport.h"
#endif
#include "flexcan_cfg.h"
#include "gen_fsk_tests.h"
#include "can_lin_common.h"
   
/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define EXAMPLE_CAN CAN0
#define EXAMPLE_CAN_CLKSRC kCLOCK_BusClk
#define EXAMPLE_CAN_CLK_FREQ CLOCK_GetFreq(kCLOCK_BusClk)
#define USE_CANFD (1)
/* 
 *    DWORD_IN_MB    DLC    BYTES_IN_MB             Maximum MBs
 *    2              8      kFLEXCAN_8BperMB        32
 *    4              10     kFLEXCAN_16BperMB       21
 *    8              13     kFLEXCAN_32BperMB       12
 *    16             15     kFLEXCAN_64BperMB       7
 *
 * Dword in each message buffer, Length of data in bytes, Payload size must align,
 * and the Message Buffers are limited corresponding to each payload configuration:
 */
#define DWORD_IN_MB (4)
#define DLC (10)
#define BYTES_IN_MB kFLEXCAN_16BperMB
#define RX_MESSAGE_BUFFER_NUM (9)
#define TX_MESSAGE_BUFFER_NUM (8)

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/
flexcan_handle_t flexcanHandle;
volatile bool txComplete = false;
volatile bool rxComplete = false;
flexcan_mb_transfer_t txXfer, rxXfer;
#if (defined(USE_CANFD) && USE_CANFD)
flexcan_fd_frame_t canFrame;
#else
flexcan_frame_t canFrame;
#endif
static uint32_t txIdentifier = CAN_TX_IDENTIFIER;
static uint32_t rxIdentifier = CAN_RX_IDENTIFIER;

can_ota_status_t g_can_ota_status = CAN_OTA_STATUS_IDLE;
can_ota_stage_t g_can_ota_stage = CAN_OTA_STAGE_IDLE;

static tmrTimerID_t mResetTimerId = gTmrInvalidTimerID_c;
static tmrTimerID_t mOtaTimeoutTimerId = gTmrInvalidTimerID_c;
static tmrTimerID_t mRspDeviceIdTimerId = gTmrInvalidTimerID_c;
#if OTA_BY_CAN_LIN
static uint8_t can_ota_block_cnt = 0;
static uint32_t can_ota_image_offset = 0;
static uint16_t can_ota_rx_frame_offset = 0;
static uint8_t eeprom_write_buffer[CAN_OTA_BLOCK_SIZE] = {0};
#endif
static uint8_t can_ota_rx_sequence = 0;
static uint8_t can_ota_frame_ack = CAN_OTA_TX_ACK_SUCCESS;
static uint8_t can_ota_timeout_cnt = 0;
uint16_t g_can_device_id;

#if OTA_BY_CAN_LIN
uint32_t g_can_ota_image_length = 0;
uint32_t g_can_write_flash_offset = gBootData_ImageLength_Offset_c;

uint8_t key128[128/8] = { 0x55, 0x6c, 0x74, 0x72,  
                          0x61, 0x53, 0x65, 0x63, 
	                      0x61, 0x73, 0x73, 0x77, 
                          0x6f, 0x72, 0x64, 0x31 };     /*AES128 key*/
#endif
extern bool_t mNewImageReady;
extern void ResetMCU(void);

static void ResetTimerCallback(void *pParam);
static void OtaTimeoutTimerCallback(void *pParam);
static void RspDeviceIdTimerCallback(void *pParam);

/*******************************************************************************
 * Code
 ******************************************************************************/
/*!
 * @brief FlexCAN Call Back function
 */
static void flexcan_callback(CAN_Type *base, flexcan_handle_t *handle, status_t status, uint32_t result, void *userData)
{
    switch (status)
    {
        case kStatus_FLEXCAN_RxIdle:
            if (RX_MESSAGE_BUFFER_NUM == result)
            {
                rxComplete = true;
                //PRINTF("rx finish\r\n");
                can_rx_process();
                if (CAN_OTA_STATUS_IDLE == g_can_ota_status)
                    can_set_to_rx_mode();
                else
                    can_tx_process();
            }
            break;

        case kStatus_FLEXCAN_TxIdle:
            if (TX_MESSAGE_BUFFER_NUM == result)
            {
                txComplete = true;
                //PRINTF("tx finish\r\n");
                can_set_to_rx_mode();
            }
            break;

        default:
            break;
    }
}

/*!
 * @brief CAN Initialization function
 */
void can_demo_init(void)
{
    flexcan_config_t flexcanConfig;
    flexcan_rx_mb_config_t mbConfig;
   
    /* Initialize board hardware. */
    BOARD_InitCAN();

    /* Get FlexCAN module default Configuration. */
    /*
     * flexcanConfig.clkSrc = kFLEXCAN_ClkSrcOsc;
     * flexcanConfig.baudRate = 1000000U;
     * flexcanConfig.maxMbNum = 16;
     * flexcanConfig.enableLoopBack = false;
     * flexcanConfig.enableSelfWakeup = false;
     * flexcanConfig.enableIndividMask = false;
     * flexcanConfig.enableDoze = false;
     * flexcanConfig.timingConfig = timingConfig;
     */
    FLEXCAN_GetDefaultConfig(&flexcanConfig);

    /* Init FlexCAN module. */
#if (!defined(FSL_FEATURE_FLEXCAN_SUPPORT_ENGINE_CLK_SEL_REMOVE)) || !FSL_FEATURE_FLEXCAN_SUPPORT_ENGINE_CLK_SEL_REMOVE
    flexcanConfig.clkSrc = kFLEXCAN_ClkSrcPeri;
#endif /* FSL_FEATURE_FLEXCAN_SUPPORT_ENGINE_CLK_SEL_REMOVE */
    /* If special quantum setting is needed, set the timing parameters. */
#if (defined(SET_CAN_QUANTUM) && SET_CAN_QUANTUM)
    flexcanConfig.timingConfig.phaseSeg1 = PSEG1;
    flexcanConfig.timingConfig.phaseSeg2 = PSEG2;
    flexcanConfig.timingConfig.propSeg = PROPSEG;
#if (defined(FSL_FEATURE_FLEXCAN_HAS_FLEXIBLE_DATA_RATE) && FSL_FEATURE_FLEXCAN_HAS_FLEXIBLE_DATA_RATE)
    flexcanConfig.timingConfig.fphaseSeg1 = FPSEG1;
    flexcanConfig.timingConfig.fphaseSeg2 = FPSEG2;
    flexcanConfig.timingConfig.fpropSeg = FPROPSEG;
#endif
#endif

#if (defined(USE_CANFD) && USE_CANFD)
    FLEXCAN_FDInit(EXAMPLE_CAN, &flexcanConfig, EXAMPLE_CAN_CLK_FREQ, BYTES_IN_MB, false);
#else
    FLEXCAN_Init(EXAMPLE_CAN, &flexcanConfig, EXAMPLE_CAN_CLK_FREQ);
#endif

    /* Create FlexCAN handle structure and set call back function. */
    FLEXCAN_TransferCreateHandle(EXAMPLE_CAN, &flexcanHandle, flexcan_callback, NULL);

    /* Set Rx Masking mechanism. */
    FLEXCAN_SetRxMbGlobalMask(EXAMPLE_CAN, FLEXCAN_RX_MB_STD_MASK(rxIdentifier, 0, 0));

    /* Setup Rx Message Buffer. */
    mbConfig.format = kFLEXCAN_FrameFormatStandard;
    mbConfig.type = kFLEXCAN_FrameTypeData;
    mbConfig.id = FLEXCAN_ID_STD(rxIdentifier);
#if (defined(USE_CANFD) && USE_CANFD)
    FLEXCAN_SetFDRxMbConfig(EXAMPLE_CAN, RX_MESSAGE_BUFFER_NUM, &mbConfig, true);
#else
    FLEXCAN_SetRxMbConfig(EXAMPLE_CAN, RX_MESSAGE_BUFFER_NUM, &mbConfig, true);
#endif

    /* Setup Tx Message Buffer. */
#if (defined(USE_CANFD) && USE_CANFD)
    FLEXCAN_SetFDTxMbConfig(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, true);
#else
    FLEXCAN_SetTxMbConfig(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, true);
#endif

    can_set_to_rx_mode();
    mResetTimerId = TMR_AllocateTimer();
    mOtaTimeoutTimerId = TMR_AllocateTimer();
    mRspDeviceIdTimerId = TMR_AllocateTimer();
}

/*!
 * @brief Set CAN to rx mode
 */
void can_set_to_rx_mode()
{
    rxComplete = false;

    /* Start receive data through Rx Message Buffer. */
    rxXfer.mbIdx = RX_MESSAGE_BUFFER_NUM;
#if (defined(USE_CANFD) && USE_CANFD)
    rxXfer.framefd = &canFrame;
    FLEXCAN_TransferFDReceiveNonBlocking(EXAMPLE_CAN, &flexcanHandle, &rxXfer);
#else
    rxXfer.frame = &canFrame;
    FLEXCAN_TransferReceiveNonBlocking(EXAMPLE_CAN, &flexcanHandle, &rxXfer);
#endif
}

/*!
 * @brief To process the received data of CAN
 */
void can_rx_process(void)
{
    uint8_t rx_data[15] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
#if OTA_BY_CAN_LIN    
    static uint8_t can_ota_total_block_numbers = 0;
    static uint8_t can_ota_previous_block_cnt = 0xFF;
#endif

    uint8_t rx_length = canFrame.length;
    if ((rx_length > 15) || (rx_length == 0))
    {
        PRINTF("invalid receive length\r\n");
        return;
    }
    //PRINTF("rx length: %d\r\n", rx_length); 

    rx_data[0] = canFrame.dataByte0;    
    if (rx_length > 1)
        rx_data[1] = canFrame.dataByte1;
    if (rx_length > 2)
        rx_data[2] = canFrame.dataByte2;
    if (rx_length > 3)
        rx_data[3] = canFrame.dataByte3;
    if (rx_length > 4)
        rx_data[4] = canFrame.dataByte4;    
    if (rx_length > 5)
        rx_data[5] = canFrame.dataByte5;
    if (rx_length > 6)
        rx_data[6] = canFrame.dataByte6;
    if (rx_length > 7)
        rx_data[7] = canFrame.dataByte7;
    if (rx_length > 8)
        rx_data[8] = canFrame.dataByte8;
    if (rx_length > 9)
        rx_data[9] = canFrame.dataByte9;
    if (rx_length > 10)
        rx_data[10] = canFrame.dataByte10;
    if (rx_length > 11)
        rx_data[11] = canFrame.dataByte11;
    if (rx_length > 12)
        rx_data[12] = canFrame.dataByte12;    
    if (rx_length > 13)
        rx_data[13] = canFrame.dataByte13;
    if (rx_length > 14)
        rx_data[14] = canFrame.dataByte14;
       
    /*for (uint8_t i = 0; i < rx_length; i ++)
        PRINTF("%02X ", rx_data[i]);
    PRINTF("\r\n"); */  

    switch (rxIdentifier)
    {
        case CAN_RX_IDENTIFIER:
        {
            if (CAN_GEN_CMD_GET_DEV_ID == rx_data[0])
            {
                if (CAN_OTA_STATUS_IDLE == g_can_ota_status)
                {
                    uint32_t randomDelayMs;
                    RNG_GetRandomNo(&randomDelayMs);
                    randomDelayMs = (uint8_t)randomDelayMs * 4;
                    PRINTF("Random delay %d ms (range:0~1020)\r\n", randomDelayMs);
                    TMR_StartSingleShotTimer(mRspDeviceIdTimerId, randomDelayMs, RspDeviceIdTimerCallback, NULL);
                }
            }
            else if (CAN_GEN_CMD_TX_LM_PARA == rx_data[0])
            {
                mLMTrackConnStatus.id = rx_data[1];
                mLMTrackConnStatus.status = rx_data[2];
                
                gLMPara.CI = (uint16_t)rx_data[3] << 8;
                gLMPara.CI += rx_data[4];
                gLMPara.hopIncrement = rx_data[5];                
                gLMPara.crcSeed  = rx_data[6] << 24;
                gLMPara.crcSeed |= rx_data[7] << 16;
                gLMPara.crcSeed |= rx_data[8] << 8;
                gLMPara.crcSeed |= rx_data[9] << 0;
                gLMPara.syncAddress  = rx_data[10] << 24;
                gLMPara.syncAddress |= rx_data[11] << 16;
                gLMPara.syncAddress |= rx_data[12] << 8;
                gLMPara.syncAddress |= rx_data[13] << 0;

                GfskApp_StartLM();
            }
#if OTA_BY_CAN_LIN
            else if (CAN_GEN_CMD_OTA_CMD == rx_data[0])
            {
                if (CAN_OTA_CMD_START == rx_data[1])
                {                  
                    if (g_can_device_id == (rx_data[2] + (rx_data[3] << 8)))
                    {
                        if (CAN_OTA_STATUS_IDLE == g_can_ota_status)
                        {
                            g_can_ota_status = CAN_OTA_STATUS_READY;
                        }
                        else
                        {
                            g_can_ota_status = CAN_OTA_STATUS_IDLE;
                        }
                    }
                    else
                    {
                        return;
                    }
                    
                    if(OTA_InitExternalMemory() != gOtaSucess_c)
                    {
                        PRINTF("eeprom init error\r\n");
                    }
                    else
                    {
                        PRINTF("eeprom init success\r\n");
                    }
#if (gEepromType_d == gEepromDevice_InternalFlash_c)
                    uint8_t start_marker[gBootData_Marker_Size_c] = {gBootData_StartMarker_Value_c};                   
                    /* Write the Start marker at the beginning of the internal storage. */
                    EEPROM_WriteData(gBootData_Marker_Size_c, gBootData_StartMarker_Offset_c, start_marker);
                    PRINTF("eeprom start marker written\r\n");
#endif
                    can_ota_image_offset = g_can_write_flash_offset;
                    can_ota_rx_frame_offset = 0;
                    TMR_StartLowPowerTimer(mOtaTimeoutTimerId, gTmrLowPowerIntervalMillisTimer_c, 200, OtaTimeoutTimerCallback, NULL);
                }
                else if (CAN_OTA_CMD_END == rx_data[1])
                {
                    if (g_can_device_id != (rx_data[2] + (rx_data[3] << 8)))
                    {
                        g_can_ota_status = CAN_OTA_STATUS_IDLE;
                        return;
                    }
                    
                    if (CAN_OTA_STATUS_FINISH == g_can_ota_status)
                    {                    
                        if (can_ota_block_cnt == (can_ota_total_block_numbers - 1))
                        {
                            PRINTF("write the last block\r\n");
                            g_can_write_flash_offset = gBootData_ImageLength_Offset_c + can_ota_block_cnt * CAN_OTA_BLOCK_SIZE;
                            if (can_ota_rx_frame_offset == CAN_OTA_BLOCK_SIZE)
                            {
                                //PRINTF("length: %d\r\n", CAN_OTA_BLOCK_SIZE);
                                EEPROM_WriteData(CAN_OTA_BLOCK_SIZE, g_can_write_flash_offset, eeprom_write_buffer);
                            }
                            else if (can_ota_rx_frame_offset < CAN_OTA_BLOCK_SIZE)
                            {
                                //PRINTF("length: %d\r\n", can_ota_rx_frame_offset);
                                EEPROM_WriteData(can_ota_rx_frame_offset, g_can_write_flash_offset, eeprom_write_buffer);
                                FLib_MemSet(&(eeprom_write_buffer[can_ota_rx_frame_offset]), 0xFF, CAN_OTA_BLOCK_SIZE - can_ota_rx_frame_offset);
                                /*EEPROM_ReadData(can_ota_rx_frame_offset, g_can_write_flash_offset, read_buf);
                                for (i = 0; i < can_ota_rx_frame_offset; i++)
                                    PRINTF("%02X ", read_buf[i]);
                                PRINTF("\r\n");*/
                            }
                            else
                            {
                                PRINTF("invalid offset!!\r\n");
                                can_ota_rx_frame_offset = 0;
                                g_can_ota_status = CAN_OTA_STATUS_ABORT;
                            }
                            g_can_ota_stage = CAN_OTA_STAGE_END;
                        }
                        else
                        {
                            g_can_ota_status = CAN_OTA_STATUS_ABORT;
                            g_can_ota_stage = CAN_OTA_STAGE_END;                            
                        }
                    }
                    else
                    {
                        g_can_ota_status = CAN_OTA_STATUS_ABORT;
                        g_can_ota_stage = CAN_OTA_STAGE_END;
                    }

                    /*if (CAN_OTA_STATUS_FINISH == g_can_ota_status)
                    {
                        PRINTF("\r\nready to reset and switch\r\n");
                        TMR_StartSingleShotTimer(mResetTimerId, 20, ResetTimerCallback, NULL);
                    }*/
                }
                can_ota_timeout_cnt = 0;
            }
            else if (CAN_GEN_CMD_OTA_DATA == rx_data[0])
            {
                if (CAN_OTA_STATUS_READY == g_can_ota_status)
                {
                    g_can_ota_status = CAN_OTA_STATUS_RUNNING;
                    g_can_ota_stage = CAN_OTA_STAGE_TX_DATA;
                    g_can_write_flash_offset = gBootData_ImageLength_Offset_c;
                }
                
                if (CAN_OTA_STATUS_RUNNING == g_can_ota_status)
                {
                    can_ota_rx_sequence = rx_data[1];
                    if (can_ota_image_offset == gBootData_ImageLength_Offset_c)
                    {
                        g_can_ota_image_length = rx_data[2] + (rx_data[3] << 8) 
                                               + (rx_data[4] << 16) + gBootData_Image_Offset_c;
                        PRINTF("length: %d\r\n", g_can_ota_image_length);
                        if (g_can_ota_image_length % CAN_OTA_BLOCK_SIZE)
                        {
                            can_ota_total_block_numbers = (g_can_ota_image_length / CAN_OTA_BLOCK_SIZE) + 1;
                        }
                        else
                        {
                            can_ota_total_block_numbers = g_can_ota_image_length / CAN_OTA_BLOCK_SIZE;
                        }
                        PRINTF("blocks: %d\r\n", can_ota_total_block_numbers);                    
                    }

                    //PRINTF("i:%d ", can_ota_image_offset);
                    //PRINTF("f:%d ", can_ota_rx_frame_offset);
                    if ((can_ota_image_offset + 8) <= g_can_ota_image_length)
                    {
                        if ((can_ota_rx_frame_offset + 8) <= CAN_OTA_BLOCK_SIZE)
                        {
                            FLib_MemCpy(&(eeprom_write_buffer[can_ota_rx_frame_offset]), &(rx_data[2]), 8);
                            can_ota_rx_frame_offset += 8;
                            can_ota_image_offset += 8;
                            if (can_ota_image_offset == g_can_ota_image_length)
                            {
                                g_can_ota_status = CAN_OTA_STATUS_FINISH;
                            }
                        }
                        else
                        {
                            PRINTF("invalid frame offest 1!!\r\n");
                            g_can_ota_status = CAN_OTA_STATUS_ABORT;
                        }
                    }
                    else if (can_ota_image_offset < g_can_ota_image_length)
                    {
                        if ((can_ota_rx_frame_offset + 8) <= CAN_OTA_BLOCK_SIZE)
                        {
                            uint8_t len = g_can_ota_image_length - can_ota_image_offset;
                            uint8_t last_frame[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
                            FLib_MemCpy(last_frame, &(rx_data[2]), len);
                            FLib_MemCpy(&(eeprom_write_buffer[can_ota_rx_frame_offset]), last_frame, 8);
                            can_ota_rx_frame_offset += 8;
                            can_ota_image_offset = g_can_ota_image_length;
                            g_can_ota_status = CAN_OTA_STATUS_FINISH;
                        }
                        else
                        {
                            PRINTF("invalid frame offest 2!!\r\n");
                            g_can_ota_status = CAN_OTA_STATUS_ABORT;                        
                        }
                    }
                    else
                    {
                        PRINTF("invalid image offest!!\r\n");
                        g_can_ota_status = CAN_OTA_STATUS_ABORT;
                    }
                }

                if (can_ota_rx_frame_offset == CAN_OTA_BLOCK_SIZE)
                {
                    if (((can_ota_previous_block_cnt == 0xFF) && (can_ota_block_cnt == 0))
                        || (can_ota_previous_block_cnt < can_ota_block_cnt))
                    {
                        if (can_ota_block_cnt < (can_ota_total_block_numbers - 1))
                        {
                            g_can_write_flash_offset = gBootData_ImageLength_Offset_c + can_ota_block_cnt * CAN_OTA_BLOCK_SIZE;
                            //PRINTF("write offset: %d\r\n", g_lin_write_flash_offset);
                            ee_err_t ec = EEPROM_WriteData(CAN_OTA_BLOCK_SIZE, g_can_write_flash_offset, eeprom_write_buffer);
                            /*PRINTF("write result: %d\r\n", ec);
                            EEPROM_ReadData(LIN_OTA_BLOCK_SIZE, g_lin_write_flash_offset, read_buf);
                            for (i = 0; i < LIN_OTA_BLOCK_SIZE; i ++)
                                PRINTF("%02X ", read_buf[i]);*/
                            PRINTF("block %d written finished\r\n", can_ota_block_cnt);
                            can_ota_previous_block_cnt = can_ota_block_cnt;
                            can_ota_block_cnt ++;
                        }
                        else
                        {
                            g_can_ota_status = CAN_OTA_STATUS_ABORT;
                        }
                    }
                    can_ota_rx_frame_offset = 0;
                }
                can_ota_timeout_cnt = 0;
            }
#endif
        }
            break;
        default:
            break;
    } 

    if (CAN_OTA_STATUS_ABORT == g_can_ota_status)
    {
        TMR_StopTimer(mOtaTimeoutTimerId);
        can_ota_timeout_cnt = 0xF0;
        TMR_StartSingleShotTimer(mOtaTimeoutTimerId, 500, OtaTimeoutTimerCallback, NULL);
    }
}

/*!
 * @brief Send one frame data of CAN to the bus, maximum length is limited to 15 
 */
void can_tx_data(uint8_t data[], uint8_t length)
{
    if ((length > 15) || (length == 0))
    {
        PRINTF("invalid input length!\r\n");
        return;
    }
    //PRINTF("tx length = %d\r\n", length);

    txComplete = false;
    
    canFrame.dataByte0 = data[0];    
    if (length > 1)
        canFrame.dataByte1 = data[1];   
    if (length > 2)
        canFrame.dataByte2 = data[2];
    if (length > 3)
        canFrame.dataByte3 = data[3];
    if (length > 4)
        canFrame.dataByte4 = data[4];
    if (length > 5)
        canFrame.dataByte5 = data[5];
    if (length > 6)
        canFrame.dataByte6 = data[6];
    if (length > 7)
        canFrame.dataByte7 = data[7];
    if (length > 8)
        canFrame.dataByte8 = data[8];
    if (length > 9)
        canFrame.dataByte9 = data[9];   
    if (length > 10)
        canFrame.dataByte10 = data[10];
    if (length > 11)
        canFrame.dataByte11 = data[11];
    if (length > 12)
        canFrame.dataByte12 = data[12];
    if (length > 13)
        canFrame.dataByte13 = data[13];
    if (length > 14)
        canFrame.dataByte14 = data[14];
    
    /*for (uint8_t i = 0; i < length; i ++)
        PRINTF("%02X ", data[i]);
    PRINTF("\r\n");*/
    
    canFrame.id = FLEXCAN_ID_STD(txIdentifier);
    canFrame.format = kFLEXCAN_FrameFormatStandard;
    canFrame.type = kFLEXCAN_FrameTypeData;
    canFrame.length = length;
    txXfer.mbIdx = TX_MESSAGE_BUFFER_NUM;
    
#if (defined(USE_CANFD) && USE_CANFD)
    txXfer.framefd = &canFrame;
    FLEXCAN_TransferFDSendNonBlocking(EXAMPLE_CAN, &flexcanHandle, &txXfer);
#else
    txXfer.frame = &canFrame;
    FLEXCAN_TransferSendNonBlocking(EXAMPLE_CAN, &flexcanHandle, &txXfer);
#endif    

}

/*!
 * @brief To prepare a frame data that will be sent to the bus
 */
void can_tx_process(void)
{   
    uint8_t tx_data[15] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
   
    switch (txIdentifier)
    {            
        case CAN_TX_IDENTIFIER:
        {
            if (CAN_OTA_STAGE_IDLE == g_can_ota_stage)
            {
                tx_data[0] = CAN_GEN_CMD_OTA_STATUS;
                tx_data[1] = g_can_ota_status;
                tx_data[2] = (uint8_t)g_can_device_id;
                tx_data[3] = (uint8_t)(g_can_device_id >> 8);
                can_tx_data(tx_data, 4);
            }
            else if (CAN_OTA_STAGE_TX_DATA == g_can_ota_stage)
            {
                tx_data[0] = CAN_GEN_CMD_OTA_DATA;
                tx_data[1] = can_ota_frame_ack;
                tx_data[2] = can_ota_rx_sequence;
                can_tx_data(tx_data, 3);
            }
            else if (CAN_OTA_STAGE_END == g_can_ota_stage)
            {
                tx_data[0] = CAN_GEN_CMD_OTA_STATUS;
                tx_data[1] = g_can_ota_status;
                tx_data[2] = (uint8_t)g_can_device_id;
                tx_data[3] = (uint8_t)(g_can_device_id >> 8);
                can_tx_data(tx_data, 4);
                g_can_ota_stage = CAN_OTA_STAGE_IDLE;
                if (CAN_OTA_STATUS_FINISH == g_can_ota_status)
                {
                    g_can_ota_status = CAN_OTA_STATUS_IDLE;
                    PRINTF("\r\nready to reset and switch\r\n");
                    TMR_StartSingleShotTimer(mResetTimerId, 5, ResetTimerCallback, NULL);                    
                }
            }
        }
            break;
        default:
            break;   
    }
}

/*!
 * @brief Callback of OTA end and reset to switch 
 */
static void ResetTimerCallback(void *pParam)
{
#if OTA_BY_CAN_LIN
    mNewImageReady = TRUE;
    OTA_SetNewImageFlag();
#endif
    ResetMCU();     
}

/*!
 * @brief Callback of OTA Timeout 
 */
static void OtaTimeoutTimerCallback(void *pParam)
{
    can_ota_timeout_cnt ++;
    if (can_ota_timeout_cnt >= 5)
    {
#if OTA_BY_CAN_LIN
        PRINTF("can ota timeout, reset related variables\r\n");
        can_ota_timeout_cnt = 0;
        g_can_ota_status = CAN_OTA_STATUS_IDLE;
        g_can_ota_stage = CAN_OTA_STAGE_IDLE;
        can_ota_block_cnt = 0;
        can_ota_image_offset = 0;
        can_ota_rx_frame_offset = 0;        
        g_can_ota_image_length = 0;
        g_can_write_flash_offset = gBootData_ImageLength_Offset_c;
        FLib_MemSet(eeprom_write_buffer, 0x00, CAN_OTA_BLOCK_SIZE);
        can_set_to_rx_mode();
#endif
        TMR_StopTimer(mOtaTimeoutTimerId);
    }
}

/*!
 * @brief Callback of Response Device Id 
 */
static void RspDeviceIdTimerCallback(void *pParam)
{
    uint8_t data[3];
    data[0] = CAN_GEN_CMD_GET_DEV_ID;
    data[1] = (uint8_t)g_can_device_id;
    data[2] = (uint8_t)(g_can_device_id >> 8);
    PRINTF("Response dev id: 0x%04X\r\n", g_can_device_id);
    can_tx_data(data, 3);
}

