/*******************************************************************************
* the includes
******************************************************************************/
#include "LIN_Master.h"


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

/* the LIN baud rate */
#define LIN_BAUD_RATE               (19200u)

/* one lin frame data size */
#define LIN_DATA_SIZE               (8u)
/* lin frame sync header */
#define LIN_SYNC_HEADER             (0x55)

/* the client requests ID */
#define LIN_ID_DIAG_MASTER_REQ      (0x3C)
/* the server response ID */
#define LIN_ID_DIAG_SLAVE_RESP      (0x3D)

#define LIN_FRAME_NUM       (5u)

/* add parity for frame id */
#define MAKE_PARITY         (0u)
/* check parity for frame pid */
#define CHECK_PARITY        (1u)

#define GETBIT(A,B)         ((A >> B) & 0x01)           //get bit B in A

#define TIMER_UNIT_US       (1000u)

/*******************************************************************************
* the typedefs
******************************************************************************/
typedef enum {
    LIN_STATE_IDLE = 0u,
    LIN_STATE_BREAK,
    LIN_STATE_SYNC_HEDADER,
    LIN_STATE_PID,
    LIN_STATE_TX_DATA,
    LIN_STATE_RX_DATA,
    LIN_STATE_CHECKSUM
}Lin_StateType;

typedef enum {
    LINSTACK_CHECKSUM_CLASSIC = 0,
    LINSTACK_CHECKSUM_ENHANCED,

    LINSTACK_CHECKSUM_NUM
}Lin_CheckSumType;

typedef enum
{
    LIN_RES_PUB     = 0x00U,    /*!< Publisher response */
    LIN_RES_SUB     = 0x01U     /*!< Subscriber response */
} lin_ResponseType;

typedef enum
{
    LIN_FRM_UNCD  = 0x00U,  /*!< Unconditional frame */
    LIN_FRM_EVNT  = 0x01U,  /*!< Event triggered frame */
    LIN_FRM_SPRDC = 0x10U,  /*!< Sporadic frame */
    LIN_FRM_DIAG  = 0x11U   /*!< Diagnostic frame */
} lin_FrameType;

typedef struct {
    uint8_t data[LIN_DATA_SIZE];
    uint8_t checkSum;
}Lin_FrameDataType;

typedef struct {
    uint8_t             frameId;        /*!< frame identifier */
    lin_FrameType       frameType;      /*!< frame type */
    uint8_t             frameLen;       /*!< frame data length */
    lin_ResponseType    resType;        /*!< response type */
    uint8_t             *pFrameData;    /*!< frame data buffer point */
    uint8_t             updatingFlag;   /*!< frame data updating flag */
}Lin_FrameInfoType;

/*******************************************************************************
* the globals
******************************************************************************/
extern McuType mcu;

static Lin_FrameDataType linTxData;
static Lin_FrameDataType linRxData;

static uint8_t linTxDataCnt = 0;
static uint8_t linRxDataCnt = 0;

static uint8_t linFrameData[LIN_FRAME_NUM][LIN_DATA_SIZE];
static uint8_t linFrameDataBackup[LIN_DATA_SIZE];

static Lin_FrameInfoType   linFrameInfoTab[LIN_FRAME_NUM] = {
    {0x10, LIN_FRM_UNCD, 4, LIN_RES_PUB, &linFrameData[0][0], 0x00},
    {0x20, LIN_FRM_UNCD, 8, LIN_RES_SUB, &linFrameData[1][0], 0x00},
    {0x21, LIN_FRM_UNCD, 8, LIN_RES_SUB, &linFrameData[2][0], 0x00},
    {0x30, LIN_FRM_UNCD, 8, LIN_RES_SUB, &linFrameData[3][0], 0x00},
    {0x31, LIN_FRM_UNCD, 8, LIN_RES_SUB, &linFrameData[4][0], 0x00},
};

uint8_t gCurFrameIdx = 0;

Lin_StateType linState = LIN_STATE_IDLE;

/* time stamp variable unit 1ms */
volatile uint32_t gTime1msCnt = 0;

/* the timeout counter of lin communication */
volatile uint32_t linComTimeoutCnt = 0;

uint32_t linMaxHeaderTimeout = 0;
uint32_t linMaxFrameResTimeout = 0;


/*******************************************************************************
* the const
******************************************************************************/



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

/*! \brief Computes maximum header timeout
 *
 * Theader_Maximum = 1.4 * THeader_Nominal, THeader_Nominal = 34 * TBit,
 * (13 nominal bits of break;   1 nominal bit of break delimiter; 10 bits for SYNC and 10 bits of PID)
 * TIMER_UNIT_US is in micro second
 *
 * \param[in] baudRate LIN network baud rate
 *
 * \return    maximum timeout for the selected baud rate
 *
 * Implements : lin_calc_max_header_timeout_cnt_Activity
 */
static uint32_t Lin_CalcMaxHeaderTimeoutCnt(uint32_t baudRate)
{
    return ((14U * 34U * 100000U / (TIMER_UNIT_US * baudRate)) + 1U);
}


/*! \brief Computes the maximum response timeout
 *
 * TResponse_Maximum = 1.4 * TResponse_Nominal, TResponse_Nominal = 10 * (NData+ 1) * TBit
 *
  * \param[in] baudRate LIN network baud rate
 *             size     frame size in bytes
 *
 * \return maximum response timeout for the given baud rate and frame size
 *
 * Implements : lin_calc_max_res_timeout_cnt_Activity
 */
static uint32_t Lin_CalcMaxResTimeoutCnt(uint32_t baudRate, uint8_t size)
{
    uint32_t bitNum = 14U * (1U + size);

    return ((bitNum * 1000000U / (TIMER_UNIT_US * baudRate)) + 1U);
}

/*! \brief The lin PID parity.
 * \param[in] PID:Pid or Id
 *            typeAction:
 *            0 : make parity
 *            1 : check parity
 * \return none
 */
uint8_t Lin_PidParity(uint8_t PID, uint8_t typeAction)
{
    uint8_t parity;
    uint8_t retVal;

    parity = (uint8_t)(((0xFFU & (GETBIT(PID, 0U) ^ GETBIT(PID, 1U) ^ GETBIT(PID, 2U) ^ GETBIT(PID, 4U))) << 6U) |
                       ((0xFFU ^ (GETBIT(PID, 1U) ^ GETBIT(PID, 3U) ^ GETBIT(PID, 4U) ^ GETBIT(PID, 5U))) << 7U));

    /* Check if action is checking parity bits */
    if (CHECK_PARITY == typeAction)
    {
        /* If parity bits are incorrect */
        if ((PID & 0xC0U) != parity)
        {
            /* Return 0xFF if parity bits are incorrect */
            retVal = 0xFFU;
        }
        /* If parity bits are correct */
        else
        {
            /* Return ID if parity bits are correct */
            retVal = (uint8_t)(PID & 0x3FU);
        }
    }
    /* If action is making parity bits */
    else
    {
        /* Return PID in case of making parity bits */
        retVal = (uint8_t)(PID | parity);
    }

    return retVal;
}

/*! \brief get lin frame index.
 * \param[in]   frameId: lin frame identifier
 * \return      frame index
 */
uint8_t Lin_GetFrameIndex(uint8_t frameId)
{
    uint8_t i = 0;
    uint8_t retVal = 0xFF;

    for(i = 0; i < LIN_FRAME_NUM; i++)
    {
        if(frameId == linFrameInfoTab[i].frameId)
        {
            retVal = i;
            break;
        }
    }

    return retVal;
}

/*! \brief The lin data checksum function.
 * \param[in] CheckSumType:
 *              0: standard checksum
 *              1: enhanced checksum
 *            pid: frame pid
 *            pData: pointer to the data of Lin_FrameType
 *            dataLen: data length
 * \return none
 */
uint8_t Lin_CalculateChecksum(Lin_CheckSumType checkSumType, const uint8_t frameId, const uint8_t* pData, uint8_t dataLen)
{
    uint16_t tCheckSum = 0;
    uint8_t i = 0;

    if ((checkSumType == LINSTACK_CHECKSUM_ENHANCED) && \
            (frameId != LIN_ID_DIAG_MASTER_REQ) && (frameId != LIN_ID_DIAG_SLAVE_RESP))
    {
        tCheckSum = Lin_PidParity(frameId, MAKE_PARITY);
    }

    for (i = 0; i < dataLen; i++)
    {
        tCheckSum += pData[i];

        tCheckSum = tCheckSum > 0xFF ? ((tCheckSum + 1) & 0x00FF) : tCheckSum;
    }

    tCheckSum = ~tCheckSum;

    return (uint8_t)(tCheckSum & 0xFF);
}

/*! \brief set lin response data.
 * \param[in]   pFrameInfoFromTab: point to the data will be send
 *              pFrame: point to send structure
 * \return none
 */
void lin_SetResponse(Lin_FrameInfoType *pFrameInfoFromTab, Lin_FrameDataType *pFrame)
{
    uint8_t i = 0;

    for(i = 0; i < pFrameInfoFromTab->frameLen; i++)
    {
        if(0 != (pFrameInfoFromTab->updatingFlag & (1 << i)))
        {
            pFrame->data[i] = linFrameDataBackup[i];
        }
        else
        {
            pFrame->data[i] = pFrameInfoFromTab->pFrameData[i];
        }
    }

    pFrame->checkSum = Lin_CalculateChecksum(LINSTACK_CHECKSUM_ENHANCED, pFrameInfoFromTab->frameId, &pFrame->data[0], pFrameInfoFromTab->frameLen);
}

/*! \brief The uart0 for lin send request function.
 * \param[in] none
 * \return none
 */
void LinMastertxIsrRequest(void)
{
    if (linState == LIN_STATE_IDLE)
    {
        UartDrv_SetInterruptReq(&mcu.uartDrv0, UARTDRV_ISR_SRC_TX_SYNC_BREAK, true);
        linComTimeoutCnt = linMaxHeaderTimeout + linMaxFrameResTimeout;
        linState = LIN_STATE_BREAK;
        UartDrv_SendLinBreakField(&mcu.uartDrv0);
    }
}

/*! \brief The uart0 for lin send interrupt handling function.
 * \param[in] none
 * \return none
 */
void LinMasterTxIsrRoutine(void)
{
    if (UartDrv_GetStatus(&mcu.uartDrv0, UARTDRV_STATUS_SBRKD))
    {
        /* The break signal has been transmitted successfully */
        UartDrv_ClearStatus(&mcu.uartDrv0, UARTDRV_STATUS_SBRKD);

        linTxDataCnt = 0;

        /* send the sync-header(0x55) */
        linState = LIN_STATE_SYNC_HEDADER;
        UartDrv_TxData(&mcu.uartDrv0, LIN_SYNC_HEADER);
    }
}

/*! \brief The uart0 for lin receive interrupt handling function.
 * \param[in] none
 * \return none
 */
void LinMasterRxIsrRoutine(void)
{
    uint8_t i = 0;
    uint8_t rcvData = 0;
    uint8_t rcvCheckSum = 0;
    uint8_t clcCheckSum = 0;
    uint8_t pid = 0;
    uint8_t frameId = 0;
    uint8_t txFreeCnt = 0;
    uint8_t rxCnt = 0;

    if (UartDrv_GetStatus(&mcu.uartDrv0, UARTDRV_STATUS_RDRF))
    {
        if (UartDrv_IsInterruptEnabled(&mcu.uartDrv0, UARTDRV_ISR_SRC_RX_FULL) == true)
        {
            switch(linState)
            {
            case LIN_STATE_SYNC_HEDADER:        /* wait to receive lin sync header */
                rcvData = (uint8_t)(UartDrv_RxData(&mcu.uartDrv0) & 0xFF);

                /* Check the sync header */
                if (rcvData == LIN_SYNC_HEADER)
                {
                    pid = Lin_PidParity(linFrameInfoTab[gCurFrameIdx].frameId, MAKE_PARITY);
                    UartDrv_TxData(&mcu.uartDrv0, pid);
                    linState = LIN_STATE_PID;
                }
                else
                {
                    linState = LIN_STATE_IDLE;
                }
                break;
            case LIN_STATE_PID:                 /* wait to receive lin pid */
                rcvData = (uint8_t)(UartDrv_RxData(&mcu.uartDrv0) & 0xFF);
                frameId = Lin_PidParity(rcvData, CHECK_PARITY);
                if(frameId != linFrameInfoTab[gCurFrameIdx].frameId)
                {
                    linState = LIN_STATE_IDLE;
                    break;
                }

                if(LIN_RES_PUB == linFrameInfoTab[gCurFrameIdx].resType)
                {
                    lin_SetResponse(&linFrameInfoTab[gCurFrameIdx], &linTxData);

                    linState = LIN_STATE_TX_DATA;

                    linTxDataCnt = 0;
                    txFreeCnt = 4u - UartDrv_GetTxCounter(&mcu.uartDrv0);
                    for(i = 0; i < txFreeCnt; i++)
                    {
                        UartDrv_TxData(&mcu.uartDrv0, linTxData.data[linTxDataCnt]);
                        linTxDataCnt++;
                        if(linTxDataCnt >= linFrameInfoTab[gCurFrameIdx].frameLen && (linTxDataCnt < 4))
                        {
                            UartDrv_TxData(&mcu.uartDrv0, linTxData.checkSum);
                            linState = LIN_STATE_CHECKSUM;
                            break;
                        }
                    }
                }
                else if(LIN_RES_SUB == linFrameInfoTab[gCurFrameIdx].resType)
                {
                    linRxDataCnt = 0;
                    linState = LIN_STATE_RX_DATA;
                }
                else
                {
                    linState = LIN_STATE_IDLE;
                }
                break;
            case LIN_STATE_TX_DATA:
                rxCnt = UartDrv_GetRxCounter(&mcu.uartDrv0);
                for(i = 0; i < rxCnt; i++)
                {
                    (uint8_t)(UartDrv_RxData(&mcu.uartDrv0) & 0xFF);
                }

                txFreeCnt = 4u - UartDrv_GetTxCounter(&mcu.uartDrv0);
                for(i = 0; i < txFreeCnt; i++)
                {
                    if(linTxDataCnt >= linFrameInfoTab[gCurFrameIdx].frameLen)
                    {
                        UartDrv_TxData(&mcu.uartDrv0, linTxData.checkSum);
                        linState = LIN_STATE_CHECKSUM;
                        break;
                    }
                    else
                    {
                        UartDrv_TxData(&mcu.uartDrv0, linTxData.data[linTxDataCnt]);
                        linTxDataCnt++;
                    }
                }
                break;
            case LIN_STATE_RX_DATA:
                rxCnt = UartDrv_GetRxCounter(&mcu.uartDrv0);
                for(i = 0; i < rxCnt; i++)
                {
                    rcvData = (uint8_t)(UartDrv_RxData(&mcu.uartDrv0) & 0xFF);
                    linRxData.data[linRxDataCnt] = rcvData;
                    linRxDataCnt++;
                    if(linRxDataCnt >= linFrameInfoTab[gCurFrameIdx].frameLen)
                    {
                        linState = LIN_STATE_CHECKSUM;
                        break;
                    }
                }
                break;
            case LIN_STATE_CHECKSUM:                /* wait to receive lin checksum */
                rcvCheckSum = (uint8_t)(UartDrv_RxData(&mcu.uartDrv0) & 0xFF);

                if(LIN_RES_SUB == linFrameInfoTab[gCurFrameIdx].resType)
                {
                    clcCheckSum = Lin_CalculateChecksum(LINSTACK_CHECKSUM_ENHANCED, linFrameInfoTab[gCurFrameIdx].frameId, &linRxData.data[0], linRxDataCnt);
                    if (clcCheckSum == rcvCheckSum)
                    {
                        memcpy(linFrameInfoTab[gCurFrameIdx].pFrameData, &linRxData.data[0], linRxDataCnt);
                    }
                }

                linState = LIN_STATE_IDLE;
                break;
            default:
                UartDrv_ClearRxFifo(&mcu.uartDrv0);
                break;
            }
        }
    }

    if(linState == LIN_STATE_IDLE)
    {
        UartDrv_EnableLineBreakDetect(&mcu.uartDrv0, true);
    }
}

/*! \brief This is a part of SysTick Interrupt Handler for lin timeout check.
 * \param[in] none
 * \return none
 */
void Lin_TimeoutService(void)
{
    switch(linState)
    {
    case LIN_STATE_BREAK:
    case LIN_STATE_SYNC_HEDADER:
    case LIN_STATE_PID:
    case LIN_STATE_TX_DATA:
    case LIN_STATE_RX_DATA:
    case LIN_STATE_CHECKSUM:
        linComTimeoutCnt--;
        if(0 == linComTimeoutCnt)
        {
            linState = LIN_STATE_IDLE;

            UartDrv_EnableLineBreakDetect(&mcu.uartDrv0, true);

            linComTimeoutCnt = linMaxFrameResTimeout;
        }
        break;
    default:
        break;
    }
}

/*! \brief The uart0 lin master init function.
 * \param[in] none
 * \return none
 */
void Uart0_LinMaster_Init(void)
{
    UartDrv_InitCfgType uartInitCfg;

    /* PORT and pins as ALT4 (UART tx rx) */
    PinsDrv_SetMuxModeSel(&mcu.ptc, 2, PINSDRV_MUX_ALT4);       /* Uart rx */
    PinsDrv_SetMuxModeSel(&mcu.ptc, 3, PINSDRV_MUX_ALT4);       /* Uart tx */
    PinsDrv_SetMuxModeSel(&mcu.ptc, 13, PINSDRV_MUX_AS_GPIO);   /* LIN module control */
    PinsDrv_SetPinDirection(&mcu.ptc, 13, 1);                   /* set to output */
    PinsDrv_WritePin(&mcu.ptc, 13, 1);                          /* set to high to active lin transceiver */

    /* Configure uart module */
    UartDrv_GetDefaultConfig(&uartInitCfg);
    uartInitCfg.basicParameters.baudRate = LIN_BAUD_RATE;
    uartInitCfg.extendFeatures.cosplayer = UARTDRV_COSPLAY_LIN;
    ClockDrv_GetFreq(&mcu.clockDrv, CLOCKDRV_UART0, &uartInitCfg.basicParameters.busClockFreq);
    /* Set the configuration */
    UartDrv_SetConfig(&mcu.uartDrv0, &uartInitCfg);

    UartDrv_SetRxWater(&mcu.uartDrv0, 0);
    UartDrv_SetRxWater(&mcu.uartDrv0, 0);

    UartDrv_SetInterruptReq(&mcu.uartDrv0, UARTDRV_ISR_SRC_TX_SYNC_BREAK, true);
    UartDrv_SetInterruptReq(&mcu.uartDrv0, UARTDRV_ISR_SRC_RX_FULL, true);

    /* Enable all the uart0 interrupts in the NVIC interrupt controller */
    IrqDrv_EnableIrq(UART0_RxTx_IRQn);

    linMaxHeaderTimeout = Lin_CalcMaxHeaderTimeoutCnt(LIN_BAUD_RATE);
    linMaxFrameResTimeout = Lin_CalcMaxResTimeoutCnt(LIN_BAUD_RATE, 8);

    linComTimeoutCnt = linMaxHeaderTimeout + linMaxFrameResTimeout;

    linState = LIN_STATE_IDLE;
}

void LIN_UpdateTxData(uint8_t frameid,uint8_t dataid,uint8_t data)
{
    linFrameDataBackup[dataid] = linFrameInfoTab[frameid].pFrameData[dataid];
    linFrameInfoTab[frameid].updatingFlag |= (0x01 << dataid);
    linFrameInfoTab[frameid].pFrameData[dataid] = data;
    linFrameInfoTab[frameid].updatingFlag &= ~(0x01 << dataid);
}

void LIN_Master_task(void)
{
    //LIN_UpdateTxData()

    /* request send LIN data */
    LinMastertxIsrRequest();
    /*
    gCurFrameIdx++;
    if(gCurFrameIdx >= LIN_FRAME_NUM)
    {
        gCurFrameIdx = 0;
    }
    */
}

/*! \brief The uart0 interrupt handling function.
 * \param[in] none
 * \return none
 */
void UART0_RxTx_Handler(void)
{
    LinMasterTxIsrRoutine();
    LinMasterRxIsrRoutine();
}

