
#include "lin_lib_config.h"
#include "lin_core.h"
#include "lin_tp.h"
#include "lin_types.h"


/** internal macro */
#define PCI_SF                          0x00U       /*!< Single Frame */
#define PCI_FF                          0x01U       /*!< First Frame */
#define PCI_CF                          0x02U       /*!< Consecutive Frame */

#define RES_POSITIVE                    0x40U       /*!< Positive response */
#define LIN_NEGATIVE                        0U          /*!< Negative response */
#define LIN_POSITIVE                        1U          /*!< Positive response */

#define LIN_PCI_RES_READ_BY_IDENTIFY          0x06U      /*!< PCI response value read by identify */
#define LIN_PCI_RES_SAVE_CONFIGURATION        0x01U      /*!< PCI response value save configuration */
#define LIN_PCI_RES_ASSIGN_FRAME_ID_RANGE     0x01U      /*!< PCI response value assign frame id range */


#define LIN_READ_USR_DEF_MIN              32U       /*!< Min user defined */
#define LIN_READ_USR_DEF_MAX              63U       /*!< Max user defined */
/*****/

/**
 * @brief init relative queue and change txStatus or rxStatus
 * @param[in] inst ifc handle
 * @param[in] queue queue to be init
 * @param[in] isTx tx or rx, l_true for tx, l_false for rx
 */
static void lin_init_queue(l_ifc_handle inst,lin_tp_queue_t* queue,l_bool isTx) {
    queue->readIdx=0;
    queue->writeIdx=0;
    l_irqmask irq=l_sys_irq_disable();
    if(isTx==l_true) {
        g_linGlobalState[inst].tpState->txStatus=LD_QUEUE_EMPTY;
        queue->queueSize=g_linGlobalConfig[inst]->tpCfg->txQueueSize;
    } else {
        g_linGlobalState[inst].tpState->rxStatus=LD_NO_DATA;
        queue->queueSize=g_linGlobalConfig[inst]->tpCfg->rxQueueSize;
    }
    l_sys_irq_restore(irq);
}

/**
 * @brief get current queue left size
 * @param[in] inst ifc handle
 * @param[in] isTx tx or rx, l_true for tx, l_false for rx
 * @return l_u8
 */
static l_u8 lin_get_queue_size(l_ifc_handle inst,l_bool isTx) {
    l_irqmask irq=l_sys_irq_disable();
    lin_core_state_t const* state=&g_linGlobalState[inst];
    l_u8 size;
    if(isTx==l_true) {
        if(state->tpState->txStatus==LD_QUEUE_EMPTY) {
            size=state->tpState->txQueue.queueSize;
        } else {
            if(state->tpState->txQueue.writeIdx>state->tpState->txQueue.readIdx) {
                size=state->tpState->txQueue.queueSize-(state->tpState->txQueue.writeIdx-state->tpState->txQueue.readIdx);
            } else {
                size=state->tpState->txQueue.writeIdx+state->tpState->txQueue.queueSize-state->tpState->txQueue.readIdx;
            }
        }
    } else {
        if(state->tpState->rxStatus==LD_NO_DATA) {
            size=state->tpState->rxQueue.queueSize;
        } else {
            if(state->tpState->rxQueue.writeIdx>state->tpState->rxQueue.readIdx) {
                size=state->tpState->rxQueue.queueSize-(state->tpState->rxQueue.writeIdx-state->tpState->rxQueue.readIdx);
            } else {
                size=state->tpState->rxQueue.writeIdx+state->tpState->rxQueue.queueSize-state->tpState->rxQueue.readIdx;
            }
        }
    }
    l_sys_irq_restore(irq);
    return size;
}

/**
 * @brief put a data input queue
 * @param[in] inst ifc handle
 * @param[in] data data buffer, length must be 8
 * @param[in] isTx tx or rx, l_true for tx, l_false for rx
 */
static void lin_put_raw(l_ifc_handle inst,const l_u8* const  data,l_bool isTx) {
    lin_core_state_t* state=&g_linGlobalState[inst];

    l_irqmask irq=l_sys_irq_disable();
    if(isTx==l_true) {
        if(state->tpState->txStatus!=LD_TRANSMIT_ERROR&&(state->tpState->txStatus==LD_QUEUE_EMPTY||state->tpState->txQueue.readIdx!=state->tpState->txQueue.writeIdx)) {
            lin_memcpy(state->tpState->txQueue.items[state->tpState->txQueue.writeIdx].data,data,8);

            state->tpState->txQueue.writeIdx++;
            if(state->tpState->txQueue.writeIdx>=state->tpState->txQueue.queueSize) {
                state->tpState->txQueue.writeIdx=0;
            }
            if(state->tpState->txQueue.writeIdx==state->tpState->txQueue.readIdx) {
                state->tpState->txStatus=LD_QUEUE_FULL;
            } else {
                state->tpState->txStatus=LD_QUEUE_AVAILABLE;
            }
        }
    } else {
        if(state->tpState->rxStatus!=LD_RECEIVE_ERROR&&(state->tpState->rxStatus==LD_NO_DATA||state->tpState->rxQueue.readIdx!=state->tpState->rxQueue.writeIdx)) {
            lin_memcpy(state->tpState->rxQueue.items[state->tpState->rxQueue.writeIdx].data,data,8);
            state->tpState->rxQueue.writeIdx++;
            if(state->tpState->rxQueue.writeIdx>=state->tpState->rxQueue.queueSize) {
                state->tpState->rxQueue.writeIdx=0;
            }
            state->tpState->rxStatus=LD_DATA_AVAILABLE;
        }
    }
    l_sys_irq_restore(irq);
}

/**
 * @brief put a func frame input queue, which will put the frame to the head of queue
 * @note if the queue is full, the frame will be dropped
 * @note if tx queue has a function address, we can't enqueue func address again, need wait the func frame has been sent
 * @param[in] inst ifc handle
 * @param[in] data data buffer, length must be 8
 * @param[in] isTx tx or rx ,l_true for tx, l_false for rx
 */
static void lin_put_func(l_ifc_handle inst,const l_u8* const  data,l_bool isTx) {
    (void)isTx;
    lin_core_state_t* state=&g_linGlobalState[inst];

    l_irqmask irq=l_sys_irq_disable();
    if(state->tpState->txStatus!=LD_TRANSMIT_ERROR&&(state->tpState->txStatus==LD_QUEUE_EMPTY||state->tpState->txQueue.readIdx!=state->tpState->txQueue.writeIdx)) {

        if(g_linGlobalState[inst].tpState->txQueue.items[state->tpState->txQueue.readIdx].data[0]!=LD_FUNCTIONAL_NAD) {

            if(state->tpState->txQueue.readIdx==0) {
                state->tpState->txQueue.readIdx=state->tpState->txQueue.queueSize-1;
            } else {
                state->tpState->txQueue.readIdx--;
            }

            lin_memcpy(state->tpState->txQueue.items[state->tpState->txQueue.readIdx].data,data,8);
            if(state->tpState->txQueue.writeIdx==state->tpState->txQueue.readIdx) {
                state->tpState->txStatus=LD_QUEUE_FULL;
            } else {
                state->tpState->txStatus=LD_QUEUE_AVAILABLE;
            }
        } else {
            /* NOTE: if tx queue has a function address, we can't enqueue func address again, need wait the func frame has been sent */
        }
    }
    l_sys_irq_restore(irq);
}
/**
 * @brief peek a frame from queue, get the frame but not remove it from queue
 * @note you should use the return pointer immediately, because the pointer will be invalid after next call
 * @param[in] inst ifc handle
 * @param[in] isTx tx or rx, l_true for tx, l_false for rx
 * @return l_u8* pointer to frame data, NULL if queue is empty
 */
static  l_u8* lin_peak_raw(l_ifc_handle inst,l_bool isTx) {
    lin_core_state_t* state=&g_linGlobalState[inst];
    l_u8* pdata=NULL;
    l_irqmask irq=l_sys_irq_disable();
    if(isTx==l_true) {
        if(state->tpState->txStatus!=LD_QUEUE_EMPTY&&state->tpState->txStatus!=LD_TRANSMIT_ERROR) {
            pdata=state->tpState->txQueue.items[state->tpState->txQueue.readIdx].data;
        }
    } else {
        if(state->tpState->rxStatus!=LD_NO_DATA&&state->tpState->rxStatus!=LD_RECEIVE_ERROR) {
            pdata=state->tpState->rxQueue.items[state->tpState->rxQueue.readIdx].data;
        }
    }

    l_sys_irq_restore(irq);
    return pdata;
}
/**
 * @brief get the data from the queue
 * @param[in] inst ifc handle
 * @param[out] data data buffer, length must be 8
 * @param[in] isTx tx or rx, l_true for tx, l_false for rx
 */
static void lin_get_raw(l_ifc_handle inst,l_u8* const  data,l_bool isTx) {
    lin_core_state_t* state=&g_linGlobalState[inst];
    l_irqmask irq=l_sys_irq_disable();
    if(isTx==l_true) {
        if(state->tpState->txStatus!=LD_QUEUE_EMPTY&&state->tpState->txStatus!=LD_TRANSMIT_ERROR) {
            lin_memcpy(data,state->tpState->txQueue.items[state->tpState->txQueue.readIdx].data,8);
            state->tpState->txQueue.readIdx++;
            if(state->tpState->txQueue.readIdx>=state->tpState->txQueue.queueSize) {
                state->tpState->txQueue.readIdx=0;
            }
            if(state->tpState->txQueue.writeIdx==state->tpState->txQueue.readIdx) {
                state->tpState->txStatus=LD_QUEUE_EMPTY;
            } else {
                state->tpState->txStatus=LD_QUEUE_AVAILABLE;
            }
        }
    } else {
        if(state->tpState->rxStatus!=LD_NO_DATA&&state->tpState->rxStatus!=LD_RECEIVE_ERROR) {
            lin_memcpy(data,state->tpState->rxQueue.items[state->tpState->rxQueue.readIdx].data,8);
            state->tpState->rxQueue.readIdx++;
            if(state->tpState->rxQueue.readIdx>=state->tpState->rxQueue.queueSize) {
                state->tpState->rxQueue.readIdx=0;
            }
            if(state->tpState->rxQueue.writeIdx==state->tpState->rxQueue.readIdx) {
                state->tpState->rxStatus=LD_NO_DATA;
            } else {
                state->tpState->rxStatus=LD_DATA_AVAILABLE;
            }
        }
    }
    l_sys_irq_restore(irq);
}

/**
 * @brief check the tx queue first frame is a func frame
 * @param[in] inst ifc handle
 * @param[in] isTx only l_true is valid
 * @return l_bool if the first frame is a func frame,the return value is l_true,else is l_false
 */
static l_bool lin_is_func(l_ifc_handle inst,l_bool isTx) {
    l_u8 readIdx;
    l_u8 isFunc=l_false;
    if(isTx==l_true) {
        readIdx=g_linGlobalState[inst].tpState->txQueue.readIdx;
        if(g_linGlobalState[inst].tpState->txQueue.items[readIdx].data[0]==LD_FUNCTIONAL_NAD) {
            isFunc=l_true;
        }
    } else {
        /* do nothing */
    }
    return isFunc;
}



void ld_put_raw (l_ifc_handle inst, const l_u8* const  data) {

    lin_core_state_t* state=&g_linGlobalState[inst];

    if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_SLAVE) {
        state->tpState->timeout=g_linGlobalConfig[inst]->slaveCfg->nAsTimeout * 1000;
        state->tpState->timeoutDir=LIN_TP_TIMEOUT_TX;
    }

    if(data[0]==LD_FUNCTIONAL_NAD) {
        lin_put_func(inst,data,l_true);
    } else {
        lin_put_raw(inst,data,l_true);
    }

}




void ld_get_raw (l_ifc_handle inst, l_u8* const  data) {
    lin_get_raw(inst,data,l_false);
}








void ld_init(l_ifc_handle inst) {

    if(g_linGlobalState[inst].tpState) {
        lin_init_queue(inst,&g_linGlobalState[inst].tpState->txQueue, l_true);
        lin_init_queue(inst,&g_linGlobalState[inst].tpState->rxQueue, l_false);
        g_linGlobalState[inst].tpState->diagState=LIN_DIAG_IDLE;
        g_linGlobalState[inst].tpState->txMsgStatus=LD_COMPLETED;
        g_linGlobalState[inst].tpState->rxMsgStatus=LD_COMPLETED;
        g_linGlobalState[inst].tpState->timeoutDir=LIN_TP_TIMEOUT_IDLE;
        g_linGlobalState[inst].tpState->serviceStatus=LD_SERVICE_IDLE;
        g_linGlobalState[inst].tpState->ffReceived=l_false;
        g_linGlobalState[inst].tpState->hasRecvMsg=l_false;
        if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER) {
            g_linGlobalState[inst].masterState->masterTpState->diagOnly=l_false;
            g_linGlobalState[inst].tpState->ownNodeNad=0;
        } else {
            g_linGlobalState[inst].slaveState->nadConfigured=l_false;
            g_linGlobalState[inst].tpState->ownNodeNad=g_linGlobalConfig[inst]->slaveCfg->initNad;
        }
        
        g_linGlobalState[inst].tpInit=true;
    }

}

void lin_tp_sch_handle(l_ifc_handle inst) {
    lin_core_state_t* state=&g_linGlobalState[inst];
    if(state->tpInit==l_true) {
        switch (state->tpState->diagState) {
        case LIN_DIAG_IDLE:
            if(state->tpState->txStatus!=LD_QUEUE_EMPTY) {
                if(lin_is_func(inst,l_true)==l_true) {
                    /* 11.Idle state -> Tx functional active state */
                    state->tpState->diagState=LIN_DIAG_TX_FUNC;
                } else {
                    /* 1. Idle state -> Tx physical active state */
                    state->tpState->diagState=LIN_DIAG_TX_PHY;
                }
                /* switch to masterReqTable*/
                l_sch_set(inst,g_linGlobalConfig[inst]->masterCfg->masterReqSchIndex,0);
            }
            break;
        case LIN_DIAG_TX_PHY:
            if(lin_is_func(inst,l_true)==l_true) {
                /* 4.Tx physical active state -> Interleaved functional during Tx physical state */
                state->tpState->diagState=LIN_DIAG_TX_TMP_FUNC;
            }
            l_sch_set(inst,g_linGlobalConfig[inst]->masterCfg->masterReqSchIndex,0);
            break;
        case LIN_DIAG_RX_PHY:
            if(state->tpState->txStatus!=LD_QUEUE_EMPTY&&lin_is_func(inst,l_true)==l_true) {
                /*8.Rx physical active state -> Interleaved functional during Rx physical state */
                l_sch_set(inst,g_linGlobalConfig[inst]->masterCfg->masterReqSchIndex,0);
                state->tpState->diagState=LIN_DIAG_RX_TMP_FUNC;
            } else {
                /* 7.Rx physical active state->Rx physical active state */
                l_sch_set(inst,g_linGlobalConfig[inst]->masterCfg->slaveRespSchIndex,0);
            }
            break;
        case LIN_DIAG_TX_FUNC:
        /* fall-through */
        case LIN_DIAG_TX_TMP_FUNC:
        /* fall-through */
        case LIN_DIAG_RX_TMP_FUNC:
            l_sch_set(inst,g_linGlobalConfig[inst]->masterCfg->masterReqSchIndex,0);
            break;
        default:
            /*do noting */
            break;
        }
    }
}




void lin_tp_timeout_handle(l_ifc_handle inst,int usedMs) {
#if (defined LIN_HAS_MASTER) || (defined LIN_HAS_SLAVE)
    lin_core_state_t* state=&g_linGlobalState[inst];
#endif
#ifdef LIN_HAS_SLAVE
    if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_SLAVE) {
        if(state->tpState->timeoutDir==LIN_TP_TIMEOUT_TX) {
            state->tpState->timeout-=usedMs;
            if(state->tpState->timeout<0) {
                /* reset tx queue */
                lin_init_queue(inst, &state->tpState->txQueue, l_true);
                state->tpState->diagState=LIN_DIAG_IDLE;
                state->tpState->timeoutDir=LIN_TP_TIMEOUT_IDLE;
                if(state->tpState->txMsgStatus  == LD_IN_PROGRESS){
                    state->tpState->txMsgStatus=LD_N_AS_TIMEOUT;
                }
            }
        } else if(state->tpState->timeoutDir==LIN_TP_TIMEOUT_RX) {
            state->tpState->timeout-=usedMs;
            if(state->tpState->timeout<0) {
                /* slave should ignore the diag request */
                state->tpState->diagState=LIN_DIAG_IDLE;
                state->tpState->ffReceived=l_false;
                state->tpState->timeoutDir=LIN_TP_TIMEOUT_IDLE;
                state->tpState->hasRecvMsg=l_false;
                if(state->tpState->rxMsgStatus  == LD_IN_PROGRESS){
                    state->tpState->rxMsgStatus=LD_N_CR_TIMEOUT;
                }
            }
        } else {
            /* do nothing */
        }
    }
    else
#endif
    {
#ifdef LIN_HAS_MASTER
        if(state->tpState->timeoutDir==LIN_TP_TIMEOUT_SCH) {
            state->tpState->timeout-=usedMs;
            if(state->tpState->timeout<0) {
                /* reset rx queue */
                lin_init_queue(inst, &state->tpState->rxQueue, l_false);

                state->tpState->diagState=LIN_DIAG_IDLE;
                state->tpState->timeoutDir=LIN_TP_TIMEOUT_IDLE;
                state->tpState->ffReceived=l_false;



                if(g_linGlobalSchTable[state->masterState->activeSch]->schType==LIN_SCH_TYPE_SLAVE_RESPONSE) {
                    lin_sch_back(inst);
                }
            }
        }
#endif
    }

}

static void lin_tp_tx_nrc78_handle(l_ifc_handle inst)
{
    lin_core_state_t* state=&g_linGlobalState[inst];

    switch (state->tpState->diagState) {
    case LIN_DIAG_TX_FUNC:
        /* 12.Tx functional active state -> Idle state */
        state->tpState->diagState=LIN_DIAG_IDLE;
        break;
    case LIN_DIAG_TX_TMP_FUNC:
        state->tpState->diagState=LIN_DIAG_TX_PHY;
        break;

    case LIN_DIAG_RX_TMP_FUNC:
        /* 9.Interleaved functional during Rx physical state->Rx physical active state */
        state->tpState->diagState=LIN_DIAG_RX_PHY;
        break;
    case LIN_DIAG_TX_PHY:
        /* When the slave responds with 78 nrc, it remains in state LIN_DIAG_TX_PHY, regardless of whether there is data in the queue*/
        state->tpState->diagState=LIN_DIAG_TX_PHY;
        if(state->tpState->txStatus==LD_QUEUE_EMPTY) {
            if(state->tpState->txMsgStatus==LD_IN_PROGRESS) {
                state->tpState->txMsgStatus=LD_COMPLETED;
            }
            /* clear timeout dir */
            state->tpState->timeoutDir=LIN_TP_TIMEOUT_IDLE;
            state->tpState->diagState=LIN_DIAG_TX_PHY;
        } else {
            state->tpState->timeout=g_linGlobalConfig[inst]->slaveCfg->nAsTimeout * 1000;
            state->tpState->timeoutDir=LIN_TP_TIMEOUT_TX;

        }
        break;
    default:
        break;
    }
}


static void lin_tp_tx_handle(l_ifc_handle inst)
{
    lin_core_state_t* state=&g_linGlobalState[inst];


    switch (state->tpState->diagState) {
    case LIN_DIAG_TX_FUNC:
        /* 12.Tx functional active state -> Idle state */
        state->tpState->diagState=LIN_DIAG_IDLE;
        break;
    case LIN_DIAG_TX_TMP_FUNC:
        state->tpState->diagState=LIN_DIAG_TX_PHY;
        break;

    case LIN_DIAG_RX_TMP_FUNC:
        /* 9.Interleaved functional during Rx physical state->Rx physical active state */
        state->tpState->diagState=LIN_DIAG_RX_PHY;
        break;
    case LIN_DIAG_TX_PHY:
        if(state->tpState->txStatus==LD_QUEUE_EMPTY) {
            if(state->tpState->txMsgStatus==LD_IN_PROGRESS) {
                state->tpState->txMsgStatus=LD_COMPLETED;
            }

            if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER) {
                /* master switch to RX */
                state->tpState->diagState=LIN_DIAG_RX_PHY;
                /* set timeout equal sch timeout */
                state->tpState->timeout=g_linGlobalConfig[inst]->masterCfg->schTimeout * 1000;
                state->tpState->timeoutDir=LIN_TP_TIMEOUT_SCH;
            } else {
                /* slave switch to IDLE */
                state->tpState->diagState=LIN_DIAG_IDLE;
                /* clear timeout dir */
                state->tpState->timeoutDir=LIN_TP_TIMEOUT_IDLE;
            }
        } else {
            /* still have frame in queue */
            if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_SLAVE) {

                state->tpState->timeout=g_linGlobalConfig[inst]->slaveCfg->nAsTimeout * 1000;
                state->tpState->timeoutDir=LIN_TP_TIMEOUT_TX;
            }
        }
        break;
    default:
        break;
    }
}


static void lin_tp_tx_error_handle(l_ifc_handle inst)
{
    lin_core_state_t* state=&g_linGlobalState[inst];
    switch (state->tpState->diagState) {
    case LIN_DIAG_TX_FUNC:
        /* 12.Tx functional active state -> Idle state */
        state->tpState->diagState=LIN_DIAG_IDLE;
        break;
    case LIN_DIAG_TX_TMP_FUNC:
        state->tpState->diagState=LIN_DIAG_TX_PHY;
        break;

    case LIN_DIAG_RX_TMP_FUNC:
        /* 9.Interleaved functional during Rx physical state->Rx physical active state */
        state->tpState->diagState=LIN_DIAG_RX_PHY;
        break;
    case LIN_DIAG_TX_PHY:
        /* PHY frame failed ,clear queue */
        lin_init_queue(inst, &state->tpState->txQueue,l_true);
        state->tpState->diagState=LIN_DIAG_IDLE;
        break;
    default:
        break;
    }
}
static void lin_tp_rx_error_handle(l_ifc_handle inst) {
    lin_core_state_t* state=&g_linGlobalState[inst];
    if(state->tpState->diagState==LIN_DIAG_RX_PHY) {
        /* PHY frame failed*/
        state->tpState->diagState=LIN_DIAG_IDLE;
    }
}
static void lin_make_slave_response_pdu(l_ifc_handle inst,
                                        l_u8 sid,
                                        l_u8 res_type,
                                        l_u8 error_code)
{

    lin_core_state_t* state=&g_linGlobalState[inst];
    l_u8 data[8];
    const lin_product_id_t * ident;
    const lin_serial_number_t * serial_number;


    /* A slave node shall remember the response for a request until a new request with a NAD */
    state->tpState->lastRSID=(RES_POSITIVE+sid);
    state->tpState->lastCode=error_code;


    /* Pack data to response PDU */
    data[0] = state->tpState->ownNodeNad;
    data[1] = 0x03U;        /* PCI */
    data[2] = LIN_RES_NEGATIVE; /* SID */
    data[3] = sid;          /* D0 */
    data[4] = error_code;   /* D1 */
    data[5] = 0xFFU;        /* D2 */
    data[6] = 0xFFU;        /* D3 */
    data[7] = 0xFFU;        /* D4 */

    switch (sid)
    {
    case LIN_SERVICE_READ_BY_IDENTIFY:
        if (LIN_POSITIVE == res_type)
        {
            /* PCI type */
            data[1] = LIN_PCI_RES_READ_BY_IDENTIFY;
            /* SID */
            data[2] = (l_u8)(RES_POSITIVE + sid);

            if (error_code == LIN_PRODUCT_ID)
            {
                /* Get Identifier info */
                ident = (&g_linGlobalConfig[inst]->slaveCfg->product_id);
                data[3] = (l_u8)(ident->supplierId & 0xFFU);
                data[4] = (l_u8)(ident->supplierId >> 8);
                data[5] = (l_u8)(ident->functionId & 0xFFU);
                data[6] = (l_u8)(ident->functionId >> 8);
                data[7] = ident->variant;
            }
            else if (error_code == LIN_SERIAL_NUMBER)
            {
                serial_number = (&g_linGlobalConfig[inst]->slaveCfg->sn);
                data[3] = serial_number->serial0;
                data[4] = serial_number->serial1;
                data[5] = serial_number->serial2;
                data[6] = serial_number->serial3;
                /* PCI for Serial Number is 0x05 */
                data[1] = 0x05U;
            }
            else
            {
                l_u8 data_callout[5] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
                // (void)ld_read_by_id_callout(iii, error_code, data_callout);
                /* packing user defined pdu */
                data[3] = data_callout[0];
                data[4] = data_callout[1];
                data[5] = data_callout[2];
                data[6] = data_callout[3];
                data[7] = data_callout[4];
                /* Check for data values*/
                for (int i = 5; i > 0U; i--)
                {
                    if (data_callout[i - 1U] != 0xFFU)
                    {
                        /* PCI: Data length is 1 (RSID) + all data exclude 0xFF */
                        data[1] = (l_u8)(i + 1U);
                        break;
                    }
                }
            }
        }
        break;
    case LIN_SERVICE_ASSIGN_FRAME_ID:
        data[0] = state->tpState->ownNodeNad;
        data[1] = 0x01U;        /* PCI */
        data[2] = 0xF1U;        /* SID */
        data[3] = 0xFFU;        /* D0 */
        data[4] = 0xFFU;        /* D1 */
        break;
    case LIN_SERVICE_ASSIGN_NAD:
        data[0] = g_linGlobalConfig[inst]->slaveCfg->initNad;
        /* PCI */
        data[1] = 0x01U;
        /* RSID */
        data[2] = 0xF0U;
        data[3] = 0xFFU;
        data[4] = 0xFFU;
        break;
    case LIN_SERVICE_CONDITIONAL_CHANGE_NAD:
        /* PCI */
        data[1] = 0x01U;
        /* RSID */
        data[2] = 0xF3U;
        data[3] = 0xFFU;
        data[4] = 0xFFU;
        break;

    case LIN_SERVICE_SAVE_CONFIGURATION:
        /* PCI type */
        data[1] = LIN_PCI_RES_SAVE_CONFIGURATION;
        /* SID */
        data[2] = (l_u8)(RES_POSITIVE + sid);
        /* Data unused */
        data[3] = 0xFFU;
        data[4] = 0xFFU;
        break;
    case LIN_SERVICE_ASSIGN_FRAME_ID_RANGE:  /* Mandatory for TL LIN 2.1 */
        if (LIN_POSITIVE == res_type)
        {
            data[1] = LIN_PCI_RES_ASSIGN_FRAME_ID_RANGE;
            data[2] = (l_u8)(RES_POSITIVE + sid);
            data[3] = 0xFFU;
            data[4] = 0xFFU;
        }
        break;

    default:
        /* do nothing */
        break;
    } /* end of switch statement */

    /* Put lin_tl_pdu data into transmit queue*/
    ld_put_raw(inst, data);
    state->tpState->diagState=LIN_DIAG_TX_PHY;
}


static void lin_read_by_identifier(l_ifc_handle inst,const l_u8* data)
{
    lin_core_state_t* state=&g_linGlobalState[inst];
    lin_product_id_t productId;
    l_u8 id;
    l_u16 supid;
    l_u16 fid;
    int i;


    /* Get the product identification */
    productId = g_linGlobalConfig[inst]->slaveCfg->product_id;

    /* Get supplier and function identification in request */
    supid = (l_u16)(((l_u16)(data[5])) << 8U);
    supid = (l_u16)(supid | (l_u16)(data[4]));

    fid = (l_u16)(((l_u16)(data[7])) << 8U);
    fid = (l_u16)(fid | (l_u16)(data[6]));


    /* Check Supplier ID and Function ID */
    if (((supid != productId.supplierId) && (supid != LD_ANY_SUPPLIER)) ||
            ((fid != productId.functionId) && (fid != LD_ANY_FUNCTION)))
    {
        /* unmatch, ignore */
    }
    else
    {
        /* Get the identifier of request */
        id = data[3];

        switch (id)
        {
        case LIN_PRODUCT_ID:
            lin_make_slave_response_pdu(inst, LIN_SERVICE_READ_BY_IDENTIFY, LIN_POSITIVE, id);
            break;
        case LIN_SERIAL_NUMBER:
            lin_make_slave_response_pdu(inst, LIN_SERVICE_READ_BY_IDENTIFY, LIN_POSITIVE, id);
            break;
        default:
            /* For ID from 32 to 63, call user defined ld_read_by_id_callout */
            if ((id >= LIN_READ_USR_DEF_MIN) && (id <= LIN_READ_USR_DEF_MAX))
            {
                l_u8 data_callout[5] = {0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU};
                l_u8 retval = ld_read_by_id_callout(inst, id, data_callout);
                /*If the User ID is supported, make positive response*/
                if (retval == LD_POSITIVE_RESPONSE)
                {
                    i = 0U;
                    while ((i < 5U) && (data_callout[i] == 0xFFU))
                    {
                        i++;
                    }
                    if (i < 5U)
                    {
                        lin_make_slave_response_pdu(inst, LIN_SERVICE_READ_BY_IDENTIFY, LIN_POSITIVE, id);
                    }
                    else
                    {
                        lin_make_slave_response_pdu(inst, LIN_SERVICE_READ_BY_IDENTIFY, LIN_NEGATIVE, LIN_SUBFUNCTION_NOT_SUPPORTED);
                    }
                }
                /*If the User ID is not supported, make negative response*/
                else if (retval == LD_NEGATIVE_RESPONSE)
                {
                    /* Make a negative slave response PDU */
                    lin_make_slave_response_pdu(inst, LIN_SERVICE_READ_BY_IDENTIFY, LIN_NEGATIVE, LIN_SUBFUNCTION_NOT_SUPPORTED);
                }
                else
                {
                    /*Do not answer*/
                    state->tpState->serviceStatus = LD_SERVICE_IDLE;
                }
            }
            /* For ID from 2 to 31 or 64-255, give negative response */
            else
            {
                /* Make a negative slave response PDU */
                lin_make_slave_response_pdu(inst, LIN_SERVICE_READ_BY_IDENTIFY, LIN_NEGATIVE, LIN_SUBFUNCTION_NOT_SUPPORTED);
            }

            break;
        } /* end of switch */
    }
}

static void lin_assign_nad(l_ifc_handle inst,const l_u8* data)
{

    l_u16 supid;
    l_u16 fid;
    lin_core_state_t* state=&g_linGlobalState[inst];
    lin_product_id_t product_id = g_linGlobalConfig[inst]->slaveCfg->product_id;



    /* Get supplier and function identification in request */
    supid = (l_u16)(((l_u16)data[4]) << 8U);
    supid = (l_u16)(supid | ((l_u16)data[3]));

    fid = (l_u16)(((l_u16)data[6]) << 8U);
    fid = (l_u16)(fid | ((l_u16)data[5]));

    /* Check Supplier ID and Function ID */
    if (((supid != product_id.supplierId) && (supid != LD_ANY_SUPPLIER)) ||
            ((fid != product_id.functionId) && (fid != LD_ANY_FUNCTION)))
    {
        /* do nothing*/
    }
    else
    {
        /* change nad */
        state->tpState->ownNodeNad = data[7];
        g_linGlobalConfig[inst]->tpCfg->idList[0]=data[7];
        state->slaveState->nadConfigured=l_true;
        lin_make_slave_response_pdu(inst, LIN_SERVICE_ASSIGN_NAD, LIN_POSITIVE, 0U);
    }
}


static void lin_assign_frame_id_range(l_ifc_handle inst,const l_u8* data)
{
    l_u8 start_index;

    /* Get start index in request */
    start_index = data[3];

    /* Find the number of configurable frames plus 2, including 0x3C and 0x3D */

    if((start_index+1)<g_linGlobalConfig[inst]->tpCfg->idNum) {
        for (uint32_t i = 4U; i < 8U; i++)
        {
            switch (data[i])
            {
            case 0x00U:
                /* Unassign frame */
                start_index++;
                g_linGlobalConfig[inst]->tpCfg->idList[start_index+1] = 0xFFU;
                break;
            case 0xFFU:
                /* keep the previous assigned value of this frame */
                break;
            default:
                /* Calculate frame ID and Assign ID to frame */
                start_index++;
                g_linGlobalConfig[inst]->tpCfg->idList[start_index+1] = data[i]&0x3FU;
                break;
            }
        } /* End of for statement */
        lin_make_slave_response_pdu(inst, LIN_SERVICE_ASSIGN_FRAME_ID_RANGE, LIN_POSITIVE, 0U);
    } else {
        lin_make_slave_response_pdu(inst, LIN_SERVICE_ASSIGN_FRAME_ID_RANGE, LIN_NEGATIVE, LIN_GENERAL_REJECT);
    }
}


static void lin_conditional_change_nad(l_ifc_handle inst,const l_u8* data)
{
    l_u8 id;
    l_u8 byte;
    l_u8 mask;
    l_u8 invert;
    l_bool give_positive_flg = (bool)0U;
    lin_core_state_t* state=&g_linGlobalState[inst];

    lin_product_id_t product_id = g_linGlobalConfig[inst]->slaveCfg->product_id;
    lin_serial_number_t serial_number = g_linGlobalConfig[inst]->slaveCfg->sn;

    /* Get receive queue */

    id = data[3];
    byte = data[4];
    mask = data[5];
    invert = data[6];

    switch (id)
    {
    case LIN_PRODUCT_ID:
        if ((byte > 0U) && (byte < 6U))
        {
            /* Byte 1: Supplier ID LSB; Byte 2: Supplier ID MSB */
            if ((byte > 0U) && (byte < 3U))
            {
                byte = (l_u8)(product_id.supplierId >> ((byte - 1U) * 8U));
            }
            /* Byte 3: Function ID LSB; Byte 4: Function ID MSB */
            else if ((byte > 2U) && (byte < 5U))
            {
                byte = (l_u8)(product_id.functionId >> ((byte - 3U) * 8U));
            }
            /* Byte 5: Variant */
            else
            {
                byte = product_id.variant;
            }

            /* Do a bitwise XOR with Invert and Do a bitwise AND with Mask */
            byte = (l_u8)((byte ^ invert) & mask);
            if (byte == 0U)
            {
                give_positive_flg = (l_bool)1U;
            }
        }

        break;
    case LIN_SERIAL_NUMBER:
        if ((byte > 0U) && (byte < 5U))
        {
            switch (byte)
            {
            case 1U:
                byte = serial_number.serial0;
                break;
            case 2U:
                byte = serial_number.serial1;
                break;
            case 3U:
                byte = serial_number.serial2;
                break;
            case 4U:
                byte = serial_number.serial3;
                break;
            default:
                /* Do nothing */
                break;
            }

            /* Do a bitwise XOR with Invert and Do a bitwise AND with Mask */
            byte = (l_u8)((byte ^ invert) & mask);
            if (byte == 0U)
            {
                give_positive_flg = (l_bool)1U;
            }
        }

        break;
    default:
        /* Do nothing */
        break;
    }

    if (give_positive_flg == (bool)1U)
    {
        /* Make response PDU before change configuration NAD */
        lin_make_slave_response_pdu(inst, LIN_SERVICE_CONDITIONAL_CHANGE_NAD, LIN_POSITIVE, 0U);
        /* change nad */
        state->tpState->ownNodeNad = data[7];
        g_linGlobalConfig[inst]->tpCfg->idList[0]=data[7];
    }
}

static void lin_check_response(l_ifc_handle inst) {

    l_u8 frameType;
    l_u8 sci=0;
    lin_core_state_t* state=&g_linGlobalState[inst];
    l_u8 const* data=lin_peak_raw(inst,l_false);
    if(data) {

        frameType = (l_u8)((data[1] & 0xF0U) >> 4U);
        if (frameType == PCI_SF)
        {
            sci = data[2];
        }
        else
        {
            if (frameType == PCI_FF)
            {
                sci = data[3];
            }
        }
        if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER) {
            state->tpState->serviceStatus=LD_SERVICE_IDLE;
            state->tpState->lastRSID = sci;
            if(sci==LIN_RES_NEGATIVE) {
                state->tpState->lastCode=data[4];
            }
        } else {
            /* slave build in response */
            switch (sci) {
            case LIN_SERVICE_READ_BY_IDENTIFY:
                lin_read_by_identifier(inst,data);
                break;
            case LIN_SERVICE_ASSIGN_NAD:
                lin_assign_nad(inst,data);
                break;
            case LIN_SERVICE_ASSIGN_FRAME_ID_RANGE:
                lin_assign_frame_id_range(inst,data);
                break;
            case LIN_SERVICE_SAVE_CONFIGURATION:
                state->frameStatus.saveConfig=l_true;
                lin_make_slave_response_pdu(inst, LIN_SERVICE_SAVE_CONFIGURATION, LIN_POSITIVE, 0U);
                break;
            case LIN_SERVICE_CONDITIONAL_CHANGE_NAD:
                lin_conditional_change_nad(inst,data);
                break;
            default:
                /* user self decide */
                if((sci&0xB0)==0xB0){
                    lin_make_slave_response_pdu(inst, sci, LIN_NEGATIVE, LIN_SERVICE_NOT_SUPPORTED);
                }
               
                
                break;
            }
        }
    }
}

static void lin_receive_message(l_ifc_handle inst)
{
    lin_core_state_t* state=&g_linGlobalState[inst];
    l_u8 pdu[8];
    l_u16 i;
    l_u16 data_index = 0U;
    l_u16 tmp_length = 0U;
    l_u8 PCI_type;
    l_u8* data = state->tpState->recvData;
    l_u8* NAD = state->tpState->recvNad;
    l_u16* length = state->tpState->recvLen;

    if (LD_NO_DATA != state->tpState->rxStatus)
    {
        /* Message is received completely */
        /* get data from receive queue */
        /*
        * @violates MISRA 2012 Required Rule 1.3 Required
        */
        ld_get_raw(inst, pdu);
        /* Analyze data */
        if (g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER)
        {
            *NAD = pdu[0];
        }

        /* Check type of pdu */
        PCI_type = (l_u8)((pdu[1] & 0xF0U) >> 4U);
        switch (PCI_type)
        {
        /* Single frame */
        case PCI_SF:
            tmp_length = (l_u16)((l_u16)pdu[1] & 0x000FU);
            i = *length;
            *length = tmp_length;
            if (i < tmp_length)
            {
                tmp_length = i;
            }

            data[0] = pdu[2];
            for (i = 1U; i < tmp_length; i++)
            {
                data[i] = pdu[i + 2U];
            }
            break;
        /* Multi frame */
        case PCI_FF:
            /* First frame */
            tmp_length = (l_u16)((((l_u16)pdu[1] & 0x000FU) << 8) + (l_u16)pdu[2]);
            i = *length;
            *length = tmp_length;
            if (i < tmp_length)
            {
                tmp_length = i;
            }
            data[0] = pdu[3];
            for (i = 1U; i < 5U; i++)
            {
                data[i] = pdu[i + 3U];
            }

            tmp_length -= 5U;
            data_index += 5U;

            /* Consecutive frame */
            while (tmp_length > 6U)
            {
                /* get PDU */
                ld_get_raw(inst, pdu);
                for (i = 2U; i < 8U; i++)
                {
                    data[data_index] = pdu[i];
                    data_index++;
                }

                tmp_length -= 6U;
            }

            /* Last frame */
            if (tmp_length > 0U)
            {
                /* get PDU */
                /*
                * @violates MISRA 2012 Required Rule 1.3 Required
                */
                ld_get_raw(inst, pdu);
                for (i = 0U; i < tmp_length; i++)
                {
                    data[data_index] = pdu[i + 2U];
                    data_index++;
                }
            }
            break;
        default:
            /* do nothing */
            break;
        } /* end of switch */
        state->tpState->hasRecvMsg = l_false;
        state->tpState->rxMsgStatus = LD_COMPLETED;
    }
}

static void lin_rx_single_frame(l_ifc_handle inst,l_u8 const* data)
{
    l_u8 length;
    lin_core_state_t* state=&g_linGlobalState[inst];

    length = (l_u8)(data[1] & 0x0FU);
    /* check length of SF. If not valid, ignore this PDU */
    if (length <= 6U)
    {
        if(state->tpState->diagState == LIN_DIAG_RX_PHY && data[0]==LD_FUNCTIONAL_NAD)
        {
            /* The slave node shall ignore any interleaved functional addressed transmission from the master node. */
            /* drop the frame */
        } else {

            if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER) {
                /* When the master receives a uds nrc of 78, the reception is not complete, and it continues to send 3D for the slave to respond */
                if((data[2] == 0x7F) && (data[4] == 0x78)) {
                    /* refresh timeout */
                    state->tpState->timeout=g_linGlobalConfig[inst]->masterCfg->schTimeout * 1000;
                } else {
                    /* set status is IDLE to receive new message */
                    state->tpState->diagState=LIN_DIAG_IDLE;
                    state->tpState->serviceStatus=LD_SERVICE_IDLE;
                }
            } else {
                /* Functional addressed messages can only be SF. */
                if(data[0]==LD_FUNCTIONAL_NAD) {
                    state->tpState->diagState=LIN_DIAG_RX_FUNC;
                } else {
                    state->tpState->diagState=LIN_DIAG_TX_PHY;
                }
            }
            if(!((g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER)&&(data[2]==0x7F) && (data[4]==0x78)))
            {
                state->tpState->timeoutDir=LIN_TP_TIMEOUT_IDLE;
            }
            /* reset queue header and queue tail */
            lin_init_queue(inst, &state->tpState->rxQueue, l_false);
            /* put to rx queue */
            lin_put_raw(inst, data,l_false);
            /* check command*/
            lin_check_response(inst);

            /* If ld_receive_message() function is called before SF pdu to be sent */
            if(state->tpState->hasRecvMsg==l_true)
            {
                lin_receive_message(inst);
            }
            state->tpState->ffReceived=l_false;
        }




    }
}

static void lin_rx_first_frame(l_ifc_handle inst,l_u8 const* data)
{
    l_u16 length;
    lin_core_state_t* state=&g_linGlobalState[inst];

    /* for both MASTER and SLAVE */
    length = (l_u16)((((l_u16)data[1] & 0x0FU) << 8U) + (l_u16)data[2]);
    /* check length of FF. If not valid, ignore this PDU */
    if ((length >= 7U) && length <= g_linGlobalConfig[inst]->tpCfg->rxMaxFrameLength)
    {
        /* put PDU to rx queue */
        /* reset queue header and queue tail */
        lin_init_queue(inst, &state->tpState->rxQueue, l_false);
        lin_put_raw(inst, data, l_false);

        /* calculate number of PDU for this message */
        if (((length - 5U) % 6U) == 0U)
        {
            state->tpState->pendRecvCnt = (l_u8)((length - 5U) / 6U);
        }
        else
        {
            state->tpState->pendRecvCnt = (l_u8)(((length - 5U) / 6U) + 1U);
        }

        /* set frame counter = 1 */
        state->tpState->frameCnt = 1U;
        state->tpState->diagState = LIN_DIAG_RX_PHY;

        /* refresh timeout */
        if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_SLAVE) {
            state->tpState->timeoutDir=LIN_TP_TIMEOUT_RX;
            state->tpState->timeout=g_linGlobalConfig[inst]->slaveCfg->nCrTimeout * 1000;
        } else {
            state->tpState->timeoutDir=LIN_TP_TIMEOUT_SCH;
            state->tpState->timeout=g_linGlobalConfig[inst]->masterCfg->schTimeout * 1000;
        }
        state->tpState->ffReceived=l_true;
    }
}




static void lin_rx_consecutive_frame(l_ifc_handle inst,l_u8 const* data)
{
    l_u8 frameCnt;
    lin_core_state_t* state=&g_linGlobalState[inst];

    if(state->tpState->diagState == LIN_DIAG_RX_PHY) {
        /* get frame counter of this PDU */
        frameCnt = (l_u8)(data[1] & 0x0FU);

        /* Check valid frame counter */
        if (frameCnt == state->tpState->frameCnt)
        {
            /* increase frame counter */
            state->tpState->frameCnt++;
            if (state->tpState->frameCnt > 15U)
            {
                state->tpState->frameCnt = 0U;
            }

            /* decrease number of PDU to check message is complete */
            state->tpState->pendRecvCnt--;
            /* put PDU to rx queue */
            lin_put_raw(inst, data, l_false);

            if (0U == state->tpState->pendRecvCnt)
            {
                if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER) {
                    /* set status is IDLE to receive new message */
                    state->tpState->diagState=LIN_DIAG_IDLE;
                    state->tpState->serviceStatus=LD_SERVICE_IDLE;
                } else {
                    state->tpState->diagState=LIN_DIAG_TX_PHY;
                }
                state->tpState->timeoutDir=LIN_TP_TIMEOUT_IDLE;

                /* check command*/
                lin_check_response(inst);


                /* If ld_receive_message() function is called before FF pdu to be sent */
                if(state->tpState->hasRecvMsg)
                {
                    lin_receive_message(inst);
                }
                state->tpState->ffReceived=l_false;

            } else {
                /* still need wait CF frame, refresh timeout*/
                if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_SLAVE) {
                    state->tpState->timeoutDir=LIN_TP_TIMEOUT_RX;
                    state->tpState->timeout=g_linGlobalConfig[inst]->slaveCfg->nCrTimeout * 1000;
                } else {
                    state->tpState->timeoutDir=LIN_TP_TIMEOUT_SCH;
                    state->tpState->timeout=g_linGlobalConfig[inst]->masterCfg->schTimeout * 1000;
                }
            }
        }
        else
        {
            /* abort this message */
            if(state->tpState->hasRecvMsg)
            {
                state->tpState->hasRecvMsg=l_false;
            }
            state->tpState->rxMsgStatus = LD_WRONG_SN;
            state->tpState->diagState = LIN_DIAG_IDLE;
            state->tpState->timeoutDir=LIN_TP_TIMEOUT_IDLE;
            state->tpState->ffReceived=l_false;
        }


    }

}

static void lin_tp_rx_handle(l_ifc_handle inst)
{
    lin_core_state_t* state=&g_linGlobalState[inst];
#ifdef LIN_USE_UART
    l_u8 const* const data=state->dataBuffer;
#else
    l_u8 const* const data=state->frameBuf.data;
#endif
    volatile l_u8 nad;

    do {

        /* master node can't receive function node */
        if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER) {
            if(data[0]==LD_FUNCTIONAL_NAD) {
                break;
            }
            /* error nad */
            if(data[0]!=state->tpState->ownNodeNad) {
                break;
            }

        } else {
            /* slave node nad handle*/
            /*  A master request frame has been received with the NAD matching the slave node's own NAD.*/
            if(data[0]==LD_BROADCAST) {
                /* If this will happen the slave node will process requests with broadcast NAD (0x7F) in the same way as if it is the slave node’s own NAD. */
                nad=data[0];
            } else {
                nad=state->tpState->ownNodeNad;
            }
            /* slave nad unmatched check,*/
            if(nad!=data[0]) {
                /* assign nad frame check*/
                if(data[0]!=g_linGlobalConfig[inst]->slaveCfg->initNad||data[2]!=LIN_SERVICE_ASSIGN_NAD) {
                    state->tpState->diagState=LIN_DIAG_IDLE;
                    break;
                }
            }
        }

        l_u8 pciType=(data[1]&0xF0)>>4;
        switch (pciType) {
        case PCI_SF:
            lin_rx_single_frame(inst,data);
            break;
        case PCI_FF:
            lin_rx_first_frame(inst,data);
            break;
        case PCI_CF:
            lin_rx_consecutive_frame(inst,data);
            break;
        default:
            break;
        }

    } while(0);



}

void lin_tp_handle(l_ifc_handle inst,lin_tp_event_t event) {
    lin_core_state_t* state=&g_linGlobalState[inst];
#ifdef LIN_USE_UART
    l_u8* const data=state->dataBuffer;
#else
    l_u8* const data=state->frameBuf.data;
#endif
    if(state->tpInit==l_true) {
        switch(event) {
        case LIN_TP_TX_DATA_PRE:
            /* get data from tx_queue*/
            lin_get_raw(inst,data,l_true);
            /* setup ownNodeNad in master mode */
            if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER&&data[0]!=LD_FUNCTIONAL_NAD) {
                state->tpState->ownNodeNad=data[0];
            }
            break;
        case LIN_TP_RX_DATA_DONE:
            lin_tp_rx_handle(inst);
            break;
        case LIN_TP_TX_DATA_NRC78_DONE:
            lin_tp_tx_nrc78_handle(inst);
            break;
        case LIN_TP_TX_DATA_DONE:
            lin_tp_tx_handle(inst);
            break;
        case LIN_TP_RX_ERROR:
            lin_tp_rx_error_handle(inst);
            state->tpState->rxStatus=LD_RECEIVE_ERROR;
            break;
        case LIN_TP_TX_ERROR:
            lin_tp_tx_error_handle(inst);
            state->tpState->txStatus=LD_TRANSMIT_ERROR;
            break;
        default:
            break;
        }
    }
}


void ld_send_message (l_ifc_handle inst,l_u16 length,l_u8 nad,const l_u8* const data) {
    lin_core_state_t* state=&g_linGlobalState[inst];
    l_u8 buf[8];
    l_u8 msgNum=0;
    l_u8 i;
    l_u8 dataIndex=0;
    l_u16 tmpLen=length;
    l_u8 frameCnt=0;

#ifdef LIN_HAS_SLAVE
    if (g_linGlobalConfig[inst]->nodeType == LIN_NODE_SLAVE) {
        if (state->tpState->txMsgStatus == LD_N_AS_TIMEOUT) {
#ifdef LIN_USE_UART
            LIN_DRV_GotoIdleState(g_linGlobalConfig[inst]->hwInst);
#else
            LINFlexD_DRV_GotoIdleState(g_linGlobalConfig[inst]->hwInst);
#endif
            state->tpState->txMsgStatus = LD_COMPLETED;
        }
    }
#endif

    if (state->tpState->txMsgStatus == LD_COMPLETED) {
        if (length < 7) {
            msgNum = 1;
        }
        else {
            if (((length - 5U) % 6U) == 0U)
            {
                msgNum = (l_u8)(((length - 5U) / 6U) + 1U);
            }
            else
            {
                msgNum = (l_u8)(((length - 5U) / 6U) + 2U);
            }
        }
        if(lin_get_queue_size(inst,l_true)>=msgNum) {

            if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER) {
                buf[0]=nad;
            } else {
                /* If the call is made in a slave node application the frames are transmitted to the master node with the address NAD.
                    The parameter NAD is not used in slave nodes.*/

                buf[0]=state->tpState->ownNodeNad;
            }

            if(length<7) {
                /* SF */

                buf[1]=length;
                for(i=0; i<length; i++) {
                    buf[i+2]=data[i];
                }
                for(; i<6; i++) {
                    buf[i+2]=0xFF;
                }
                ld_put_raw(inst, buf);
            } else {
                /* FF */
                buf[1] = (l_u8)(((length / 256U) & 0x0FU) | 0x10U); /* PCI */
                buf[2] = (l_u8)(length % 256U); /* length */
                for (i = 0U; i < 5U; i++)
                {
                    /* data */
                    buf[i + 3U] = data[i];
                }
                msgNum--;
                dataIndex += 5U;
                tmpLen -= 5U;
                ld_put_raw(inst, buf);
                while (msgNum > 0U)
                {
                    ++frameCnt;
                    buf[1] = (l_u8)(0x20U | (frameCnt & 0x0FU));
                    if(tmpLen<6) {
                        /* last CF */
                        for(i=0; i<tmpLen; i++) {
                            buf[i+2]=data[dataIndex];
                            dataIndex++;
                        }
                        for(; i<6; i++) {
                            buf[i+2]=0xFF;
                        }
                    } else {
                        for(i=0; i<6; i++) {
                            buf[i+2]=data[dataIndex];
                            dataIndex++;
                        }
                        tmpLen-=6;
                    }
                    msgNum--;
                    ld_put_raw(inst, buf);
                }
            }
        }
        state->tpState->txMsgStatus=LD_IN_PROGRESS;
    }

}

l_u8 ld_raw_tx_status (l_ifc_handle inst){
    return g_linGlobalState[inst].tpState->txStatus;
}

l_u8 ld_raw_rx_status (l_ifc_handle inst){
    return g_linGlobalState[inst].tpState->rxStatus;
}


void ld_receive_message (l_ifc_handle inst,l_u16* const length,l_u8* const NAD,l_u8* const data)
{
    lin_core_state_t* state=&g_linGlobalState[inst];
    if((state->tpState->rxMsgStatus== LD_COMPLETED) && (*length != 0U)) {
        state->tpState->rxMsgStatus=LD_IN_PROGRESS;
        state->tpState->recvLen=length;
        state->tpState->recvNad=NAD;
        state->tpState->recvData=data;
    }

#ifdef LIN_HAS_SLAVE
    if (g_linGlobalConfig[inst]->nodeType == LIN_NODE_SLAVE) {
        if (state->tpState->rxMsgStatus == LD_N_CR_TIMEOUT) {
#ifdef LIN_USE_UART
            LIN_DRV_GotoIdleState(g_linGlobalConfig[inst]->hwInst);
#else
            LINFlexD_DRV_GotoIdleState(g_linGlobalConfig[inst]->hwInst);
#endif
            state->tpState->rxMsgStatus = LD_COMPLETED;
        }
    }
#endif
}
l_u8 ld_tx_status (l_ifc_handle inst) {
    return g_linGlobalState[inst].tpState->txMsgStatus;
}

l_u8 ld_rx_status (l_ifc_handle inst) {
    return g_linGlobalState[inst].tpState->rxMsgStatus;
}


l_u8 ld_is_ready(l_ifc_handle inst)
{
    return g_linGlobalState[inst].tpState->serviceStatus;
}


void ld_check_response(l_ifc_handle inst,l_u8* const RSID,l_u8* const error_code) {
    if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER) {
        *RSID=g_linGlobalState[inst].tpState->lastRSID;
        *error_code=g_linGlobalState[inst].tpState->lastCode;
    }
}

void ld_assign_frame_id_range (l_ifc_handle inst, l_u8 NAD, l_u8 start_index, const l_u8* const PIDs)
{

    l_u8 buff[6];
    lin_core_state_t* state=&g_linGlobalState[inst];

    /* Check if this interface is a LIN Master */
    if (g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER)
    {
        if (state->tpState->serviceStatus != LD_SERVICE_BUSY)
        {
            /* pack data into a single frame */
            /* Buffer[0] SID of Service Assign Frame ID Range: 0xB7 */
            buff[0] = LIN_SERVICE_ASSIGN_FRAME_ID_RANGE;
            buff[1] = start_index;
            buff[2] = PIDs[0];
            buff[3] = PIDs[1];
            buff[4] = PIDs[2];
            buff[5] = PIDs[3];

            /* put data into TX_QUEUE */
            ld_send_message(inst, 6U, NAD, buff);

            /* set node config status to busy */
            state->tpState->serviceStatus = LD_SERVICE_BUSY;
        } /* End of checking service status */
    }
}


void ld_assign_NAD(l_ifc_handle inst,l_u8 initial_NAD,l_u16 supplier_id,l_u16 function_id,l_u8 new_NAD)
{

    l_u8 data[6];
    lin_core_state_t* state=&g_linGlobalState[inst];

    if (g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER)
    {
        /* check service is busy? */
        if (state->tpState->serviceStatus != LD_SERVICE_BUSY)
        {
            /* data[0] SID of Service assign NAD: 0xB0 */
            data[0] = LIN_SERVICE_ASSIGN_NAD;
            data[1] = (l_u8)(supplier_id & 0x00FFU);
            data[2] = (l_u8)((supplier_id >> 8U) & 0x00FFU);
            data[3] = (l_u8)(function_id & 0x00FFU);
            data[4] = (l_u8)((function_id >> 8U) & 0x00FFU);
            data[5] = new_NAD;

            /* put data into TX_QUEUE */
            ld_send_message(inst, 6U, initial_NAD, data);

            /* set node config status to busy */
            state->tpState->serviceStatus = LD_SERVICE_BUSY;
        } /* End of checking service status */
    }
}

void ld_save_configuration (l_ifc_handle inst,l_u8 NAD)
{
    l_u8 data[2];
    lin_core_state_t* state=&g_linGlobalState[inst];

    if (g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER)
    {
        /* check service is busy? */
        if (state->tpState->serviceStatus != LD_SERVICE_BUSY)
        {
            /* data[0] SID of Service save configuration: 0xB6 */
            data[0] = LIN_SERVICE_SAVE_CONFIGURATION;
            data[1] = 0xFFU;

            /* put data into TX_QUEUE */
            ld_send_message(inst, 2U, NAD, data);

            /* set node config status to busy */
            state->tpState->serviceStatus = LD_SERVICE_BUSY;
        } /* End of checking service status */
    }
}
l_u8 ld_read_configuration(l_ifc_handle inst,l_u8 * const data,l_u8 * const length)
{
    l_u8 temp;
    l_u8 retval = LD_READ_OK;

    /* Check if slave node */
    if (g_linGlobalConfig[inst]->nodeType==LIN_NODE_SLAVE)
    {
        temp = *length;
        /* Get node attribute */

        if (temp < g_linGlobalConfig[inst]->tpCfg->idNum)
        {
            /* The 'data' size is not enough to store NAD+PIDs */
            retval = (l_u8)LD_LENGTH_TOO_SHORT;
        }
        else
        {

            /* Copy protected IDs to 'data' */
            for (int i = 0; i < g_linGlobalConfig[inst]->tpCfg->idNum; i++)
            {
                data[i] = g_linGlobalConfig[inst]->tpCfg->idList[i];
            }

            /* Set the length parameter to the actual size of the configuration */
            *length =  g_linGlobalConfig[inst]->tpCfg->idNum;
        }
    }
    else
    {
        retval = 0xFFU;
    }

    return retval;
} /* End ld_read_configuration() */
l_u8 ld_set_configuration(l_ifc_handle inst,const l_u8 * const data,l_u16 length)
{

    /* Set the default returned value to LD_DATA_ERROR */
    l_u8 retval = LD_DATA_ERROR;
    lin_core_state_t* state=&g_linGlobalState[inst];

    /* Check if slave node */
    if (g_linGlobalConfig[inst]->nodeType==LIN_NODE_SLAVE)
    {
        /* Get node attribute */

        if (length < g_linGlobalConfig[inst]->tpCfg->idNum)
        {
            /* The 'data' size is not enough to contain NAD+PIDs */
            retval = LD_LENGTH_NOT_CORRECT;
        }
        else
        {
            /* The 'data' size is enough to contain NAD+PIDs, so proceed to read from 'data' */
            /* Read actual NAD from 'data' */
            state->tpState->ownNodeNad= data[0];

            /* Copy protected IDs in 'data' to RAM configuration */
            for (int i = 0; i < g_linGlobalConfig[inst]->tpCfg->idNum; ++i)
            {
                g_linGlobalConfig[inst]->tpCfg->idList[i] = data[i];
            }

            /* No error, return OK */
            retval = LD_SET_OK;
            state->slaveState->nadConfigured=l_true;
        }
    }
    else
    {
        retval = 0xFFU;
    }

    return retval;
} /* End ld_set_configuration() */


void ld_conditional_change_NAD (l_ifc_handle inst,l_u8 NAD,l_u8 id,\
                                l_u8 byte,l_u8 mask,l_u8 invert,l_u8 new_NAD)
{
    l_u8 data[6];
    lin_core_state_t* state=&g_linGlobalState[inst];
    if ((id < 32U) && ((0U < byte) && (byte < 6U)))
    {
        if(g_linGlobalConfig[inst]->nodeType==LIN_NODE_MASTER) {
            /* data[0] SID of Service conditional change NAD: 0xB3 */
            data[0] = LIN_SERVICE_CONDITIONAL_CHANGE_NAD;
            data[1] = id;
            data[2] = byte;
            data[3] = mask;
            data[4] = invert;
            data[5] = new_NAD;

            /* put data into TX_QUEUE */
            ld_send_message(inst, 6U, NAD, data);

            /* set node config status to busy */
            state->tpState->serviceStatus = LD_SERVICE_BUSY;
        }
    }

}

void lin_set_diag_mode(l_ifc_handle inst,l_bool diagOnly){
#ifdef LIN_HAS_MASTER
    g_linGlobalState[inst].masterState->masterTpState->diagOnly=diagOnly;
#endif
}