/*
 * Copyright (c) 2022, Shenzhen CVA Innovation CO.,LTD
 * All rights reserved.
 *
 * Shenzhen CVA Innovation CO.,LTD (CVA chip) is supplying this file for use
 * exclusively with CVA's microcontroller products. This file can be freely
 * distributed within development tools that are supporting such microcontroller
 * products.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * CVA SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 */

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

#include <stdint.h>
#include <string.h>
#include "lin_if.h"
#include "lin_schedule.h"

/*******************************************************************************
 * the defines
 ******************************************************************************/

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

/*******************************************************************************
 * the globals
 ******************************************************************************/
static LinIf_CtrlInfoType     sLinIf_CtrlInfo;

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

/*! \brief Convert frame id to pid
 *
 * This function execute to convert the frame id to protected id
 *
 * \param[in] frameId: Frame id, this includes the low 6bit 
 * \return: the result of convert pid
 */
uint8_t LinIf_ConvertFrameIdToPid(uint8_t frameId)
{
    uint8_t ret = frameId;
    uint8_t i = 0, tempP0 = 0, tempP1 = 0, tempBit = 0;

    for(i = 0; i < 6; i++)
    {
        if(((frameId >> i) & 0x01) != 0u)
        {
            tempBit = 1;
        }
        else
        {
            tempBit = 0;
        }

        if((i == 0) || (i == 2))
        {
            /* P0 */
            if(i == 0)
            {
                tempP0 = tempBit;
            }
            else
            {
                tempP0 = tempP0 ^ tempBit;
            }
        }
        else if((i == 3) || (i == 5))
        {
            /* P1 */
            tempP1 = tempP1 ^ tempBit;

            if(i == 5)
            {
                if(tempP1 == 0x01)
                {
                    tempP1 = 0x00;
                }
                else
                {
                    tempP1 = 0x01;
                }
            }
        }
        else
        {
            /* P1 */
            if(i == 1)
            {
                tempP1 = tempBit;
            }
            else
            {
                tempP1 = tempP1 ^ tempBit;
            }

            tempP0 = tempP0 ^ tempBit;
        }
    }

    ret = (ret | (tempP0 << 6));
    ret = (ret | (tempP1 << 7));

    return ret;
}

/*! \brief Convert pid to frame id
 *
 * This function execute to convert pid to frame id
 *
 * \param[in] pid: protected id
 * \return: the result of convert frame id
 */
uint8_t LinIf_ConvertPidToFrameId(uint8_t pid)
{
    uint8_t ret = LIN_DATA_INVALID, tempPid = pid;
    uint8_t i = 0, tempP0 = 0, tempP1 = 0, tempBit = 0;

    for(i = 0; i < 6; i++)
    {
        if(((tempPid >> i) & 0x01) != 0u)
        {
            tempBit = 1;
        }
        else
        {
            tempBit = 0;
        }

        if((i == 0) || (i == 2))
        {
            /* P0 */
            if(i == 0)
            {
                tempP0 = tempBit;
            }
            else
            {
                tempP0 = tempP0 ^ tempBit;
            }
        }
        else if((i == 3) || (i == 5))
        {
            /* P1 */
            tempP1 = tempP1 ^ tempBit;
            if(i == 5)
            {
                if(tempP1 == 0x01)
                {
                    tempP1 = 0x00;
                }
                else
                {
                    tempP1 = 0x01;
                }
            }
        }
        else
        {
            tempP0 = tempP0 ^ tempBit;
            /* P1 */
            if(i == 1)
            {
                tempP1 = tempBit;
            }
            else
            {
                tempP1 = tempP1 ^ tempBit;
            }
        }
    }

    if(tempP0 == ((tempPid >> 6) & 0x01))
    {
        if(tempP1 == ((tempPid >> 7) & 0x01))
        {
            ret = (pid & 0x3F);
        }
    }
    if((tempP0 == ((tempPid >> 6) & 0x01)) && (tempP1 == ((tempPid >> 7) & 0x01)))
    {
        ret = (pid & 0x3F);
    }

    return ret;
}

/*! \brief Set the sleep request indication flag
 *
 * This function execute to sleep request indication, Indication Upper layer that go to sleep cmd is doing 
 *
 * \param[in] channelIdx: which physical channel is used 
 * \return: void
 */
void LinIf_SetSleepRequestIndication(uint8_t channelIdx)
{
    sLinIf_CtrlInfo.pduState[channelIdx].sleepReqIndication = true;
}

/*! \brief Clear the sleep request  
 *
 * This function execute to clear the sleep reqeust indication
 *
 * \param[in] channelIdx: which physical channel is used 
 * \return: void
 */
void LinIf_ClearSleepRequestIndication(uint8_t channelIdx)
{
    sLinIf_CtrlInfo.pduState[channelIdx].sleepReqIndication = false;
}

/*! \brief Get the sleep request indication
 *
 * This function execute to get the sleep request indication after enter into prepare sleep mode 
 *
 * \param[in] channelIdx: which physical channel is used 
 * \return: bool
 */
bool LinIf_GetSleepRequestIndication(uint8_t channelIdx)
{
    bool ret = false;

    ret = sLinIf_CtrlInfo.pduState[channelIdx].sleepReqIndication;
    return ret;
}

/*! \brief Request transmit the frame 
 *
 * This function execute to request transmit the frame, the function is only used by master node 
 *
 * \param[in] channelIdx: which physical channel is used
 * \param[in] frameInfoPtr: the pointer of frame  
 * \return: true: request transmit successfully
 *          false: request transmit failed
 */
bool LinIf_ReqTransmit(uint8_t channelIdx, const Lin_FrameInfoType *frameInfoPtr)
{
    bool ret = false;

    sLinIf_CtrlInfo.pduState[channelIdx].opStatusInfo = LIN_STATUS_READY;

    sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl    = frameInfoPtr->frameCtrl;
    sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameType    = frameInfoPtr->frameType;
    sLinIf_CtrlInfo.pduState[channelIdx].pdu.pid          = frameInfoPtr->pid;
    sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx       = LinSchedule_GetPidIdx(frameInfoPtr->pid);
    sLinIf_CtrlInfo.pduState[channelIdx].pdu.checkSumType = LinSchedule_GetCheckSumType(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pid);

    if(sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl == LIN_FRAME_CTRL_PUBLISH)
    {
        sLinIf_CtrlInfo.pduState[channelIdx].pdu.dataLen = frameInfoPtr->dataLen;

        if((LinSchedule_GetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx) == LIN_FRAME_ACCESS_STATE_IDLE) || (LinSchedule_GetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx) == LIN_FRAME_ACCESS_STATE_UPDATED))
        {
            /* Copy pdu to local buffer */
            memcpy(&sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameData, frameInfoPtr->pFrameData, sizeof(Lin_FrameDataType));

            sLinIf_CtrlInfo.pduState[channelIdx].pdu.checkSum =  sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr->pLinIf_CalculateChecksumFunction(sLinIf_CtrlInfo.pduState[channelIdx].pdu.checkSumType,
                                                                                          sLinIf_CtrlInfo.pduState[channelIdx].pdu.pid,
                                                                                          (const uint8_t *)&sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameData.poyload.byteData[0],
                                                                                          sLinIf_CtrlInfo.pduState[channelIdx].pdu.dataLen);

            LinSchedule_SetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx, LIN_FRAME_ACCESS_STATE_IDLE);
            ret = sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr->pLinIf_SendFrameFunction(channelIdx, &sLinIf_CtrlInfo.pduState[channelIdx].pdu);
        }
    }
    else if(sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl == LIN_FRAME_CTRL_SUBSCRIBER)
    {
        /* If the current frame is an event-trigger, then the datalen is get from the next step, not in current step, just set with the max value to it */
        if(sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameType == LIN_FRAME_TYPE_EVENT_TRIGGER)
        {
            sLinIf_CtrlInfo.pduState[channelIdx].pdu.dataLen = 8;
        }
        else
        {
            sLinIf_CtrlInfo.pduState[channelIdx].pdu.dataLen = frameInfoPtr->dataLen;
        }

        if(LinSchedule_GetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx) == LIN_FRAME_ACCESS_STATE_IDLE)
        {
            if(sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl == LIN_FRAME_CTRL_SUBSCRIBER)
            {
                LinSchedule_SetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx, LIN_FRAME_ACCESS_STATE_READING_OUT);
            }

            sLinIf_CtrlInfo.pduState[channelIdx].upperLevelFrameDataPtr = LinSchedule_GetFrameBufferPtr(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx);
            ret                                                        = sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr->pLinIf_SendFrameFunction(channelIdx, &sLinIf_CtrlInfo.pduState[channelIdx].pdu);
        }
    }
    else if(sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl == LIN_FRAME_CTRL_NO_ACTION)
    {
        sLinIf_CtrlInfo.pduState[channelIdx].pdu.dataLen = 0;

        if(LinSchedule_GetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx) == LIN_FRAME_ACCESS_STATE_IDLE)
        {
            sLinIf_CtrlInfo.pduState[channelIdx].upperLevelFrameDataPtr = LinSchedule_GetFrameBufferPtr(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx);
            ret                                                        = sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr->pLinIf_SendFrameFunction(channelIdx, &sLinIf_CtrlInfo.pduState[channelIdx].pdu);
        }
    }
    else
    {
    }

    return ret;
}

/*! \brief The call-back function when received valid header 
 *
 * This function execute to process the pdu attribute by received pid, this function is only used by slave node, this is called by isr
 *
 * \param[in] uint8_t channelIdx: which physical channel is used
 * \param[in] pid: the received pid
 * \param[in] pduInfoPtr: the local pdu pointer
 * \return: true: the current received pid is valid and the frame control is not the no-action type
 *          false; the pid is invalid or not supported or the frame control is the no-action type
 */
bool LinIf_HeaderIndication(uint8_t channelIdx, uint8_t pid, Lin_PduType **pduInfoPtr)
{
    bool    ret    = false;
    uint8_t pidIdx = LIN_DATA_INVALID;

    pidIdx = LinSchedule_GetPidIdx(pid);
    if(pidIdx != LIN_DATA_INVALID)
    {
        /* Tell the low level the data address that stored in Interface Level */
        *pduInfoPtr = &sLinIf_CtrlInfo.pduState[channelIdx].pdu;

        sLinIf_CtrlInfo.pduState[channelIdx].pdu.pid       = pid;
        sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx    = pidIdx;
        sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameType = LinSchedule_GetFrameType(channelIdx, pidIdx);

        if(sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameType == LIN_FRAME_TYPE_EVENT_TRIGGER)
        {
            uint8_t                                linkTblIdx = 0;
            const LinSchedule_ScheduleTblListType *tblPtr     = NULL;
            uint8_t                                i = 0, subscriberCnt = 0;
            Lin_FrameCtrlType                      frameCtrl = LIN_FRAME_CTRL_NO_ACTION;

            /* Use the normal table, in slave mode, there is only one normal schedule table */
            linkTblIdx = LinSchedule_GetLinkTblIdx(channelIdx, 1, sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx);

            tblPtr                                    = (const LinSchedule_ScheduleTblListType *)LinSchedule_GetLinkScheduleTblPtr(channelIdx, linkTblIdx);
            sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl = LIN_FRAME_CTRL_NO_ACTION;

            if(tblPtr != NULL)
            {
                for(i = 0; i < tblPtr->frameNum; i++)
                {
                    pidIdx    = LinSchedule_GetPidIdxInSchedulerTbl(channelIdx, linkTblIdx, i);
                    frameCtrl = LinSchedule_GetFrameCtrl(channelIdx, pidIdx);
                    if(frameCtrl == LIN_FRAME_CTRL_PUBLISH)
                    {
                        sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl = LIN_FRAME_CTRL_PUBLISH;
                        break;
                    }
                    else
                    {
                        if(frameCtrl == LIN_FRAME_CTRL_SUBSCRIBER)
                        {
                            subscriberCnt++;
                        }
                    }
                }
            }

            if(sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl == LIN_FRAME_CTRL_NO_ACTION)
            {
                if(subscriberCnt > 0u)
                {
                    sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl = LIN_FRAME_CTRL_SUBSCRIBER;
                }
            }

            /* Get all frames in the associated collision table, if there are at least one frame that need to be transmit by this node, the node is set to be in publisher, nor to be set as subscriber */
            if(sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl != LIN_FRAME_CTRL_NO_ACTION)
            {
                sLinIf_CtrlInfo.pduState[channelIdx].pdu.checkSumType = LinSchedule_GetCheckSumType(pid);
                if(sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl == LIN_FRAME_CTRL_PUBLISH)
                {
                    uint8_t i = 0;

                    /* If the current pid is a event trigger frame, but local buffer data is not diffirent with last value, so no response to bus */
                    if(LinSchedule_GetFrameAccessState(pidIdx) == LIN_FRAME_ACCESS_STATE_UPDATED)
                    {
                        LinSchedule_SetFrameAccessState(pidIdx, LIN_FRAME_ACCESS_STATE_WRITING_IN);

                        /* Get the upper level data to store in local pdu buffer */
                        sLinIf_CtrlInfo.pduState[channelIdx].upperLevelFrameDataPtr = LinSchedule_GetFrameBufferPtr(pidIdx);
                        sLinIf_CtrlInfo.pduState[channelIdx].pdu.dataLen            = LinSchedule_GetFrameDataLen(pidIdx);

                        sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameData.poyload.eventBytes.pid = LinSchedule_GetPid(pidIdx);
                        for(i = 1; i < sLinIf_CtrlInfo.pduState[channelIdx].pdu.dataLen; i++)
                        {
                            sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameData.poyload.byteData[i] = sLinIf_CtrlInfo.pduState[channelIdx].upperLevelFrameDataPtr->poyload.byteData[i];
                        }

                        sLinIf_CtrlInfo.pduState[channelIdx].pdu.checkSum =  sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr->pLinIf_CalculateChecksumFunction(sLinIf_CtrlInfo.pduState[channelIdx].pdu.checkSumType,
                                                                                                      sLinIf_CtrlInfo.pduState[channelIdx].pdu.pid,
                                                                                                      (const uint8_t *)&sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameData.poyload.byteData[0],
                                                                                                      sLinIf_CtrlInfo.pduState[channelIdx].pdu.dataLen);

                        LinSchedule_SetFrameAccessState(pidIdx, LIN_FRAME_ACCESS_STATE_IDLE);

                        sLinIf_CtrlInfo.pduState[channelIdx].txTimeOutCnt = 0;

                        ret = true;
                    }
                }
                else if(sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl == LIN_FRAME_CTRL_SUBSCRIBER)
                {
                    /* With the default data length, the actual length is set in the next step */
                    sLinIf_CtrlInfo.pduState[channelIdx].pdu.dataLen = 8;
                    if(LinSchedule_GetFrameAccessState(pidIdx) == LIN_FRAME_ACCESS_STATE_IDLE)
                    {
                        LinSchedule_SetFrameAccessState(pidIdx, LIN_FRAME_ACCESS_STATE_READING_OUT);
                    }

                    /* Get the upper level data address to store the received data from low level */
                    sLinIf_CtrlInfo.pduState[channelIdx].upperLevelFrameDataPtr = LinSchedule_GetFrameBufferPtr(pidIdx);

                    ret = true;
                }
                else
                {
                    if(LinSchedule_GetFrameAccessState(pidIdx) == LIN_FRAME_ACCESS_STATE_IDLE)
                    {
                        LinSchedule_SetFrameAccessState(pidIdx, LIN_FRAME_ACCESS_STATE_WRITING_IN);
                    }
                }
            }
            else
            {
                sLinIf_CtrlInfo.pduState[channelIdx].opStatusInfo = LIN_STATUS_IDLE;
            }
        }
        else
        {
            sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl = LinSchedule_GetFrameCtrl(channelIdx, sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx);

            if(sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl != LIN_FRAME_CTRL_NO_ACTION)
            {
                sLinIf_CtrlInfo.pduState[channelIdx].pdu.dataLen      = LinSchedule_GetFrameDataLen(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx);
                sLinIf_CtrlInfo.pduState[channelIdx].pdu.checkSumType = LinSchedule_GetCheckSumType(pid);
                if(sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl == LIN_FRAME_CTRL_PUBLISH)
                {
                    uint8_t i = 0;

                    /* If the current pid is a event trigger frame, but local buffer data is not diffirent with last value, so no response to bus */
                    if((LinSchedule_GetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx) == LIN_FRAME_ACCESS_STATE_UPDATED) || (LinSchedule_GetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx) == LIN_FRAME_ACCESS_STATE_IDLE))
                    {
                        LinSchedule_SetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx, LIN_FRAME_ACCESS_STATE_WRITING_IN);

                        /* Get the upper level data to store in local pdu buffer */
                        sLinIf_CtrlInfo.pduState[channelIdx].upperLevelFrameDataPtr = LinSchedule_GetFrameBufferPtr(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx);

                        for(i = 0; i < sLinIf_CtrlInfo.pduState[channelIdx].pdu.dataLen; i++)
                        {
                            sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameData.poyload.byteData[i] = sLinIf_CtrlInfo.pduState[channelIdx].upperLevelFrameDataPtr->poyload.byteData[i];
                        }

                        sLinIf_CtrlInfo.pduState[channelIdx].pdu.checkSum =  sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr->pLinIf_CalculateChecksumFunction(sLinIf_CtrlInfo.pduState[channelIdx].pdu.checkSumType,
                                                                                                      sLinIf_CtrlInfo.pduState[channelIdx].pdu.pid,
                                                                                                      (const uint8_t *)&sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameData.poyload.byteData[0],
                                                                                                      sLinIf_CtrlInfo.pduState[channelIdx].pdu.dataLen);

                        LinSchedule_SetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx, LIN_FRAME_ACCESS_STATE_IDLE);

                        sLinIf_CtrlInfo.pduState[channelIdx].txTimeOutCnt = 0;
                    }
                }
                else if(sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameCtrl == LIN_FRAME_CTRL_SUBSCRIBER)
                {
                    if(LinSchedule_GetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx) == LIN_FRAME_ACCESS_STATE_IDLE)
                    {
                        LinSchedule_SetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx, LIN_FRAME_ACCESS_STATE_READING_OUT);
                    }

                    /* Get the upper level data address to store the received data from low level */
                    sLinIf_CtrlInfo.pduState[channelIdx].upperLevelFrameDataPtr = LinSchedule_GetFrameBufferPtr(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx);
                }
                else
                {
                    if(LinSchedule_GetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx) == LIN_FRAME_ACCESS_STATE_IDLE)
                    {
                        LinSchedule_SetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx, LIN_FRAME_ACCESS_STATE_WRITING_IN);
                    }
                }

                ret = true;
            }
            else
            {
                sLinIf_CtrlInfo.pduState[channelIdx].opStatusInfo = LIN_STATUS_IDLE;
            }
        }
    }

    return ret;
}

/*! \brief Process the frame data length 
 *
 * This function execute to process the link data length by the event-trigger frame id, this is called by isr
 *
 * \param[in] channelIdx: which physical channel is used 
 * \param[in] pid: the first data in the response of event trigger frame, the event frame is link to one unconditional frame, the first data in payload is pid 
 * \param[in] pDataLen: the pointer of data length
 * \param[in] nodeType: node type 
 * \return: void
 */
void LinIf_EventFrameDataLenIndication(uint8_t channelIdx, uint8_t pid, uint8_t *pDataLen, Lin_NodeType nodeType)
{
    uint8_t pidIdx  = LIN_DATA_INVALID;
    uint8_t dataLen = 0;

    if(nodeType == LIN_NODE_IS_MASTER)
    {
        pidIdx  = LinSchedule_GetPidIdx(pid);
        dataLen = LinSchedule_GetFrameDataLen(pidIdx);

        *pDataLen = dataLen;
    }
    else
    {
        pidIdx  = LinSchedule_GetPidIdx(pid);
        dataLen = LinSchedule_GetFrameDataLen(pidIdx);

        *pDataLen = dataLen;
    }
}

/*! \brief Received indication
 *
 * This function execute to indicate the upper layer that one new received frame is done, this is called by isr 
 *
 * \param[in] channelIdx: which physical channel is used
 * \return: void
 */
void LinIf_RxIndication(uint8_t channelIdx)
{
    Lin_FrameAccessStateType accessState = LinSchedule_GetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx);

    if(accessState == LIN_FRAME_ACCESS_STATE_READING_OUT)
    {
        LinSchedule_SetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx, LIN_FRAME_ACCESS_STATE_UPDATED);
    }
    else if(accessState == LIN_FRAME_ACCESS_STATE_UPDATED)
    {
        LinSchedule_SetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx, LIN_FRAME_ACCESS_STATE_OVER_RECEIVED);
    }

    memcpy(sLinIf_CtrlInfo.pduState[channelIdx].upperLevelFrameDataPtr, &sLinIf_CtrlInfo.pduState[channelIdx].pdu.frameData, sizeof(Lin_FrameDataType));
}

/*! \brief Transfer confirmed
 *
 * This function execute to tell upper layer that one transfer is done successfully,, this is called by isr
 *
 * \param[in] channelIdx: which physical channel is used
 * \return: void
 */
void LinIf_TxConfirmation(uint8_t channelIdx)
{
    /* Nothing to do */
}

/*! \brief Transfer or receive indicate
 *
 * This function execute to indicate that one data is received or one data is transmitted, then to clear the timeout counter,, this is called by isr 
 *
 * \param[in] channelIdx: which physical channel is used
 * \return: void
 */
void LinIf_TRxIndication(uint8_t channelIdx)
{
    switch(sLinIf_CtrlInfo.pduState[channelIdx].opStatusInfo)
    {
    case LIN_STATUS_TX_DATA_BUSY:
        sLinIf_CtrlInfo.pduState[channelIdx].txTimeOutCnt = 0;
        break;

    case LIN_STATUS_HEADER_BUSY:
        sLinIf_CtrlInfo.pduState[channelIdx].rxTimeOutCnt = 0;
        break;

    case LIN_STATUS_RX_DATA_BUSY:
        sLinIf_CtrlInfo.pduState[channelIdx].rxTimeOutCnt = 0;

        break;

    default:

        break;
    }
}

/*! \brief Send wake-up signal
 *
 * This function execute to send wake-up signal
 *
 * \param[in] channelIdx: which physical channel is used
 * \return: void
 */
void LinIf_SendWakeUpSignal(uint8_t channelIdx)
{
    sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr->pLinIf_SendWakeUpSignalFunction(channelIdx);
}

/*! \brief Get the signal of wake-up
 *
 * This function execute to get the signal of wake-up
 *
 * \param[in] channelIdx: which physical channel of lin to be selected
 * \return: true: wake-up signal is valid
 *          false: wake-up signal is invalid
 */
bool LinIf_GetWakeupSignal(uint8_t channelIdx)
{
    return sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr->pLinIf_GetWakeupSignalFunction(channelIdx);
}

/*! \brief Get the transfer operated status
 *
 * This function execute to get the current transfer status
 *
 * \param[in] channelIdx: which physical channel is used
 * \return: the status of current transfer operation 
 */
Lin_OpStatusType LinIf_GetTransferOperateStatus(uint8_t channelIdx)
{
    return sLinIf_CtrlInfo.pduState[channelIdx].opStatusInfo;
}

/*! \brief Indicate error information
 *
 * This function execute to report the error information, this is called by isr
 *
 * \param[in] channelIdx: which physical channel is used
 * \param[in] errStatus: the current error information 
 * \return: void
 */
void LinIf_ErrorIndication(uint8_t channelIdx, Lin_OpStatusType errStatus)
{
    sLinIf_CtrlInfo.pduState[channelIdx].opStatusInfo = errStatus;

    switch(errStatus)
    {
    case LIN_STATUS_TX_DATA_BUSY:
        sLinIf_CtrlInfo.pduState[channelIdx].txTimeOutCnt = 0;
        break;

    case LIN_STATUS_HEADER_BUSY:
    case LIN_STATUS_RX_DATA_BUSY:
        sLinIf_CtrlInfo.pduState[channelIdx].rxTimeOutCnt = 0;
        break;

    default:
        if(errStatus >= LIN_STATUS_ERR_BIT)
        {
            LinSchedule_SetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx, LIN_FRAME_ACCESS_STATE_IDLE);
        }
        sLinIf_CtrlInfo.pduState[channelIdx].txTimeOutCnt = 0;
        sLinIf_CtrlInfo.pduState[channelIdx].rxTimeOutCnt = 0;
        break;
    }
}

/*! \brief Process the current transfer operation timeout
 *
 * This function execute to calculate the current transfer is timeout or not
 *
 * \param[in] channelIdx: which physical channel is used
 * \return: void
 */
void LinIf_MainFunction(uint8_t channelIdx)
{
    switch(sLinIf_CtrlInfo.pduState[channelIdx].opStatusInfo)
    {
    case LIN_STATUS_TX_DATA_BUSY:
        /* Tx Timeout */
        sLinIf_CtrlInfo.pduState[channelIdx].txTimeOutCnt++;
        if(sLinIf_CtrlInfo.pduState[channelIdx].txTimeOutCnt >= LINCFG_RESP_BYTE_TIMEOUT_MAX_CNT)
        {
            sLinIf_CtrlInfo.pduState[channelIdx].txTimeOutCnt = 0;
            sLinIf_CtrlInfo.pduState[channelIdx].opStatusInfo = LIN_STATUS_ERR_TIMEOUT;
            sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr->pLinIf_ResetFunction(channelIdx);
        }
        break;

    case LIN_STATUS_HEADER_BUSY:
    case LIN_STATUS_RX_DATA_BUSY:
        /* Rx Timeout */
        sLinIf_CtrlInfo.pduState[channelIdx].rxTimeOutCnt++;
        if(sLinIf_CtrlInfo.pduState[channelIdx].rxTimeOutCnt >= LINCFG_RESP_BYTE_TIMEOUT_MAX_CNT)
        {
            sLinIf_CtrlInfo.pduState[channelIdx].rxTimeOutCnt = 0;
            sLinIf_CtrlInfo.pduState[channelIdx].opStatusInfo = LIN_STATUS_ERR_TIMEOUT;
            LinSchedule_SetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx, LIN_FRAME_ACCESS_STATE_IDLE);
            sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr->pLinIf_ResetFunction(channelIdx);
        }
        break;

    case LIN_STATUS_READY: /* Tx Timeout */
        sLinIf_CtrlInfo.pduState[channelIdx].txTimeOutCnt++;
        if(sLinIf_CtrlInfo.pduState[channelIdx].txTimeOutCnt >= LINCFG_RESP_BYTE_TIMEOUT_MAX_CNT)
        {
            sLinIf_CtrlInfo.pduState[channelIdx].txTimeOutCnt = 0;
            sLinIf_CtrlInfo.pduState[channelIdx].opStatusInfo = LIN_STATUS_ERR_TIMEOUT;
            LinSchedule_SetFrameAccessState(sLinIf_CtrlInfo.pduState[channelIdx].pdu.pidIdx, LIN_FRAME_ACCESS_STATE_IDLE);
            sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr->pLinIf_ResetFunction(channelIdx);
        }
        break;

    default:
        sLinIf_CtrlInfo.pduState[channelIdx].rxTimeOutCnt = 0;
        sLinIf_CtrlInfo.pduState[channelIdx].txTimeOutCnt = 0;
        break;
    }
}

/*! \brief Install the call back function 
 *
 * This function execute to initialize the lin interface
 *
 * \param[in] callbackList: the call back interface from lin driver in lin interface layer
 * \return: void
 */
void LinIf_CallbackInit(const LinIf_CallBackFuncListType *callbackList)
{
    uint8_t i                                 = 0;

    for(i = 0; i < LINCFG_CH_NUM; i++)
    {
        memset(&sLinIf_CtrlInfo.pduState[i], 0, sizeof(LinIf_StateType));
    }

    sLinIf_CtrlInfo.callBackInstalled          = true;
    sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr = callbackList;
}

/*! \brief Initialize the Lin Interface
 *
 * This function execute to initialize the lin interface
 *
 * \param[in] channelIdx: which physical channel is used
 * \param[in] nodeType: the current node type
 * \param[in] callbackList: the call back interface from lin driver in lin interface layer 
 * \return: void
 */
void LinIf_Init(uint8_t channelIdx, Lin_NodeType nodeType)
{
    memset(&sLinIf_CtrlInfo.pduState[channelIdx], 0, sizeof(LinIf_StateType));

    sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr->pLinIf_InitFunction(channelIdx, nodeType);
}

/*! \brief DeInitialize the Lin Interface
 *
 * This function execute to initialize the lin interface
 *
 * \param[in] channelIdx: which physical channel is used
 * \return: void
 */
void LinIf_DeInit(uint8_t channelIdx)
{
    memset(&sLinIf_CtrlInfo.pduState[channelIdx], 0, sizeof(LinIf_StateType));

    sLinIf_CtrlInfo.pLinIf_CallBackFuncListPtr->pLinIf_DeInitFunction(channelIdx);
}
