/**
 * @file hv_app_DdcciOTA.c
 * @brief ddcci ota c file.
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2023-01-31
 * @copyright Copyright(c),2022-8, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author       <th>Date            <th>Change Description
 * <tr><td>HiView SoC Software Team     <td>2024-01-10      <td>create
 * </table>
 */


#include "hv_mw_Ddcci.h"
#include "hv_app_Ddcci.h"
#include "hv_app_DdcciOTA.h"
#include "hv_comm_DataBase.h"
#include "hv_comm_MemParser.h"
#include "hv_mw_Hdmi.h"
#include "hv_mw_DisplayPort.h"
#include "hv_drv_Flash.h"
#include "OtaConfig.h"
#include "hv_comm_Ota.h"
#include "hv_drv_Watchdog.h"


//--------------------------------------------------
// Definitions of DDCCI OTA Protocol Items
//--------------------------------------------------
#define OTA_DDCCI_SOURCE            0
#define OTA_DDCCI_LEN               1
#define OTA_DDCCI_CMD               2
#define OTA_DDCCI_OPCODE            3
#define OTA_DDCCI_PKT_NUM           4
#define OTA_DDCCI_RESULT_CODE       5
#define OTA_DDCCI_CRC               6

#define OTA_DDCCI_START_ADDR0       4
#define OTA_DDCCI_START_ADDR1       5
#define OTA_DDCCI_START_ADDR2       6
#define OTA_DDCCI_START_ADDR3       7
#define OTA_DDCCI_START_LEN0        8
#define OTA_DDCCI_START_LEN1        9
#define OTA_DDCCI_START_LEN2        10
#define OTA_DDCCI_START_LEN3        11

#define OTA_DDCCI_DATA_START        5

#define OTA_DDCCI_NULL_MSG_LEN      3
#define OTA_DDCCI_RSP_MSG_LEN       7

#define RESULT_CODE_SUCCESS         0xE0
#define RESULT_CODE_FAILURE         0xCC
#define RESULT_CODE_OTA_OK          0xAB


#define OTA_DDCCI_WRITE_LEN         (12 * 1024)
#define OTA_DP_BUFLEN               (3 * 1024)


/**
 * @brief Structure of ddcci ota global data.
 */
typedef struct{
    BOOL                bDdcciOtaMode;                    //Indicate the OTA state.
    UCHAR8              ucPacketNum;                      //Indicate the packet number, always 0-255.
    DdcciPortEnum       enPort;                           //Indicate input port
    UINT32              uiReceived;                       //Indicates the length of data that has been received and saved, max is OTA_DDCCI_WRITE_LEN.
    UCHAR8              aucOTAData[OTA_DDCCI_WRITE_LEN];  //Data buffer, write flash with page units.
    UINT32              uiFlashAddr;                      //Indicates the flash address to do ota.
    UINT32              uiBinLen;                         //Indicates the total length of ota bin.
    UINT32              uiAddrOffset;                     //Indicates the offset address of uiFlashAddr.
    OtaPartDef          enPart;                           //Indicates the ota partition(A or B).
}DdcciOTAData;

//上位机发送GET_PART来获取当前要升级的分区，然后根据分区标志来设置E5命令中的长度信息
#define DDCCI_OPCODE_VCP_OTA_GET_PART                   0xE4
//上位机发送START来启动升级，下位机要做出包含分区标志的回复
#define DDCCI_OPCODE_VCP_OTA_START                      0xE5
//数据传输，每256字节写一次flash
#define DDCCI_OPCODE_VCP_OTA_TRANS                      0xE6
//完成或取消命令
#define DDCCI_OPCODE_VCP_OTA_STOP                       0xE7
//下位机执行擦操作时不会响应上位机，所以上位机会循环发送该命令直到收到回复以表征擦除完成
#define DDCCI_OPCODE_VCP_OTA_CHECK_ERASE_OK             0xE8


static DdcciOTAData s_stDdcciOtaParam = {0};
static UCHAR8 s_aucCodeCrc[OTA_SBLK_CRC_LEN] = {0};


/**
 * @brief Some preparation for the OTA.
 * @param[in] pucData Pointer to DDCCI_START data buffer.
 * @return Status HV_SUCCESS:OTA can be performed, HV_FAILURE:OTA cann't be performed.
 */
static Status Hv_App_Ddcci_PrepareOTA(UCHAR8 *pucData)
{
    s_stDdcciOtaParam.enPart = Hv_Comm_OTA_GetUpgradePart();
    if(OTA_PART_MAX == s_stDdcciOtaParam.enPart)
    {
        HV_LOG_DEBUG(DDCCI,"Got part error, cann't do ddcci ota");
        s_stDdcciOtaParam.bDdcciOtaMode = HV_FALSE;
        return HV_FAILURE;
    }
    
    s_stDdcciOtaParam.bDdcciOtaMode = HV_TRUE;
    s_stDdcciOtaParam.ucPacketNum = 0;
    s_stDdcciOtaParam.uiReceived = 0;
    s_stDdcciOtaParam.uiAddrOffset = 0;
    HV_MEMSET(s_stDdcciOtaParam.aucOTAData, 0xFF, OTA_DDCCI_WRITE_LEN);
    if(OTA_PART_A == s_stDdcciOtaParam.enPart)
    {
        s_stDdcciOtaParam.uiFlashAddr = OTA_PART_A_ADDR;
    }
    else
    {
        s_stDdcciOtaParam.uiFlashAddr = OTA_PART_B_ADDR;
    }
    s_stDdcciOtaParam.uiBinLen = (pucData[OTA_DDCCI_START_LEN3] 
                                     | (pucData[OTA_DDCCI_START_LEN2] << 8) 
                                     | (pucData[OTA_DDCCI_START_LEN1] << 16) 
                                     | (pucData[OTA_DDCCI_START_LEN0] << 24));

    HV_LOG_INFO(DDCCI,"enPart:%d, uiFlashAddr:%x, uiBinLen:%x", s_stDdcciOtaParam.enPart, s_stDdcciOtaParam.uiFlashAddr, s_stDdcciOtaParam.uiBinLen);

    //Inform OSD?
    
    return HV_SUCCESS;
}

/**
 * @brief Handle the OTA transfer process.
 * @param[in] pucData Pointer to the commging message data.
 * @param[in] ucLen Indicates the length of message.
 * @return Status HV_SUCCESS:packet received ok, HV_FAILURE:something wrong.
 */
static Status Hv_App_Ddcci_TransferOTA(UCHAR8 *pucData, UCHAR8 ucLen)
{
    USHORT16 usValidDataLen = 0;
    UCHAR8 ucRemainsLen = 0;
    UCHAR8 aucBuf[OTA_DP_BUFLEN] = {0};
    UCHAR8 ucCRC = 0;
    USHORT16 usBurst = 0;

    /* check packet number */
    if(pucData[OTA_DDCCI_PKT_NUM] != s_stDdcciOtaParam.ucPacketNum)
    {
        return HV_FAILURE;
    }

    if ((s_stDdcciOtaParam.enPort == DDCCI_PORT_DP0) || (s_stDdcciOtaParam.enPort == DDCCI_PORT_DP1))
    {
        if(s_stDdcciOtaParam.uiReceived + OTA_DP_BUFLEN < s_stDdcciOtaParam.uiBinLen)
        {
            usValidDataLen = OTA_DP_BUFLEN;
        }
        else
        {
            usValidDataLen = s_stDdcciOtaParam.uiBinLen - s_stDdcciOtaParam.uiReceived;
        }
        Hv_Common_ReadRegBytes(aucBuf, RX_EDP_RX_DPCD_EDID_00_00, 512);
        Hv_Common_ReadRegBytes(&aucBuf[512], RX_EDP_RX_DPCD_MSGAUX_DRQ_00_00, 512);
        Hv_Common_ReadRegBytes(&aucBuf[512*2], HV_EDP_RX_MSGAUX_URQ_SW_00_BASE_ADDRESS, 512);
        Hv_Common_ReadRegBytes(&aucBuf[512*3], RX_EDP_RX_DPCD_EDID_01_00, 512);
        Hv_Common_ReadRegBytes(&aucBuf[512*4], RX_EDP_RX_DPCD_MSGAUX_DRQ_01_00, 512);
        Hv_Common_ReadRegBytes(&aucBuf[512*5], HV_EDP_RX_MSGAUX_URQ_SW_01_BASE_ADDRESS, 512);
        for (UINT32 uiIndex = 0; uiIndex < OTA_SBLK_CRC_LEN; uiIndex++)
        {
            s_aucCodeCrc[uiIndex] = aucBuf[usValidDataLen - OTA_SBLK_CRC_LEN + uiIndex];
        }
    }
    else
    {
        /* HDMI To Do */
    }

    /* check crc */
    for (UINT32 uiIndex = 0; uiIndex < usValidDataLen; uiIndex++)
    {
        ucCRC = ucCRC ^ aucBuf[uiIndex];
    }
    if(ucCRC != pucData[OTA_DDCCI_PKT_NUM + 1])
    {
        HV_LOG_ERROR(DP, "%d %d %d", usValidDataLen, ucCRC, pucData[OTA_DDCCI_PKT_NUM + 1]);
        return HV_FAILURE;
    }

    usBurst = (s_stDdcciOtaParam.uiReceived % OTA_DDCCI_WRITE_LEN);
    /* got data */
    if(usBurst + usValidDataLen <= OTA_DDCCI_WRITE_LEN)
    {
        HV_MEMCPY(&s_stDdcciOtaParam.aucOTAData[usBurst], aucBuf, usValidDataLen);
        s_stDdcciOtaParam.uiReceived += usValidDataLen;
        ucRemainsLen = 0;
    }
    else
    {
        ucRemainsLen = usBurst + usValidDataLen - OTA_DDCCI_WRITE_LEN;
        HV_MEMCPY(&s_stDdcciOtaParam.aucOTAData[usBurst], aucBuf, OTA_DDCCI_WRITE_LEN-usBurst);
        s_stDdcciOtaParam.uiReceived += (OTA_DDCCI_WRITE_LEN-usBurst);
    }

    if ((usBurst + usValidDataLen >= OTA_DDCCI_WRITE_LEN) || (s_stDdcciOtaParam.uiReceived >= s_stDdcciOtaParam.uiBinLen))
    {
        Hv_Drv_Flash_OnlyWrite(s_stDdcciOtaParam.uiFlashAddr + s_stDdcciOtaParam.uiAddrOffset, s_stDdcciOtaParam.aucOTAData, OTA_DDCCI_WRITE_LEN);
        s_stDdcciOtaParam.uiAddrOffset += OTA_DDCCI_WRITE_LEN;
    }

    /* 拷贝剩余的数据放到下一个buffer的起始位置 */
    if(ucRemainsLen > 0)
    {
        HV_MEMSET(s_stDdcciOtaParam.aucOTAData, 0xFF, OTA_DDCCI_WRITE_LEN);
        HV_MEMCPY(s_stDdcciOtaParam.aucOTAData, &aucBuf[usValidDataLen-ucRemainsLen], ucRemainsLen);
        s_stDdcciOtaParam.uiReceived += ucRemainsLen;
        ucRemainsLen = 0;
    }

    s_stDdcciOtaParam.ucPacketNum++;

    return HV_SUCCESS;
}

VOID Hv_App_Ddcci_ProcessOTA(UCHAR8 ucSource, UCHAR8 *pucData, UCHAR8 ucLen)
{
    UCHAR8 ucOtaOpCode = pucData[OTA_DDCCI_OPCODE];
    UCHAR8 aucRspData[OTA_DDCCI_RSP_MSG_LEN + 1] = {0};

    // DDC_BUF_PRINT("req: ", pucData, ucLen);
    aucRspData[OTA_DDCCI_SOURCE] = DDCCI_DEST_ADDRESS;
    aucRspData[OTA_DDCCI_LEN] = (DDCCI_CONTROL_STATUS_FLAG | OTA_DDCCI_RSP_MSG_LEN) - DDCCI_HEADER_LEN;
    aucRspData[OTA_DDCCI_CMD] = DDCCI_CMD_MANUFACTURER_FW_UPDATE;
    aucRspData[OTA_DDCCI_OPCODE] = ucOtaOpCode;
    aucRspData[OTA_DDCCI_PKT_NUM] = 0x00;
    aucRspData[OTA_DDCCI_RESULT_CODE] = RESULT_CODE_SUCCESS;
    
    switch(ucOtaOpCode)
    {
        case DDCCI_OPCODE_VCP_OTA_GET_PART:
        {
            HV_LOG_INFO(DDCCI,"OTA Get Part");
            s_stDdcciOtaParam.enPort = ucSource;
            s_stDdcciOtaParam.enPart = Hv_Comm_OTA_GetUpgradePart();
            if(OTA_PART_MAX == s_stDdcciOtaParam.enPart)
            {
                HV_LOG_DEBUG(DDCCI,"Got part error, cann't do ddcci ota");
                s_stDdcciOtaParam.bDdcciOtaMode = HV_FALSE;
                aucRspData[OTA_DDCCI_RESULT_CODE] = RESULT_CODE_FAILURE;
            }
            aucRspData[OTA_DDCCI_PKT_NUM] = s_stDdcciOtaParam.enPart;
            aucRspData[OTA_DDCCI_PKT_NUM + 1] = 1; /* 1 for HT7315 */
            aucRspData[OTA_DDCCI_PKT_NUM + 2] = RESULT_CODE_SUCCESS;
            aucRspData[OTA_DDCCI_LEN] = (DDCCI_CONTROL_STATUS_FLAG | (OTA_DDCCI_RSP_MSG_LEN + 1)) - DDCCI_HEADER_LEN;
            aucRspData[OTA_DDCCI_CRC + 1] = Hv_App_Ddcci_CalCheckSum(aucRspData);
            Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN + 1);
            if ((ucSource == DDCCI_PORT_DP0) || (ucSource == DDCCI_PORT_DP1))
            {
                Hv_Mw_DpRx_EnableApbWr(HV_TRUE);
            }
            Hv_Drv_WDG_Disable();
            break;
        }
        case DDCCI_OPCODE_VCP_OTA_START:
        {
            HV_LOG_INFO(DDCCI,"OTA Start");
            if(Hv_App_Ddcci_PrepareOTA(pucData) != HV_SUCCESS)
            {
                aucRspData[OTA_DDCCI_RESULT_CODE] = RESULT_CODE_FAILURE;
            }
            aucRspData[OTA_DDCCI_PKT_NUM] = s_stDdcciOtaParam.enPart;
            aucRspData[OTA_DDCCI_CRC] = Hv_App_Ddcci_CalCheckSum(aucRspData);
            Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN);
            //HV_LOG_BUF_INFO("E5 rsp: ",aucRspData,OTA_DDCCI_RSP_MSG_LEN);
            HV_MEMSET(s_aucCodeCrc, 0, OTA_SBLK_CRC_LEN);
            if(RESULT_CODE_SUCCESS == aucRspData[OTA_DDCCI_RESULT_CODE])
            {
                HV_LOG_INFO(DDCCI,"Wait flash erase.....");
                /* Do erase flash by addr & aligned 4k length */
                Hv_Drv_Flash_OnlyErase(s_stDdcciOtaParam.uiFlashAddr, ALIGN_4K(s_stDdcciOtaParam.uiBinLen));
                HV_LOG_INFO(DDCCI,"flash erase ok");
            }
            break;
        }
        case DDCCI_OPCODE_VCP_OTA_TRANS:
        {
            if(HV_TRUE == s_stDdcciOtaParam.bDdcciOtaMode)
            {
                HV_LOG_DEBUG(DDCCI,"Trans:%d", pucData[OTA_DDCCI_PKT_NUM]);
                if(Hv_App_Ddcci_TransferOTA(pucData, ucLen) != HV_SUCCESS)
                {
                    aucRspData[OTA_DDCCI_RESULT_CODE] = RESULT_CODE_FAILURE;
                }
                aucRspData[OTA_DDCCI_PKT_NUM] = pucData[OTA_DDCCI_PKT_NUM];
                aucRspData[OTA_DDCCI_CRC] = Hv_App_Ddcci_CalCheckSum(aucRspData);
                Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN);
                //HV_LOG_BUF_INFO("E6 rsp: ",aucRspData,OTA_DDCCI_RSP_MSG_LEN);
            }
            break;
        }
        case DDCCI_OPCODE_VCP_OTA_STOP:
        {
            HV_LOG_INFO(DDCCI,"OTA Stop");
            //运行中，需要判断接收到的数据长度是否足够，如果不足则停止，如果收完了则更新分区标志，且总是要清空变量
            if(HV_TRUE == s_stDdcciOtaParam.bDdcciOtaMode)
            {
                //写入剩余数据
                if(s_stDdcciOtaParam.uiAddrOffset < s_stDdcciOtaParam.uiReceived)
                {
                    Hv_Drv_Flash_OnlyWrite(s_stDdcciOtaParam.uiFlashAddr + s_stDdcciOtaParam.uiAddrOffset, s_stDdcciOtaParam.aucOTAData, OTA_DDCCI_WRITE_LEN);
                    s_stDdcciOtaParam.uiAddrOffset = s_stDdcciOtaParam.uiReceived;
                }
            
                if(s_stDdcciOtaParam.uiAddrOffset >= s_stDdcciOtaParam.uiBinLen)
                {
                    HV_LOG_INFO(DDCCI,"OTA Success!");
                    Hv_Comm_OTA_UpdateParams(s_aucCodeCrc, OTA_SBLK_CRC_LEN);
                    aucRspData[OTA_DDCCI_RESULT_CODE] = RESULT_CODE_OTA_OK;
                }
            }
            //其余情况收到STOP，只清除状态变量，且总是回复SUCCESS
            s_stDdcciOtaParam.bDdcciOtaMode = HV_FALSE;
            s_stDdcciOtaParam.ucPacketNum = 0;
            s_stDdcciOtaParam.uiReceived = 0;
            s_stDdcciOtaParam.enPart = OTA_PART_MAX;
            s_stDdcciOtaParam.uiAddrOffset = 0;
            s_stDdcciOtaParam.uiFlashAddr = 0;
            s_stDdcciOtaParam.uiBinLen = 0;

            aucRspData[OTA_DDCCI_CRC] = Hv_App_Ddcci_CalCheckSum(aucRspData);
            Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN);
            Hv_Drv_WDG_Enable();
            //HV_LOG_BUF_INFO("E7 rsp: ",aucRspData,OTA_DDCCI_RSP_MSG_LEN);
            break;
        }
        case DDCCI_OPCODE_VCP_OTA_CHECK_ERASE_OK:
        {
            if(HV_TRUE == s_stDdcciOtaParam.bDdcciOtaMode)
            {
                HV_LOG_INFO(DDCCI,"OTA Check Erase Ok");
                aucRspData[OTA_DDCCI_RESULT_CODE] = RESULT_CODE_SUCCESS;
                aucRspData[OTA_DDCCI_CRC] = Hv_App_Ddcci_CalCheckSum(aucRspData);
                Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN);
                //HV_LOG_BUF_INFO("E8 rsp: ",aucRspData,OTA_DDCCI_RSP_MSG_LEN);
            }
            break;
        }
        default:
        {
            Hv_App_Ddcci_SendNullMsg(ucSource);
            HV_LOG_INFO(DDCCI,"default");
            break;
        }
    }
}

BOOL Hv_App_Ddcci_GetOtaRunFlag(VOID)
{
    return s_stDdcciOtaParam.bDdcciOtaMode;
}

