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

/**
 * This is template for main module created by New Kinetis SDK 2.x Project Wizard. Enjoy!
 **/

#include "board.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "fsl_port.h"
#include "fsl_lin.h"
#include "fsl_lin_lpuart.h"
#include "lin_cfg.h"
#include "fsl_debug_console.h"
#include "fsl_tpm.h"

#include "fsl_os_abstraction.h"
#include "panic.h"
#include "TimersManager.h"
#include "FunctionLib.h"
#include "can_lin_common.h"
#if OTA_BY_CAN_LIN
#include "EEPROM.h"
#include "OtaSupport.h"
#endif

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define SIM_LPUART_CLK_SEL_OSCERCLK_CLK                   2U  /*!< LPUART clock select: OSCERCLK clock */
#define SIM_TPM_CLK_SEL_OSCERCLK_CLK                      2U  /*!< TPM clock select: OSCERCLK clock */
#define LIN_CLOCK_NAME kCLOCK_Osc0ErClk
#define TJA_WAKEUP 1
#define TIMER_TPM 1
#define DEMO_TPM_BASEADDR TPM2
#define DEMO_TPM_IRQn TPM2_IRQn
#define DEMO_TPM_IRQHandler TPM2_IRQHandler
/* timer frequency */
#define TIMER_FREQ CLOCK_GetFreq(kCLOCK_Osc0ErClk)
/* (timer period (us) * (timer clock frequency)(Hz)) - 1 ) */
#define MODULO_VALUE ((500U*(CLOCK_GetFreq(kCLOCK_Osc0ErClk)/1000000U))-1U)
/* nanoseconds / timer clock frequency  */
#define TIMER_1TICK_DURATION_PS (1000000000000U/TIMER_FREQ)

#define LI0_Master 0x01

#define DEMO_SLPN_GPIO GPIOC
#define DEMO_SLPN_PIN 5

#define MASTER_INSTANCE LI0_Master
#define HARDWARE_INSTANCE 0U

/* make PID from ID */
#define MAKE_PID(id) LIN_ProcessParity(id, MAKE_PARITY)

/* Test Tasks Events */ 
#define gLinTask            (1 << 0)

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

/*!
 * This function prints response content
 * @param instance LIN instance
 * @param id frame ID
 */
#if OTA_BY_CAN_LIN
static void PrintBuffer(uint8_t instance, uint8_t id);
#endif
/*!
 * This timer returns the count of nanoseconds between two consequencing bits.
 * @param ns number of nanoseconds between two bits, return parameter
 */
void timerGetTimeIntervalCallback0(uint32_t *ns);
/*!
 * This function handles messages from low level
 * @param instance LIN instance
 * @param linState pointer to current lin state
 */
void CallbackHandler(uint32_t instance, void * linState);
/*!
 * This function initializes master node and low level
 * @param linUserConfig pointer to user configuration structure
 * @param linCurrentState pointer to LIN low level state structure
 * @param clockSource LIN clock frequency
 */
lin_status_t MasterInit(lin_user_config_t *linUserConfig, lin_state_t *linCurrentState, uint32_t clockSource);
/*!
 * This function switches the schedule table
 */
void MasterScheduleTick(void);
/*!
 * This function process a frame id. If node is publisher, response is sent. If node is subscriber, response is awaited.
 * @param instance LIN instance
 * @param id frame ID
 */
void MasterProcessId(uint8_t instance, uint8_t id);
/*!
 * This function set bus activity timeout
 * @param instance LIN instance
 */
void MasterTimeoutService(uint8_t instance);
/*!
 * This function handle error raised by low-level
 * @param instance LIN instance
 * @param event_id ID of LIN bus event
 * @param id frame ID
 */
void MasterHandleError(uint8_t instance, lin_event_id_t event_id, uint8_t id);
/*!
 * Updates signal, status, and flag after response is sent.
 * @param instance LIN instance
 * @param id frame ID
 */
void MasterUpdateTx(uint8_t instance, uint8_t id);
/*!
 * This function update signal, status, and flag after response is received.
 * @param instance LIN instance
 * @param id frame ID
 */
void MasterUpdateRx(uint8_t instance, uint8_t id);
/*!
 * This function update tx flag array after response is sent.
 * @param instance LIN instance
 * @param id frame ID
 */
void MasterUpdateTxFlags(uint8_t instance, uint8_t id);
/*!
 * This function makes or updates unconditional frames.
 * @param instance LIN instance
 * @param id frame ID
 * @param type type of action
 */
void MasterProcesUncdFrame(uint8_t instance, uint8_t id, uint8_t type);
/*!
 * This function returns index of array ID from RAM table.
 * @param instance LIN instance
 * @param id frame ID
 */
static inline uint8_t MasterGetFrameIndex(uint8_t instance, uint8_t id);

static void OtaTimeoutTimerCallback(void *pParam);

void Lin_Task(void* argument); 

void lin_go_to_sleep(void);

void lin_app_process(uint8_t id, uint8_t frame_offset);

/************************************************************************************
*************************************************************************************
* Private type definitions and macros
*************************************************************************************
************************************************************************************/
/* Size of each ota block */
#define LIN_OTA_BLOCK_SIZE       1024U

/* lin frames of each ota block */
#define LIN_OTA_FRAMES_OF_BLOCK  (LIN_OTA_BLOCK_SIZE / 8)

typedef enum
{
    LIN_APP_STAGE_IDLE = 0x00,
    LIN_APP_STAGE_OTA_TX,
    LIN_APP_STAGE_OTA_END,
      
    LIN_APP_STAGE_LM_TX,
    LIN_APP_STAGE_LM_END  
} lin_app_stage_t;

typedef enum 
{
    LIN_APP_STATUS_IDLE = 0x00,
    LIN_APP_STATUS_OTA_READY,
    LIN_APP_STATUS_OTA_RUNNING,
    LIN_APP_STATUS_OTA_FINISH,
    LIN_APP_STATUS_OTA_ABORT,
        
    LIN_APP_STATUS_LM_READY,        //5
    LIN_APP_STATUS_LM_RUNNING,
    LIN_APP_STATUS_LM_FINISH,
    LIN_APP_STATUS_LM_ABORT
} lin_app_status_t;

typedef enum
{
    LIN_APP_CMD_NONE = 0x00,
    LIN_APP_CMD_OTA_START,
    LIN_APP_CMD_OTA_CONTINUE,
    LIN_APP_CMD_OTA_END,
    
    LIN_APP_CMD_LM_START,       //4  link monitor
    LIN_APP_CMD_LM_CONTINUE,
    LIN_APP_CMD_LM_END,
} lin_app_cmd_c;


/*******************************************************************************
 * Variables
 ******************************************************************************/
/* storage for timer counter */
uint16_t timerCounterValue[2] = {0u};
/* number of timer overflow */
uint16_t timerOverflowInterruptCount = 0u;
/* buffer handling messages between lower and higher level communication */
static uint8_t g_linResponseBuffer[LIN_NUM_OF_IFCS][10];
/* maximal header duration time */
static volatile  uint16_t linMaxHeaderTimeoutVal[LIN_NUM_OF_IFCS];
/* maximal response duration time */
static uint16_t linMaxResTimeoutVal[LIN_NUM_OF_IFCS];
/* interval timer to check the ota status */
static tmrTimerID_t mOtaTimeoutTimerId = gTmrInvalidTimerID_c;

OSA_TASK_DEFINE(Lin_Task, 7, 1, 1024, FALSE ); 
osaTaskId_t gLinTaskId = 0; 
static osaEventId_t  mLinTaskEvent; 

lin_state_t linCurrentState;
lin_user_config_t linUserConfigMaster;

uint8_t first_time_command_flag;
static uint16_t lin_ota_block_frame_count = 0;
#if OTA_BY_CAN_LIN
static uint8_t eeprom_read_buffer[LIN_OTA_BLOCK_SIZE] = {0};
static uint16_t lin_ota_tx_frame_offset = 0;
static uint32_t lin_ota_image_offset = 0;
#endif
static uint8_t lin_ota_timeout_cnt = 0;

static lin_app_stage_t g_lin_app_stage = LIN_APP_STAGE_IDLE;
static lin_app_status_t g_lin_app_status = LIN_APP_STATUS_IDLE; 
 
uint32_t g_lin_ota_image_length = 0;
#if OTA_BY_CAN_LIN
uint32_t g_lin_read_flash_offset = gBootData_ImageLength_Offset_c;
#endif
extern bool_t g_device_reset_for_ota;
extern bool_t g_ota_image_ready_for_lin_or_can;

//link monitor
uint8_t gLMConnectionParaBlockNum = 0; 

/*******************************************************************************
 * Code
 ******************************************************************************/
static inline uint8_t MasterGetFrameIndex(uint8_t instance, uint8_t id)
{
    uint8_t retVal = 0xFF;
    uint8_t i;

    const lin_protocol_user_config_t *prot_user_config_ptr = &g_lin_protocol_user_cfg_array[HARDWARE_INSTANCE];

    for (i = prot_user_config_ptr->number_of_configurable_frames; i > 0U; i--)
    {
        if (prot_user_config_ptr->list_identifiers_RAM_ptr[i] == id)
        {
            retVal = i + prot_user_config_ptr->frame_start - 1U;
            break;
        }
    }
    return retVal;
}
#if OTA_BY_CAN_LIN
static void PrintBuffer(uint8_t instance, uint8_t id)
{
    uint8_t i = 0;
    uint8_t frame_index;
    uint8_t frame_len;
    uint8_t print_byte;

    const lin_protocol_user_config_t *prot_user_config_ptr = &g_lin_protocol_user_cfg_array[HARDWARE_INSTANCE];
    lin_protocol_state_t *prot_state_ptr = &g_lin_protocol_state_array[HARDWARE_INSTANCE];

    /* get index of frame in RAM table */
    frame_index = MasterGetFrameIndex(instance, id);
    /* get length of frame in RAM table */
    frame_len = prot_user_config_ptr->frame_tbl_ptr[frame_index].frm_len;
    /* print whole response from buffer */

    while (i < frame_len)
    {
        print_byte = prot_state_ptr->response_buffer_ptr[i++];
        PRINTF("%02X ", print_byte);
    }
    PRINTF("\r\n");
}
#endif
#if defined(TIMER_TPM) && TIMER_TPM
/*!
 * This interrupt routine checks for bus timeout and switches schedule table
 */
void DEMO_TPM_IRQHandler(void)
{
    static volatile uint32_t tick_count = 0U;

    /* if timer overflow flag */
    if(TPM_GetStatusFlags(DEMO_TPM_BASEADDR) & kTPM_TimeOverflowFlag)
    {
         /* Clear interrupt flag.*/
        TPM_ClearStatusFlags(DEMO_TPM_BASEADDR, kTPM_TimeOverflowFlag);
        /* increase number of overflow count */
        timerOverflowInterruptCount++;
        /* check timeout */
        MasterTimeoutService(MASTER_INSTANCE);

        /* reload schedule every 5ms */
        if (++tick_count >= 10U)
        {
            tick_count = 0U;
            MasterScheduleTick();
        }
        else
        {
            ;
        }
    }
}
#endif

#if defined(TIMER_TPM) && TIMER_TPM
void timerGetTimeIntervalCallback0(uint32_t *ns)
{
    uint16_t moduloValue = MODULO_VALUE;

    /* check current CNT value */
    timerCounterValue[1] = (uint16_t)(DEMO_TPM_BASEADDR->CNT);
    /* calculate number of ns from current and previous count value */
    if(timerCounterValue[1]>=timerCounterValue[0])
    {
        *ns = (uint32_t)((uint32_t)(((timerCounterValue[1] - timerCounterValue[0])*TIMER_1TICK_DURATION_PS)/1000U) + (timerOverflowInterruptCount*500000U));
    }
    else /* (timerCounterValue[1]<timerCounterValue[0]) */
    {
        *ns = (uint32_t)(((moduloValue - timerCounterValue[0] + timerCounterValue[1])*TIMER_1TICK_DURATION_PS)/1000U);
        if(timerOverflowInterruptCount>0)
        {
            *ns += (uint32_t)((timerOverflowInterruptCount-1)*500000U);
        }
    }
    
    /* set current count value to previous count value */
    timerCounterValue[0] = timerCounterValue[1];
    /* clear timerOverflowInterruptCount mark */
    timerOverflowInterruptCount = 0U;
}
#endif

void CallbackHandler(uint32_t instance, void * linState)
{
    lin_state_t * linCurrentState = (lin_state_t *) linState;
    uint8_t bytesRemaining;

    const lin_protocol_user_config_t  *prot_user_config_ptr = &g_lin_protocol_user_cfg_array[HARDWARE_INSTANCE];
    lin_protocol_state_t              *prot_state_ptr       = &g_lin_protocol_state_array[HARDWARE_INSTANCE];

    /* check timeout flag */
    if(linCurrentState->timeoutCounterFlag == true)
    {
        /* set timeout error event id */
        linCurrentState->currentEventId = LIN_TIMEOUT_ERROR;
    }
    /* check event id */
    switch (linCurrentState->currentEventId)
    {
        case LIN_PID_OK:
            /* process PID of frame */
            MasterProcessId(instance, linCurrentState->currentId);
            break;
        case LIN_RECV_BREAK_FIELD_OK:
            /* reload frame timeout */
            prot_state_ptr->frame_timeout_cnt = linMaxResTimeoutVal[HARDWARE_INSTANCE] + linMaxHeaderTimeoutVal[HARDWARE_INSTANCE];
            break;
        case LIN_TX_COMPLETED:
            /* update protocol state */
            MasterUpdateTx(instance, linCurrentState->currentId);
            break;
        case LIN_RX_COMPLETED:
            /* update protocol state */
            MasterUpdateRx(instance, linCurrentState->currentId);
            
            break;
        case LIN_BUS_ACTIVITY_TIMEOUT:
        case LIN_TIMEOUT_ERROR:
            /* check for remaining bytes */
            (void)LIN_GetReceiveStatus(instance, &bytesRemaining);
            if (linCurrentState->rxSize > bytesRemaining)
            {
                /* report timeout to higher level */
                MasterHandleError(instance, LIN_NO_DATA_TIMEOUT, linCurrentState->currentId);
            }
          break;
        case LIN_PID_ERROR:
        case LIN_FRAME_ERROR:
        case LIN_CHECKSUM_ERROR:
        case LIN_READBACK_ERROR:
        case LIN_RECV_SYNC_ERROR:
            /* report error to higher level */
            MasterHandleError(instance, linCurrentState->currentEventId, linCurrentState->currentId);
            break;
        case LIN_WAKEUP_SIGNAL:
        case LIN_NO_EVENT:
        case LIN_RECV_SYNC_OK:
        default:
          /* do nothing */
          break;
    }
    /* update idle timeout */
    prot_state_ptr->idle_timeout_cnt = prot_user_config_ptr->max_idle_timeout_cnt;
}

lin_status_t MasterInit(lin_user_config_t *linUserConfig, lin_state_t *linCurrentState, uint32_t clockSource)
{  
    lin_status_t status = LIN_SUCCESS;
    /* initialize protocol variables */
    const lin_protocol_user_config_t *prot_user_config_ptr = &g_lin_protocol_user_cfg_array[HARDWARE_INSTANCE];
    lin_protocol_state_t *prot_state_ptr = &g_lin_protocol_state_array[HARDWARE_INSTANCE];

    if(clockSource == 0)
    {
        /* with no clock report error */
        status = LIN_ERROR;
    }
    else
    {
        /* initialize LIN low lovel */
        status = LIN_Init(MASTER_INSTANCE,  linUserConfig, linCurrentState,  clockSource);
        /* make hardware instance visible to lower level */
        g_linUserconfigPtr[MASTER_INSTANCE]->hardware_instance = HARDWARE_INSTANCE;
        /* register time interval callback */
        linUserConfig->timerGetTimeIntervalCallback = &timerGetTimeIntervalCallback0;
        /* install callback service */
        LIN_InstallCallback(MASTER_INSTANCE, CallbackHandler);

        prot_state_ptr->baud_rate = linUserConfig->baudRate;
        prot_state_ptr->response_buffer_ptr = g_linResponseBuffer[HARDWARE_INSTANCE];
        prot_state_ptr->idle_timeout_cnt = prot_user_config_ptr->max_idle_timeout_cnt;

        linMaxResTimeoutVal[HARDWARE_INSTANCE] = LIN_CalcMaxResTimeoutCnt(prot_state_ptr->baud_rate, 8U);
        prot_state_ptr->frame_timeout_cnt = linMaxResTimeoutVal[HARDWARE_INSTANCE];
        linMaxHeaderTimeoutVal[HARDWARE_INSTANCE] = LIN_CalcMaxHeaderTimeoutCnt(prot_state_ptr->baud_rate);
    }
    return status;
}


void MasterScheduleTick(void)
{
  OSA_EventSet(mLinTaskEvent, gLinTask);  
}


void MasterProcessId(uint8_t instance, uint8_t id)
{
    uint8_t frame_index;
    uint32_t response_length;
    uint32_t lin_max_frame_res_timeout_val;
    const lin_frame_struct *lin_frame_ptr;

    const lin_protocol_user_config_t *prot_user_config_ptr = &g_lin_protocol_user_cfg_array[HARDWARE_INSTANCE];
    lin_protocol_state_t *prot_state_ptr = &g_lin_protocol_state_array[HARDWARE_INSTANCE];

    /* get index of current frame from RAM table */
    frame_index = MasterGetFrameIndex(instance, id);

    if(frame_index != 0xFF)
    {
        prot_state_ptr->go_to_sleep_flg = (bool)0U;
        /* get frame buffer pointer */
        lin_frame_ptr = &(prot_user_config_ptr->frame_tbl_ptr[frame_index]);
        /* check if id represents a supported frame */
        if ((id > 0) && (id < 0x3B))
        {
            response_length = lin_frame_ptr->frm_len;
            lin_max_frame_res_timeout_val = LIN_CalcMaxResTimeoutCnt(prot_state_ptr->baud_rate, response_length);

            /* check response type */
            if (LIN_RES_PUB == lin_frame_ptr->frm_response)
            {
                /* make unconditional frame */
                MasterProcesUncdFrame(instance, id, LIN_MAKE_UNCONDITIONAL_FRAME);

                if ((response_length <= 8U) && (response_length > 0U))
                {
                    /* Set response */
                    LIN_SetResponse(instance, &(prot_state_ptr->response_buffer_ptr[0]),  response_length, lin_max_frame_res_timeout_val);
                }
            }
            else
            { 
                if ((response_length <= 8U) && (response_length != 0U))
                {
                    /* wait for response */
                    LIN_RxResponse(instance, &(prot_state_ptr->response_buffer_ptr[0]),  response_length, lin_max_frame_res_timeout_val);
                } 
            }
        }
        /* frame is not supported */
        else
        {
            /* ignore frame */
            LIN_IgnoreResponse(instance);
        }
    }
    /* unknown id */
    else
    {
        /* ignore frame */
        LIN_IgnoreResponse(instance);
    } 
}

void MasterProcesUncdFrame(uint8_t instance, uint8_t id, uint8_t type)
{
    uint8_t frame_index;
    uint8_t *response_buffer_ptr;
    uint8_t i;
    uint8_t frame_byte_offset;

    const lin_protocol_user_config_t *prot_user_config_ptr = &g_lin_protocol_user_cfg_array[HARDWARE_INSTANCE];
    lin_protocol_state_t *prot_state_ptr = &g_lin_protocol_state_array[HARDWARE_INSTANCE];

    /* get index of current frame from RAM table */
    frame_index = MasterGetFrameIndex(instance, id);
    /* get protocol reponse buffer */
    if(frame_index != 0xFF)
    {
        response_buffer_ptr = prot_state_ptr->response_buffer_ptr;
        /* get protocolo reponse buffer length */
        prot_state_ptr->response_length = prot_user_config_ptr->frame_tbl_ptr[frame_index].frm_len;
        /* get protocolo reponse buffer offset */
        frame_byte_offset = prot_user_config_ptr->frame_tbl_ptr[frame_index].frm_offset;

        /* check for frame type */
        if (LIN_MAKE_UNCONDITIONAL_FRAME == type)
        {
            lin_app_process(id, frame_byte_offset);
                                        
            /* Get data from configuration buffer */
            for (i = 0U; i < prot_state_ptr->response_length; i++)
            {
                response_buffer_ptr[i] = g_lin_frame_data_buffer[frame_byte_offset + i];
            }
        }
        /* update unconditional frame */
        else
        {
            for (i = 0U; i < prot_state_ptr->response_length; i++)
            {
                /* Set data of configuration buffer */
                g_lin_frame_data_buffer[frame_byte_offset + i] = response_buffer_ptr[i];
            }
            lin_app_process(id, frame_byte_offset);
        }
    }
}

void MasterUpdateTx(uint8_t instance, uint8_t id)
{
    uint8_t frame_index;
    lin_protocol_state_t *prot_state_ptr = &g_lin_protocol_state_array[HARDWARE_INSTANCE];

    /* Set successful transfer */
    prot_state_ptr->successful_transfer = 1U;

    if (prot_state_ptr->num_of_processed_frame < 0xFFU)
    {
        /* increase a number of processed frames */
        prot_state_ptr->num_of_processed_frame++;
    }
    /* Check if frame contains error signal */
    if ((bool)1U == prot_state_ptr->transmit_error_resp_sig_flg)
    {
        /* Set error in response */
        prot_state_ptr->error_in_response = 0U;
        /* clear error flag */
        prot_state_ptr->transmit_error_resp_sig_flg = (bool)0U;
    }
    else
    {
        /* increase a number of succesfull frames */
        prot_state_ptr->num_of_successfull_frame++;
    }
    /* print received response */
    //PrintBuffer(instance, id);
    /* get index of current frame from RAM table */
    frame_index = MasterGetFrameIndex(instance, id);
    /* update Tx flags */
    MasterUpdateTxFlags(instance, frame_index);
}

void MasterUpdateRx(uint8_t instance, uint8_t id)
{
    lin_protocol_state_t *prot_state_ptr = &g_lin_protocol_state_array[HARDWARE_INSTANCE];

    /* Set successful transfer */
    prot_state_ptr->successful_transfer = 1U;

    if (prot_state_ptr->num_of_processed_frame < 0xFFU)
    {
        /* increase a number of processed frames */
        prot_state_ptr->num_of_processed_frame++;
    }
    /* check if id represents a supported frame */
    if ((id > 0) && (id < 0x3B))
    {
        /* make unconditional frame */
        MasterProcesUncdFrame(instance, id, LIN_UPDATE_UNCONDITIONAL_FRAME);
        /* increase a number of succesfull frames */
        prot_state_ptr->num_of_successfull_frame++;
        /* print received response */
        //PrintBuffer(instance, id);
    }
    else
    {
            ;
    }
}

void MasterUpdateTxFlags(uint8_t instance, uint8_t id)
{
    uint8_t flag_offset;
    uint8_t flag_size;
    uint8_t i;
    const lin_frame_struct *lin_frame_ptr;

    const lin_protocol_user_config_t  *prot_user_config_ptr = &g_lin_protocol_user_cfg_array[HARDWARE_INSTANCE];
    lin_frame_ptr = &(prot_user_config_ptr->frame_tbl_ptr[id]);

    if (LIN_FRM_UNCD == lin_frame_ptr->frm_type)
    {
        flag_offset = lin_frame_ptr->flag_offset;
        flag_size = lin_frame_ptr->flag_size;
        /* Update transmit flags */
        for (i = 0U; i < flag_size; i++)
        {
            g_lin_flag_handle_tbl[flag_offset] = 0x00U;
            flag_offset++;
        }
    }
}

void MasterTimeoutService(uint8_t instance)
{
    lin_state_t *linState = g_linStatePtr[MASTER_INSTANCE];
    const lin_protocol_user_config_t  *prot_user_config_ptr = &g_lin_protocol_user_cfg_array[HARDWARE_INSTANCE];
    lin_protocol_state_t *prot_state_ptr = &g_lin_protocol_state_array[HARDWARE_INSTANCE];

    /* check if timeout occurs during communication */
    LIN_TimeoutService(instance);

    /* check for current node state */
    switch (linState->currentNodeState)
    {
        case LIN_NODE_STATE_IDLE:
            /* check if idle timeout runs out */
            if (!(prot_state_ptr->idle_timeout_cnt-- > 0U))
            {
                /* Set go to sleep flag */
                prot_state_ptr->go_to_sleep_flg = (bool)1U;

                /* Put current node to Idle state, reset idle timeout count */
                prot_state_ptr->idle_timeout_cnt = prot_user_config_ptr->max_idle_timeout_cnt;

                /* Put current node to sleep mode */
                (void)LIN_GoToSleepMode(instance);
            }
            break;
        case LIN_NODE_STATE_RECV_PID:
        case LIN_NODE_STATE_SEND_PID:
        case LIN_NODE_STATE_RECV_SYNC:
            /* timeout send has occurred - change state of the node and inform core */
            if (!(prot_state_ptr->frame_timeout_cnt -- > 0U))
            {
                /* Go to idle state */
                (void)LIN_GotoIdleState(instance);

                /* Reset frame count timeout */
                prot_state_ptr->frame_timeout_cnt = linMaxResTimeoutVal[HARDWARE_INSTANCE];
            }
            break;
        case LIN_NODE_STATE_SEND_DATA:
        case LIN_NODE_STATE_SEND_DATA_COMPLETED:
            /* timeout send has occurred - change state of the node and inform core */
            if (!(prot_state_ptr->frame_timeout_cnt-- >0U ))
            {
                /* Abort frame data transferring */
                (void)LIN_AbortTransferData(instance);

                /* Reset frame count timeout */
                prot_state_ptr->frame_timeout_cnt = linMaxResTimeoutVal[HARDWARE_INSTANCE];
            }
            break;
        case LIN_NODE_STATE_UNINIT:
        case LIN_NODE_STATE_SLEEP_MODE:
        case LIN_NODE_STATE_SEND_BREAK_FIELD:
        case LIN_NODE_STATE_RECV_DATA:
        case LIN_NODE_STATE_RECV_DATA_COMPLETED:
        default:
            /* do nothing */
            break;
    }
}

void MasterHandleError(uint8_t instance, lin_event_id_t event_id, uint8_t id)
{

    lin_protocol_state_t *prot_state_ptr = &g_lin_protocol_state_array[HARDWARE_INSTANCE];

    /* increase a number of processed frames */
    if (prot_state_ptr->num_of_processed_frame < 0xFFU)
    {
        prot_state_ptr->num_of_processed_frame++;
    }

    switch (event_id)
    {
        /* PID error */
        case LIN_PID_ERROR:
        case LIN_FRAME_ERROR:
        case LIN_CHECKSUM_ERROR:
        case LIN_READBACK_ERROR:
        case LIN_RECV_SYNC_ERROR:
            /* Set response error */
            prot_state_ptr->error_in_response += 1U;
            break;
        case LIN_NO_DATA_TIMEOUT:
            /* Set timeout error */
            prot_state_ptr->timeout_in_response += 1U;
            break;
        default:
            /* do nothing */
            break;
    }
}

void lin_go_to_sleep(void)
{  
    lin_protocol_state_t *prot_state_ptr = &g_lin_protocol_state_array[HARDWARE_INSTANCE];  
    prot_state_ptr->go_to_sleep_flg = (bool)1U;
}

uint8_t check_if_lin_sleeping(void)
{  
    lin_state_t * linCurrentState = g_linStatePtr[MASTER_INSTANCE];
  
    if(linCurrentState->currentNodeState == LIN_NODE_STATE_SLEEP_MODE)
    {   
        if (!first_time_command_flag)
        {
            LIN_SendWakeupSignal(0x01);  //first time turn on lin
            first_time_command_flag = 1;
            return 0;
        } 
        else
        {        
            return 1; //lin is off, send message to app
        }        
    }
    else
    {
        return 0; //lin is on
    }
}

/*!
 * @brief start the lin ota for slave
 */
void lin_ota_start(void)
{
    PRINTF("lin_ota_start\r\n");
#if OTA_BY_CAN_LIN     
    /* if device resets, initial the eeprom and start ota */
    if (g_device_reset_for_ota)
    {
        g_device_reset_for_ota = FALSE; 
        /* check if there is new image on the flash for lin slave ota */
        if (FLib_MemCmpToVal((void*)gBootFlag_NewImageAvailable_Offset_c, gBootValueForLinSlave_c, 1))
        {
            if(OTA_InitExternalMemory() != gOtaSucess_c)
            {
                PRINTF("eeprom init fail\r\n");
                return;
            }

            uint8_t length[gBootData_ImageLength_Size_c];
            EEPROM_ReadData(gBootData_ImageLength_Size_c, gBootData_ImageLength_Offset_c, length);
            /* the length of image data */
            g_lin_ota_image_length = (length[2] << 16) + (length[1] << 8) + length[0]; 
            /* add the length of image header */
            g_lin_ota_image_length += gBootData_Image_Offset_c; 
            PRINTF("parsed image length: %d\r\n", g_lin_ota_image_length);
        }
        else
        {
            PRINTF("no new image for lin slave\r\n");
            return;
        }
    }

    PRINTF("lin wake up\r\n");
    LIN_SendWakeupSignal(0x01);
#endif
}

/*!
 * @brief process the lin ota running procedure
 */
void lin_app_process(uint8_t id, uint8_t frame_offset)
{
    uint8_t *frame_data = g_lin_frame_data_buffer;

    switch (id)
    {
        case gID_Cmd_c:
        {
            if (LIN_APP_STAGE_IDLE == g_lin_app_stage)
            {
#if OTA_BY_CAN_LIN
                if (g_ota_image_ready_for_lin_or_can)
                {
                    frame_data[frame_offset] = LIN_APP_CMD_OTA_START;
                    g_lin_read_flash_offset = gBootData_ImageLength_Offset_c;
                    lin_ota_image_offset = g_lin_read_flash_offset;
                    lin_ota_tx_frame_offset = 0;
                    TMR_StartLowPowerTimer(mOtaTimeoutTimerId, gTmrLowPowerIntervalMillisTimer_c, 500, OtaTimeoutTimerCallback, NULL);
                }
#endif
                if(gLMConnectionReady)
                {
                    gLMConnectionReady = FALSE;                     
                    frame_data[frame_offset] = LIN_APP_CMD_LM_START;
                    TMR_StartLowPowerTimer(mOtaTimeoutTimerId, gTmrLowPowerIntervalMillisTimer_c, 500, OtaTimeoutTimerCallback, NULL);                    
                }
            }
#if OTA_BY_CAN_LIN 
            else if (LIN_APP_STAGE_OTA_TX == g_lin_app_stage)
            {
                if ((g_lin_read_flash_offset == g_lin_ota_image_length)
                    || (LIN_APP_STATUS_OTA_ABORT == g_lin_app_status))
                {
                    frame_data[frame_offset] = LIN_APP_CMD_OTA_END;
                    g_lin_app_stage = LIN_APP_STAGE_OTA_END;
                    g_lin_read_flash_offset = gBootData_ImageLength_Offset_c;
                    lin_ota_image_offset = g_lin_read_flash_offset;
                    lin_ota_tx_frame_offset = 0;                    
                }
                else
                {
                    frame_data[frame_offset] = LIN_APP_CMD_OTA_CONTINUE;
                }
            }
            else if (LIN_APP_STAGE_OTA_END == g_lin_app_stage)
            {
                frame_data[frame_offset] = LIN_APP_CMD_OTA_END;
                g_lin_app_stage = LIN_APP_STAGE_IDLE;
                g_lin_read_flash_offset = gBootData_ImageLength_Offset_c;
                lin_ota_image_offset = g_lin_read_flash_offset;
                lin_ota_tx_frame_offset = 0;                
                g_ota_image_ready_for_lin_or_can = FALSE;
                lin_ota_timeout_cnt = 0;
                TMR_StopTimer(mOtaTimeoutTimerId);
            }
#endif  
            else if (LIN_APP_STAGE_LM_TX == g_lin_app_stage)
            {
                if (LIN_APP_STATUS_LM_ABORT == g_lin_app_status){
                    frame_data[frame_offset] = LIN_APP_CMD_LM_END;
                }else{
                    frame_data[frame_offset] = LIN_APP_CMD_LM_CONTINUE;
                }
            }
            else if (LIN_APP_STAGE_LM_END == g_lin_app_stage)
            {
                frame_data[frame_offset] = LIN_APP_CMD_LM_END;
                g_lin_app_stage = LIN_APP_STAGE_IDLE;
                TMR_StopTimer(mOtaTimeoutTimerId);
            }
            break;
        }

            
        case gID_Data_c:
        {
#if OTA_BY_CAN_LIN 
            if (LIN_APP_STAGE_OTA_TX == g_lin_app_stage)
            {
                if ((lin_ota_image_offset + 8) <= g_lin_ota_image_length)
                {                    
                    FLib_MemCpy(&(frame_data[frame_offset]), &(eeprom_read_buffer[lin_ota_tx_frame_offset]), 8);
                    lin_ota_image_offset += 8;
                    lin_ota_tx_frame_offset += 8;
                    lin_ota_block_frame_count ++;
                    if (lin_ota_image_offset + 8 == g_lin_ota_image_length)
                    {
                        g_lin_app_stage = LIN_APP_STAGE_OTA_END;
                    }
                }
                else if (lin_ota_image_offset < g_lin_ota_image_length)
                {
                    uint8_t len = g_lin_ota_image_length - lin_ota_image_offset;
                    FLib_MemCpy(&(frame_data[frame_offset]), &(eeprom_read_buffer[lin_ota_tx_frame_offset]), len);
                    FLib_MemSet(&(frame_data[frame_offset + len]), 0xFF, (8 - len));
                    lin_ota_image_offset = g_lin_ota_image_length;
                    lin_ota_block_frame_count ++;
                    g_lin_read_flash_offset = g_lin_ota_image_length;
                    g_lin_app_stage = LIN_APP_STAGE_OTA_END;
                }                
            }
#endif
            if (LIN_APP_STAGE_LM_TX == g_lin_app_stage)
            {
                switch(gLMConnectionParaBlockNum)
                {
                    case 0:
                    {
                        frame_data[frame_offset]   = gLMPara.peerDeviceId;
                        frame_data[frame_offset+1] = gLMPara.connOrDisconn;
                        frame_data[frame_offset+2] = gLMPara.CI >> 8;
                        frame_data[frame_offset+3] = gLMPara.CI & 0xFF;
                        frame_data[frame_offset+4] = gLMPara.hopIncrement;                        
                    }
                    break;
                    case 1:
                    {
                        frame_data[frame_offset] = (gLMPara.crcSeed>>24) & 0xFF;
                        frame_data[frame_offset+1] = (gLMPara.crcSeed>>16) & 0xFF;
                        frame_data[frame_offset+2] = (gLMPara.crcSeed>>8) & 0xFF;
                        frame_data[frame_offset+3] = (gLMPara.crcSeed>>0) & 0xFF;
                        frame_data[frame_offset+4] = (gLMPara.syncAddress>>24) & 0xFF;
                        frame_data[frame_offset+5] = (gLMPara.syncAddress>>16) & 0xFF;
                        frame_data[frame_offset+6] = (gLMPara.syncAddress>>8) & 0xFF;
                        frame_data[frame_offset+7] = (gLMPara.syncAddress>>0) & 0xFF;
                        g_lin_app_stage = LIN_APP_STAGE_LM_END;
                    }
                    break;
                    default:
                    break;
                }               
            }
            else if (LIN_APP_STAGE_IDLE == g_lin_app_stage) 
            {
                PRINTF("lin sleep\r\n");
                lin_go_to_sleep();
                TMR_StopTimer(mOtaTimeoutTimerId);
            }
            else
            {
                FLib_MemSet(&(frame_data[frame_offset]), 0x00, 8);
            }
            break;
        }
        
        case gID_GetStatus_c:
        {
#if OTA_BY_CAN_LIN 
            if ((LIN_APP_STATUS_OTA_READY == frame_data[frame_offset])
                || (LIN_APP_STATUS_OTA_RUNNING == frame_data[frame_offset]))
            {
                g_lin_app_stage = LIN_APP_STAGE_OTA_TX;
                uint8_t lin_ota_block_num = frame_data[frame_offset + 1];
                PRINTF("status: %x, block num: %d\r\n", frame_data[frame_offset], lin_ota_block_num);
                
                g_lin_read_flash_offset = gBootData_ImageLength_Offset_c + lin_ota_block_num * LIN_OTA_BLOCK_SIZE;
                if ((g_lin_read_flash_offset + LIN_OTA_BLOCK_SIZE) <= g_lin_ota_image_length)
                {
                    EEPROM_ReadData(LIN_OTA_BLOCK_SIZE, g_lin_read_flash_offset, eeprom_read_buffer);                    
                    lin_ota_image_offset = g_lin_read_flash_offset;
                }
                else if (g_lin_read_flash_offset < g_lin_ota_image_length)
                {
                    uint16_t length;
                    length = g_lin_ota_image_length - g_lin_read_flash_offset;
                    EEPROM_ReadData(length, g_lin_read_flash_offset, eeprom_read_buffer);
                    FLib_MemSet(&(eeprom_read_buffer[length]), 0xFF, LIN_OTA_BLOCK_SIZE - length);
                    lin_ota_image_offset = g_lin_read_flash_offset;
                }
                else
                {
                    PRINTF("invalid block request!!!\r\n");;
                    lin_ota_image_offset = g_lin_ota_image_length;                    
                }
                lin_ota_block_frame_count = 0;
                lin_ota_tx_frame_offset = 0;                
            }
            else if ((LIN_APP_STATUS_OTA_FINISH == frame_data[frame_offset])
                || (LIN_APP_STATUS_OTA_ABORT == frame_data[frame_offset]))
            {
                g_lin_app_stage = LIN_APP_STAGE_IDLE;
                lin_ota_block_frame_count = 0;
                lin_ota_tx_frame_offset = 0;
                lin_ota_image_offset = 0;
            }
#endif
            if ((LIN_APP_STATUS_LM_READY == frame_data[frame_offset])
                || (LIN_APP_STATUS_LM_RUNNING == frame_data[frame_offset]))
            {
                g_lin_app_stage = LIN_APP_STAGE_LM_TX;
                gLMConnectionParaBlockNum = frame_data[frame_offset + 1];
//                PRINTF("status: %x, block num: %d\r\n", frame_data[frame_offset], gLMConnectionParaBlockNum);
            }
            else if ((LIN_APP_STATUS_LM_FINISH == frame_data[frame_offset])
                || (LIN_APP_STATUS_LM_ABORT == frame_data[frame_offset]))
            {
                g_lin_app_stage = LIN_APP_STAGE_IDLE;
            }
            lin_ota_timeout_cnt = 0;
            break;
        }            
        default:
            break;
    }           
} 

static void OtaTimeoutTimerCallback(void *pParam)
{
    lin_ota_timeout_cnt ++;
    if (lin_ota_timeout_cnt >= 10)
    {
        PRINTF("lin ota timeout, reset related variables\r\n");
        lin_ota_timeout_cnt = 0;
        g_lin_app_status = LIN_APP_STATUS_IDLE;
        g_lin_app_stage = LIN_APP_STAGE_IDLE;
#if OTA_BY_CAN_LIN
        lin_ota_block_frame_count = 0;
        lin_ota_image_offset = 0;
        lin_ota_tx_frame_offset = 0;        
        g_lin_ota_image_length = 0;
        g_lin_read_flash_offset = gBootData_ImageLength_Offset_c;
        FLib_MemSet(eeprom_read_buffer, 0x00, LIN_OTA_BLOCK_SIZE);
#endif
        TMR_StopTimer(mOtaTimeoutTimerId);
    }
}


/*!
 * @brief Application entry point.
 */
void lin_demo_init_master(void) {

    gpio_pin_config_t slpn_config = {
        kGPIO_DigitalOutput, 1,
    };

    tpm_config_t tpmInfo;
    uint32_t linfreq;
    lin_status_t status;

    /* Init board hardware. */
    BOARD_InitLinLpuart();
    /* Set LIN LPUART clock */
    CLOCK_SetLpuart1Clock(SIM_LPUART_CLK_SEL_OSCERCLK_CLK);
#if defined(TIMER_TPM) && TIMER_TPM
    /* Set Timer LPUART clock */
    CLOCK_SetTpmClock(SIM_TPM_CLK_SEL_OSCERCLK_CLK);
#endif

     gLinTaskId = OSA_TaskCreate(OSA_TASK(Lin_Task), NULL); 
     if( NULL == gLinTaskId )
     {
          panic(0,0,0,0);
          return;
     }
    
     mLinTaskEvent = OSA_EventCreate(TRUE); 
     if( NULL == mLinTaskEvent )
     {
          panic(0,0,0,0);
          return;
     }    
    
    /*
     * .linUserConfigMaste.autobaudEnable = false;
     * .linUserConfigMaster.baudRate = 19200;
     * .linUserConfigMaster.nodeFunction = MASTER;
     * .linUserConfigMaster.timerGetTimeIntervalCallback = NULL;
     */

    LIN_GetMasterDefaultConfig(&linUserConfigMaster);
    /* get LIN clock frequency */
    linfreq  = CLOCK_GetFreq(LIN_CLOCK_NAME);


#if defined(TIMER_TPM) && TIMER_TPM
    TPM_GetDefaultConfig(&tpmInfo);
    /* Initialize TPM module */
    TPM_Init(DEMO_TPM_BASEADDR, &tpmInfo);
    /* Set module value */
    DEMO_TPM_BASEADDR->MOD = MODULO_VALUE;
    /* Enable interrupt on overflow */
    TPM_EnableInterrupts(DEMO_TPM_BASEADDR, kTPM_TimeOverflowInterruptEnable);
    /* Enable at the NVIC */
    EnableIRQ(DEMO_TPM_IRQn);
#endif

#if defined(TJA_WAKEUP) && TJA_WAKEUP
    /* Wakeup TJA transceiver */
    GPIO_PinInit(DEMO_SLPN_GPIO, DEMO_SLPN_PIN, &slpn_config);
#endif

    /* Initialize master node */
    status = MasterInit(&linUserConfigMaster, &linCurrentState, linfreq);

    if(status != LIN_INITIALIZED)
    {
        PRINTF("\r\nLIN initialization failed!");
    }
    else
    {
//        PRINTF("\r\nLIN master initialized\r\n");
    }    

    /* Sleep until "lin on" command is sent*/
    LIN_GoToSleepMode(MASTER_INSTANCE);

#if defined(TIMER_TPM) && TIMER_TPM
    /* Start scheduler */
    TPM_StartTimer(DEMO_TPM_BASEADDR, kTPM_SystemClock);
#endif  

    mOtaTimeoutTimerId = TMR_AllocateTimer();  
}

void Lin_Task (void* argument)
{    
    osaEventFlags_t event;  
    
    lin_status_t status = LIN_SUCCESS;
    uint8_t current_id;
    lin_protocol_state_t *prot_state_ptr = &g_lin_protocol_state_array[HARDWARE_INSTANCE];
    const lin_protocol_user_config_t *prot_user_config_ptr = &g_lin_protocol_user_cfg_array[HARDWARE_INSTANCE];
    lin_state_t * linCurrentState = g_linStatePtr[MASTER_INSTANCE];
    static uint32_t prev_error_in_transfer = 0U;
    static int curr_id_index = 1;
    static int32_t frame_delay_count = 0;   
    
    while(1)
    {
        OSA_EventWait(mLinTaskEvent, gLinTask, FALSE, osaWaitForever_c , &event);
        if (event & gLinTask) 
        {          
            if(frame_delay_count<=0)
            {
                if(prot_state_ptr->go_to_sleep_flg == (bool)1U)
                {
                    LIN_GoToSleepMode(MASTER_INSTANCE);
                    prot_state_ptr->go_to_sleep_flg = (bool)0U;
                }
                else if(linCurrentState->currentNodeState != LIN_NODE_STATE_SLEEP_MODE)
                {
                    if( (prot_state_ptr->error_in_response != prev_error_in_transfer) )
                    {
                        if(curr_id_index > 1U)
                        {
                            curr_id_index--;
                        }
                    }
                    current_id = prot_user_config_ptr->list_identifiers_RAM_ptr[curr_id_index];

                    if (0xFFU != current_id)
                    {
                        frame_delay_count = prot_user_config_ptr->frame_tbl_ptr[curr_id_index-1].delay;
                        prot_state_ptr->frame_timeout_cnt = linMaxResTimeoutVal[HARDWARE_INSTANCE] + linMaxHeaderTimeoutVal[HARDWARE_INSTANCE];
                        status = LIN_MasterSendHeader(MASTER_INSTANCE, current_id);
                        if(status != LIN_SUCCESS)
                        {
                            LIN_GoToSleepMode(MASTER_INSTANCE);
                        }
                        prev_error_in_transfer = prot_state_ptr->error_in_response;
                    }
                    
                    if ((gID_Data_c == current_id) &&
                        (LIN_APP_STAGE_OTA_TX == g_lin_app_stage) &&
                        (lin_ota_block_frame_count < (LIN_OTA_FRAMES_OF_BLOCK - 1)))
                    {
                        /* keep sending ota data */
                    }
                    else
                    {
                        curr_id_index ++;
                    }
                    if(curr_id_index >=  prot_user_config_ptr->number_of_configurable_frames)
                    {
                        curr_id_index = 1;
                    }
                }
                else
                {
                }
            }
            else
            {
                frame_delay_count--;
            }          
        }                  
    }
} 
