/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2023. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */
/******************************************************************************
 * @version: V1.0.0 demo
 ******************************************************************************/


/*!
* @file lin.c
*
* @brief This file contains the user lin function.
*
*/

/* ===========================================  Includes  =========================================== */
#include "lin.h"
#include "ac780x_gpio.h"
#include "ac7803x.h"
#include "ac780x_uart.h"
#include "ac780x_lin.h"
#include "ac780x_timer.h"
#include <string.h>

/* ===========================================  Typedef  ============================================ */

/* ==========================================  Variables  =========================================== */
/* Signal data */
static uint8_t s_linData1[1] = {0xA5};
static uint8_t s_linData2[2] = {0xFF, 0x00};
static uint8_t s_linData4[4] = {0xAA, 0x55, 0xAA, 0x55};
static uint8_t s_linData8[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
static uint8_t s_linClassicPID[4] = {0x0}; /*PID for classic checksums*/

static lin_cur_node_t s_linCurNode = {0U};           /* LIN current state */
static lin_user_config_t s_linUserConfig = {0U};     /* LIN user configuration */
static lin_state_t s_linStateUser = {0U};            /* LIN state */
static uint8_t s_firstFlag = 1U;                     /* First flag for master */

/* Master send header and receive data */
static lin_frame_info_t s_linFrameTable0[] =
{
    {0x01, LIN_DIR_TX, LIN_CLASSIC_CHECKSUM, 1, 60, &s_linData1[0]},
    {0x02, LIN_DIR_RX, LIN_CLASSIC_CHECKSUM, 2, 60, &s_linData2[0]},
    {0x03, LIN_DIR_TX, LIN_CLASSIC_CHECKSUM, 4, 60, &s_linData4[0]},
    {0x04, LIN_DIR_RX, LIN_CLASSIC_CHECKSUM, 8, 60, &s_linData8[0]},
};

/* Slave receive header and response data */
static lin_frame_info_t s_linFrameTable1[] =
{
    {0x01, LIN_DIR_RX, LIN_CLASSIC_CHECKSUM, 1, 60, &s_linData1[0]},
    {0x02, LIN_DIR_TX, LIN_CLASSIC_CHECKSUM, 2, 60, &s_linData2[0]},
    {0x03, LIN_DIR_RX, LIN_CLASSIC_CHECKSUM, 4, 60, &s_linData4[0]},
    {0x04, LIN_DIR_TX, LIN_ENHANCED_CHECKSUM, 8, 60, &s_linData8[0]},
};

/* Schedule table */
static lin_schedule_table_t s_linScheduleTable[] =
{
    {4, &s_linFrameTable0[0]},
    {4, &s_linFrameTable1[0]},
};

/* ====================================  Functions declaration  ===================================== */

/* ======================================  Functions define  ======================================== */
/*!
 * @brief       LIN_InitGPIO
 *              Pin initialization
 * @param[in]  none
 *
 * @return int
 */
static void LIN_InitGPIO(void)
{
    /*LIN transceiver sleep pin initialization */
    GPIO_SetFunc(LIN_SLEEP_GPIO, LIN_SLEEP_PIN, GPIO_FUN0);
    GPIO_SetDir(LIN_SLEEP_GPIO, LIN_SLEEP_PIN, GPIO_OUT);
    GPIO_SetPinLevel(LIN_SLEEP_GPIO, LIN_SLEEP_PIN, GPIO_LEVEL_HIGH);

    /*LIN TX RX pin multiplexing function*/
    GPIO_SetFunc(LIN_TX_GPIO, LIN_TX_PIN, GPIO_FUN3);
    GPIO_SetFunc(LIN_RX_GPIO, LIN_RX_PIN, GPIO_FUN3);
}

/*!
 * @brief     LIN_IntCallback
 *            user LIN interrupt callback function.
 * @param[in] instance: LIN instance
 * @param[in] linState: pointer to LIN state
 * @return none
 */
void LIN_IntCallback(uint8_t instance, lin_state_t *linState)
{
    lin_cur_node_t *linCurNode = &s_linCurNode;
    lin_frame_info_t *frameInfo = NULL;
    uint8_t index = 0U, id, responseFlag = 0U;

    switch (linState->currentEventId)
    {
    case LIN_RECV_BREAK_FIELD_OK:
        //printf("S\r\n");
        break;

    case LIN_PID_OK:
        if ((bool)MASTER == s_linUserConfig.nodeFunction)
        {
            frameInfo = &linCurNode->schedule[linCurNode->frameIdx];
            responseFlag = 1U;
        }
        else
        {
            id = linState->currentId;
            /* Find the needed id */
            for (index = 0U; index < linCurNode->frameNum; index++)
            {
                frameInfo = &linCurNode->schedule[index];
                if (id == frameInfo->id)
                {
                    linCurNode->frameIdx = index;
                    break;
                }
            }
            if (index != linCurNode->frameNum)
            {
                responseFlag = 1U;
            }
            else /*No need to process this id, goto idlestate*/
            {
                (void)LIN_DRV_GotoIdleState(instance);
//                printf("[LIN%d]Err id\r\n", instance);
            }
        }

        if (responseFlag)
        {
            LIN_DRV_SetTimeoutCounter(instance, LIN_TIMEOUT);
            /* Check response direction */
            if (LIN_DIR_TX == frameInfo->dir)
            {
                /* Send response */
                (void)LIN_DRV_SendFrameData(instance, frameInfo->data, frameInfo->length);
            }
            else
            {
                /* Receive response */
                (void)LIN_DRV_ReceiveFrameData(instance, frameInfo->data, frameInfo->length);
            }
        }
        //printf("%x\r\n", id);
        break;

    case LIN_RX_COMPLETED:
    case LIN_TX_COMPLETED:
        //printf("E\r\n");
        break;

    case LIN_SYNC_ERROR:
    case LIN_PID_ERROR:
    case LIN_FRAME_ERROR:
    case LIN_READBACK_ERROR:
    case LIN_CHECKSUM_ERROR:
    case LIN_RX_OVERRUN:
//        printf("[LIN%d]err:%x\r\n", instance, linState->currentEventId);
        break;

    case LIN_WAKEUP_SIGNAL:
//        printf("[LIN%d]Recv wakeup\r\n", instance);
        break;

    default:
        break;
    }
}

/*!
 * @brief       LIN_Init
 *              LIN module and pin initialization required for LIN module
 *              LIN is configured as master mode, and Baudrate is 19200
 * @param[in]   instance: LIN module instance
 * @param[in]   mode: Configure the lin node to either host mode or slave mode
 * @param[in]   tblIdx
 * @param[in]   baudrate: LIN module baudrate
 * @return      none
 *
 */
void LIN_Init(uint8_t instance, uint8_t mode, uint8_t tblIdx, uint16_t baudrate)
{
    uint8_t i,j = 0;
    lin_cur_node_t *linCurNode = &s_linCurNode;

    printf("[LIN%d]mode:%d schtable:%d baudrate:%d\r\n", instance, mode, tblIdx, baudrate);

    s_linUserConfig.nodeFunction = (bool)mode;                  /*master mode*/
    s_linUserConfig.baudRate = baudrate;                        /*lin baudrate*/
    s_linUserConfig.breakLength = LIN_BREAK_LENGTH;             /*lin break length*/
    s_linUserConfig.breakThreshold = LIN_BREAK_THRESHOLD;       /*lin break length detection bits*/
    s_linUserConfig.classicPID = s_linClassicPID;               /*lin classic checksum id buffer*/
    s_linUserConfig.numOfClassicPID = 0;                        /*lin classic checksum id number*/

    linCurNode->schTblIdx = tblIdx;                             /*schedule table index*/
    linCurNode->frameNum = s_linScheduleTable[tblIdx].frameNum; /*schedule table frame number*/
    linCurNode->schedule = s_linScheduleTable[tblIdx].schedule; /*schedule table pointer*/

    s_linStateUser.Callback = (lin_callback_t)LIN_IntCallback;    /*user lin callback function*/

    for(i = 0; i < linCurNode->frameNum; i++)
    {
        if(LIN_CLASSIC_CHECKSUM == linCurNode->schedule[i].cst)
        {
            s_linUserConfig.classicPID[j++] = LIN_DRV_ProcessParity(linCurNode->schedule[i].id, (uint8_t)MAKE_PARITY);
            s_linUserConfig.numOfClassicPID++;
        }
    }

    LIN_InitGPIO();     /*Pin initialization*/

    (void)LIN_DRV_Init(instance, &s_linUserConfig, &s_linStateUser);
}

/*!
 * @brief Sends the next message frame on the current schedule table.
 *
 * @param[in] instance: LIN instance
 * @return : entry index
 */
uint8_t LIN_SchTick(uint8_t instance)
{
    lin_cur_node_t *linCurNode = &s_linCurNode;
    lin_frame_info_t *frameTable = linCurNode->schedule;
    status_t ret = STATUS_SUCCESS;

    /* Decrease the delay time */
    if (linCurNode->delay >= LIN_TIME_BASE)
    {
        linCurNode->delay -= LIN_TIME_BASE;
    }
    /* If the delay time delay has expired, send the next frame */
    if (0U == linCurNode->delay)
    {
        if (s_firstFlag)
        {
            s_firstFlag = 0U;
        }
        else if (linCurNode->frameIdx < (linCurNode->frameNum - 1U))    /* Send the next frame */
        {
            linCurNode->frameIdx++;
        }
        else    /* The current schedule is end */
        {
            linCurNode->frameIdx = 0U;
        }
        linCurNode->delay = frameTable->delayTime;
        ret = LIN_DRV_MasterSendHeader(instance, frameTable[linCurNode->frameIdx].id);

        if (STATUS_SUCCESS != ret)
        {
            printf("send break faild , ret is %d\r\n", ret);
            (void)LIN_DRV_GotoIdleState(instance); /*Goto LIN idle state, clear lin status*/
        }
    }

    return linCurNode->frameIdx;
}

/*!
 * @brief Timer handle function for master schedule table tick time.
 *
 * @param[in] device: timer device
 * @param[in] wpara: interrupt flag
 * @param[in] lpara: parameter, always be 0
 * @return : none
 */
void LIN_TimerHandle(void *device, uint32_t wpara, uint32_t lpara)
{
    (void)LIN_SchTick(LIN_INSTANCE);
    LIN_DRV_TimeoutService(LIN_INSTANCE);
}

/*!
 * @brief LIN_InitScheduleTimer
 *        Initialize timer.
 *
 * @param[in] time: time value  ms
 * @return : none
 */
void LIN_InitScheduleTimer(uint32_t time)
{
    TIMER_ConfigType cfg;
    memset((void *)&cfg, 0, sizeof(TIMER_ConfigType));

    cfg.timeMode = TIMER_WORKMODE_TIMER; /*TIMER channel mode set Periodic Counter mode*/
    cfg.chainModeEn = DISABLE; /*TIMER channel chainmode disable*/
    cfg.bypEn = ENABLE;        /*TIMER channel bypass disable*/
    cfg.periodValue = 8000U * time - 1; /*TIMER channel period value , ms x timer */
    cfg.cvalReadSel = TIMER_BCLK_FCLK_4; /*TIMER channel read cval precision selection ,BUSCLK/FUNCTION CLK >= 4*/
    cfg.interruptEn = ENABLE; /*TIMER channel interrupt enable*/
    cfg.callback = LIN_TimerHandle; /*TIMER channel callback pointer*/

    TIMER_Init(TIMER_CLKSR_HSI);
    TIMER_InitChannel(TIMER_CHANNEL0, &cfg);
}

/* =============================================  EOF  ============================================== */
