/**
 * @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"
#include "hv_chip_Driver.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_PKT_NUM_HDMI      4
#define OTA_DDCCI_RESULT_CODE_HDMI  8
#define OTA_DDCCI_CRC_HDMI          9

#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 OTA_DDCCI_RSP_MSG_LEN_HDMI  10

#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               (4 * 1024)
#define OTA_HDMI_BUFLEN             (14)

#define CHIP_HT7315                 1
#define HV_MEMORY_CONFIG_OTA_START  PHY_TO_VDM(0x3C00000)

typedef enum _DdcciOTAType
{
    DDCCI_OTA_DUAL_TYPE = 0,
    DDCCI_OTA_SIGL_TYPE = 1,
    DDCCI_OTA_SHORT_PATCH = 2
}DdcciOTAType;

/**
 * @brief Structure of ddcci ota global data.
 */
typedef struct{
    BOOL                bDdcciOtaMode;                    //Indicate the OTA state.
    UINT32              uiPacketNum;                      //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 or the Short Patch Addr.
    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).
    DdcciOTAType        enOTAType;                        //Indicates DUAL or Signal Or Short Patch
    UCHAR8              ucWrStatus;                       //Indicates result of write step
}DdcciOTAData;

//数据传输，HDMI通过DDCCI传输，一次传输16bytes，不足补0
#define DDCCI_OPCODE_VCP_OTA_TRANS_HDMI                 0xE1
//获取版本信息
#define DDCCI_OPCODE_VCP_OTA_GET_VER                    0xE3
//上位机发送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_WRITE                      0xED
//下位机执行擦操作时不会响应上位机，所以上位机会循环发送该命令直到收到回复以表征擦除完成
#define DDCCI_OPCODE_VCP_OTA_CHECK_ERASE_OK             0xE8
//完成所有校验结束升级
#define DDCCI_OPCODE_VCP_OTA_REBOOT                     0xEA
//获取有效block个数
#define DDCCI_OPCODE_VCP_OTA_GET_VALID_BLK              0xEB
//获取block
#define DDCCI_OPCODE_VCP_OTA_GET_BLK                    0xEC
//更新CRC
#define DDCCI_OPCODE_VCP_OTA_STOP                       0xE7

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


static VOID Hv_App_Ddcci_PrepareBLK(UCHAR8 index, UCHAR8 *pucData)
{
    pucData[OTA_DDCCI_PKT_NUM + 0] = index;
    pucData[OTA_DDCCI_PKT_NUM + 2] = *(UCHAR8 *)(HV_MEMORY_CONFIG_OTA_START + index*SBLK_PER_SIZE + 4);
    pucData[OTA_DDCCI_PKT_NUM + 3] = *(UCHAR8 *)(HV_MEMORY_CONFIG_OTA_START + index*SBLK_PER_SIZE + 5);
    pucData[OTA_DDCCI_PKT_NUM + 4] = *(UCHAR8 *)(HV_MEMORY_CONFIG_OTA_START + index*SBLK_PER_SIZE + 6);
    pucData[OTA_DDCCI_PKT_NUM + 5] = *(UCHAR8 *)(HV_MEMORY_CONFIG_OTA_START + index*SBLK_PER_SIZE + 7);
    pucData[OTA_DDCCI_PKT_NUM + 6] = *(UCHAR8 *)(HV_MEMORY_CONFIG_OTA_START + index*SBLK_PER_SIZE + 12);
    pucData[OTA_DDCCI_PKT_NUM + 7] = *(UCHAR8 *)(HV_MEMORY_CONFIG_OTA_START + index*SBLK_PER_SIZE + 13);
    pucData[OTA_DDCCI_PKT_NUM + 8] = *(UCHAR8 *)(HV_MEMORY_CONFIG_OTA_START + index*SBLK_PER_SIZE + 14);
    pucData[OTA_DDCCI_PKT_NUM + 9] = *(UCHAR8 *)(HV_MEMORY_CONFIG_OTA_START + index*SBLK_PER_SIZE + 15);

    return;
}

UCHAR8 HV_App_Ddcci_GetValidSBLK()
{
    UCHAR8 ucValidBLK = 0;

    Hv_Drv_Flash_OnlyRead(HV_FLASH_CONFIG_SUPERBLOCK_START, (UCHAR8 *)HV_MEMORY_CONFIG_OTA_START, HV_FLASH_CONFIG_SUPERBLK_SIZE);
    for (UINT32 i = 0; i < MAX_SBLK_SIZE; i += SBLK_PER_SIZE)
    {
        if (0 == HV_MEMCMP((UCHAR8 *)(HV_MEMORY_CONFIG_OTA_START + i), SBLK_HEADER, 4))
        {
            ucValidBLK++;
        }
        else
        {
            break;
        }
    }
    return ucValidBLK;
}

/**
 * @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)
{
    PartitionSBLKInfo stPartInfo = {};
    s_stDdcciOtaParam.uiPacketNum = 0;
    s_stDdcciOtaParam.uiReceived = 0;
    s_stDdcciOtaParam.uiAddrOffset = 0;
    s_stDdcciOtaParam.ucWrStatus = 0;
    s_stDdcciOtaParam.uiFlashAddr = (pucData[OTA_DDCCI_START_ADDR3]
                                     | (pucData[OTA_DDCCI_START_ADDR2] << 8)
                                     | (pucData[OTA_DDCCI_START_ADDR1] << 16)
                                     | (pucData[OTA_DDCCI_START_ADDR0] << 24));

    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));

    if (HV_FAILURE == Hv_Comm_OTA_GetPartitionInfoByStartAddr((s_stDdcciOtaParam.uiFlashAddr == OTA_PART_B_ADDR) ? OTA_PART_A_ADDR : s_stDdcciOtaParam.uiFlashAddr, &stPartInfo))
    {
        HV_LOG_INFO(DDCCI, "Partition not exist");
        return HV_FAILURE;
    }

    if (stPartInfo.uiTotalLen < s_stDdcciOtaParam.uiBinLen)
    {
        HV_LOG_INFO(DDCCI, "Partition length is less than bin length");
        return HV_FAILURE;
    }

    s_stDdcciOtaParam.bDdcciOtaMode = HV_TRUE;

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

    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;
    USHORT16 usBurst = 0;
    USHORT16 usRemainsLen = 0;
    UCHAR8 aucBuf[OTA_DP_BUFLEN] = {0};
    UCHAR8 ucCRC = 0;

    /* check packet number */
    if (pucData[OTA_DDCCI_PKT_NUM] != s_stDdcciOtaParam.uiPacketNum)
    {
        HV_LOG_ERROR(DP, "Not expected packet %d, is %d", s_stDdcciOtaParam.uiPacketNum, pucData[OTA_DDCCI_PKT_NUM]);
        return HV_FAILURE;
    }

    if (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[512*0], RX_EDP_RX_DPCD_MSGAUX_DRQ_00_00, 512);
        Hv_Common_ReadRegBytes(&aucBuf[512*1], HV_EDP_RX_MSGAUX_URQ_SW_00_BASE_ADDRESS, 512);
        Hv_Common_ReadRegBytes(&aucBuf[512*2], RX_EDP_RX_DPCD_HDCP2X_0_00_00, 1024);
        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);
        Hv_Common_ReadRegBytes(&aucBuf[512*6], RX_EDP_RX_DPCD_HDCP2X_0_01_00, 1024);
    }
    else if (s_stDdcciOtaParam.enPort == DDCCI_PORT_DP0)
    {
        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[512*0], RX_EDP_RX_DPCD_MSGAUX_DRQ_01_00, 512);
        Hv_Common_ReadRegBytes(&aucBuf[512*1], HV_EDP_RX_MSGAUX_URQ_SW_01_BASE_ADDRESS, 512);
        Hv_Common_ReadRegBytes(&aucBuf[512*2], RX_EDP_RX_DPCD_HDCP2X_0_01_00, 1024);
        Hv_Common_ReadRegBytes(&aucBuf[512*4], RX_EDP_RX_DPCD_MSGAUX_DRQ_00_00, 512);
        Hv_Common_ReadRegBytes(&aucBuf[512*5], HV_EDP_RX_MSGAUX_URQ_SW_00_BASE_ADDRESS, 512);
        Hv_Common_ReadRegBytes(&aucBuf[512*6], RX_EDP_RX_DPCD_HDCP2X_0_00_00, 1024);
    }
    else
    {
        HV_LOG_ERROR(DP, "Not DP port");
        return HV_FAILURE;
    }

    /* 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, "CRC error %x %x", pucData[OTA_DDCCI_PKT_NUM + 1], ucCRC);
        return HV_FAILURE;
    }

    HV_MEMCPY((VOID *)(HV_MEMORY_CONFIG_OTA_START + s_stDdcciOtaParam.uiReceived), aucBuf, usValidDataLen);
    s_stDdcciOtaParam.uiReceived += usValidDataLen;

    s_stDdcciOtaParam.uiPacketNum = (s_stDdcciOtaParam.uiPacketNum + 1) % 256;

    return HV_SUCCESS;
}

/**
 * @brief Handle the OTA Erase and Write 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_ExecuteOTA(UCHAR8 *pucData, UCHAR8 ucLen)
{
    UINT32 uiIndex = 0;
    PartitionSBLKInfo stPartInfo = {0};
    Hv_Comm_OTA_GetPartitionInfoByStartAddr((s_stDdcciOtaParam.uiFlashAddr == OTA_PART_B_ADDR)  ? OTA_PART_A_ADDR : s_stDdcciOtaParam.uiFlashAddr, &stPartInfo);
    if (0 == HV_MEMCMP(stPartInfo.ucSBLKName, STRING_SBLKPMSB, SBLK_MAGIC_STRING_LENGTH))
    {
        stPartInfo.uiTotalLen = 0x7000;
    }
    HV_LOG_INFO(DDCCI,"Wait flash erase.....");
    for (uiIndex = 0; uiIndex < stPartInfo.uiTotalLen / 0x10000; uiIndex++)
    {
        Hv_Drv_Flash_ProtectDisable();
        Hv_Drv_Flash_OnlyEraseMultiSector(s_stDdcciOtaParam.uiFlashAddr + uiIndex * 0x10000);
    }
    if (stPartInfo.uiTotalLen % 0x10000 > 0)
    {
        Hv_Drv_Flash_ProtectDisable();
        Hv_Drv_Flash_OnlyErase(s_stDdcciOtaParam.uiFlashAddr + uiIndex * 0x10000, ALIGN_4K(stPartInfo.uiTotalLen % 0x10000));
    }
    HV_LOG_INFO(DDCCI,"flash erase ok");
    HV_LOG_INFO(DDCCI,"Wait flash write.....");
    Hv_Drv_Flash_ProtectDisable();
    return Hv_Drv_Flash_OnlyWrite(s_stDdcciOtaParam.uiFlashAddr, (UCHAR8 *)HV_MEMORY_CONFIG_OTA_START, s_stDdcciOtaParam.uiBinLen);
}

/**
 * @brief Handle the HDMI 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_TransferHdmiOTA(UCHAR8 *pucData, UCHAR8 ucLen)
{
    USHORT16 usValidDataLen = 0;
    UCHAR8 ucRemainsLen = 0;
    UCHAR8 aucBuf[OTA_HDMI_BUFLEN] = {0};
    UCHAR8 ucCRC = 0;
    USHORT16 usBurst = 0;
    UINT32 uiPktNum = 0;

    uiPktNum = (pucData[OTA_DDCCI_PKT_NUM_HDMI + 3] 
               | (pucData[OTA_DDCCI_PKT_NUM_HDMI + 2] << 8) 
               | (pucData[OTA_DDCCI_PKT_NUM_HDMI + 1] << 16) 
               | (pucData[OTA_DDCCI_PKT_NUM_HDMI] << 24));

    /* check packet number */
    if (uiPktNum != s_stDdcciOtaParam.uiPacketNum)
    {
        HV_LOG_INFO(HDMI, "pkt Num need%d but%d.", s_stDdcciOtaParam.uiPacketNum, uiPktNum);
        return HV_FAILURE;
    }

    if ((s_stDdcciOtaParam.enPort == DDCCI_PORT_HDMI0) || (s_stDdcciOtaParam.enPort == DDCCI_PORT_HDMI1))
    {
        usValidDataLen = (pucData[OTA_DDCCI_LEN] & DDCCI_DATA_LEN_CTRL) - 7;

        if(usValidDataLen > OTA_HDMI_BUFLEN)
        {
            HV_LOG_ERROR(DDCCI, "OTA TRAN Length：0x%x is Invalid.", pucData[OTA_DDCCI_LEN]);
            return HV_FAILURE;
        }

        HV_MEMCPY(aucBuf, &pucData[OTA_DDCCI_PKT_NUM_HDMI + 4], usValidDataLen);
        // DDC_BUF_PRINT("aucBuf -> ", aucBuf, OTA_HDMI_BUFLEN);
        // DDC_BUF_PRINT("pucData -> ", pucData + OTA_DDCCI_PKT_NUM_HDMI + 4, usValidDataLen);
        /* 判断是否接收到了CRC */
        if (s_stDdcciOtaParam.uiReceived + usValidDataLen > s_stDdcciOtaParam.uiBinLen - OTA_SBLK_CRC_LEN && DDCCI_OTA_SHORT_PATCH != s_stDdcciOtaParam.enOTAType)
        {
            if (0 == s_stDdcciOtaParam.uiBinLen - (s_stDdcciOtaParam.uiReceived + usValidDataLen))
            {

                for (UINT32 uiIndex = OTA_SBLK_CRC_LEN - 1; uiIndex < usValidDataLen; uiIndex--)
                {
                    s_aucCodeCrc[uiIndex] = aucBuf[usValidDataLen -1 - (OTA_SBLK_CRC_LEN - 1 - uiIndex)];
                }
            }
            else
            {
                for (UINT32 uiIndex = 0; uiIndex < OTA_SBLK_CRC_LEN - (s_stDdcciOtaParam.uiBinLen - (s_stDdcciOtaParam.uiReceived + usValidDataLen)); uiIndex++)
                {
                    s_aucCodeCrc[uiIndex] = aucBuf[usValidDataLen - (OTA_SBLK_CRC_LEN - (s_stDdcciOtaParam.uiBinLen - (s_stDdcciOtaParam.uiReceived + usValidDataLen))) + uiIndex];
                }
            }

            HV_LOG_INFO(HDMI, "crc:%x %x %x %x", s_aucCodeCrc[0], s_aucCodeCrc[1], s_aucCodeCrc[2], s_aucCodeCrc[3]);
        }
    }
    else
    {
        HV_LOG_ERROR(DDCCI, "OTA Port error.");
        return HV_FAILURE;
    }

    /* check crc */
    ucCRC = aucBuf[0];

    for (UINT32 uiIndex = 1; uiIndex < usValidDataLen; uiIndex++)
    {
        ucCRC = ucCRC ^ aucBuf[uiIndex];
    }

    if (ucCRC != pucData[ucLen - 2])
    {
        HV_LOG_ERROR(DDCCI, "%d %x %x", usValidDataLen, ucCRC, pucData[ucLen - 2]);
        return HV_FAILURE;
    }

    usBurst = (s_stDdcciOtaParam.uiReceived % OTA_DDCCI_WRITE_LEN);
    HV_LOG_INFO(HDMI, "usBurst:%d validDataLen:%d.", usBurst, usValidDataLen);
    /* got data */
    if (usBurst + usValidDataLen <= OTA_DDCCI_WRITE_LEN)
    {
        HV_MEMCPY(&s_stDdcciOtaParam.aucOTAData[usBurst], aucBuf, usValidDataLen);
        s_stDdcciOtaParam.uiReceived += usValidDataLen;
        // DDC_BUF_PRINT("cpy data -> ", s_stDdcciOtaParam.aucOTAData + usBurst, usValidDataLen);
        ucRemainsLen = 0;
    }
    else
    {
        ucRemainsLen = usBurst + usValidDataLen - OTA_DDCCI_WRITE_LEN;
        HV_MEMCPY(&s_stDdcciOtaParam.aucOTAData[usBurst], aucBuf, OTA_DDCCI_WRITE_LEN-usBurst);
        // DDC_BUF_PRINT("cpy data -> ", s_stDdcciOtaParam.aucOTAData + usBurst, 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))
    {
        if (s_stDdcciOtaParam.uiReceived >= s_stDdcciOtaParam.uiBinLen)
        {
            Hv_Drv_Flash_OnlyWrite(s_stDdcciOtaParam.uiFlashAddr + s_stDdcciOtaParam.uiAddrOffset, s_stDdcciOtaParam.aucOTAData, (usBurst + usValidDataLen > OTA_DDCCI_WRITE_LEN) ? (OTA_DDCCI_WRITE_LEN) : (usBurst + usValidDataLen));
            HV_LOG_INFO(HDMI, "Write addr%x offset%x len:%x.", s_stDdcciOtaParam.uiFlashAddr, s_stDdcciOtaParam.uiAddrOffset, (usBurst + usValidDataLen > OTA_DDCCI_WRITE_LEN) ? (OTA_DDCCI_WRITE_LEN) : (usBurst + usValidDataLen));
            s_stDdcciOtaParam.uiAddrOffset += OTA_DDCCI_WRITE_LEN;
        }
        else
        {
            Hv_Drv_Flash_OnlyWrite(s_stDdcciOtaParam.uiFlashAddr + s_stDdcciOtaParam.uiAddrOffset, s_stDdcciOtaParam.aucOTAData, OTA_DDCCI_WRITE_LEN);
            HV_LOG_INFO(HDMI, "Write addr%x offset%x len:%x.", s_stDdcciOtaParam.uiFlashAddr, s_stDdcciOtaParam.uiAddrOffset, OTA_DDCCI_WRITE_LEN);
            s_stDdcciOtaParam.uiAddrOffset += OTA_DDCCI_WRITE_LEN;
        }

        // DDC_BUF_PRINT("Write Flash Data -> ", s_stDdcciOtaParam.aucOTAData, 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);
        // DDC_BUF_PRINT("cpy data -> ", s_stDdcciOtaParam.aucOTAData, ucRemainsLen);
        s_stDdcciOtaParam.uiReceived += ucRemainsLen;
        ucRemainsLen = 0;
    }

    s_stDdcciOtaParam.uiPacketNum++;

    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 + 9] = {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] = 0;
    aucRspData[OTA_DDCCI_RESULT_CODE] = RESULT_CODE_SUCCESS;

    switch(ucOtaOpCode)
    {
        case DDCCI_OPCODE_VCP_OTA_GET_VER:
        {
#ifdef HV_PROJECT_CONFIG_SW_MAJOR_VER
            aucRspData[OTA_DDCCI_PKT_NUM] = HV_PROJECT_CONFIG_SW_MAJOR_VER;
#else
            aucRspData[OTA_DDCCI_PKT_NUM] = 1;
#endif
#ifdef HV_PROJECT_CONFIG_SW_MINOR_VER
            aucRspData[OTA_DDCCI_PKT_NUM + 1] = HV_PROJECT_CONFIG_SW_MINOR_VER;
#else
            aucRspData[OTA_DDCCI_PKT_NUM + 1] = 3;
#endif
            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_PKT_NUM + 3] = Hv_App_Ddcci_CalCheckSum(aucRspData);
            Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN + 1);
            HV_LOG_INFO(DDCCI,"OTA GET VER: %d.%d", aucRspData[OTA_DDCCI_PKT_NUM], aucRspData[OTA_DDCCI_PKT_NUM + 1]);
            break;
        }
        case DDCCI_OPCODE_VCP_OTA_GET_PART:
        {
            s_stDdcciOtaParam.enPort = ucSource;
            s_stDdcciOtaParam.enPart = Hv_Comm_OTA_GetUpgradePart();
            s_stDdcciOtaParam.enOTAType = pucData[OTA_DDCCI_OPCODE + 1];
            s_stDdcciOtaParam.bDdcciOtaMode = HV_TRUE;
            aucRspData[OTA_DDCCI_PKT_NUM + 1] = CHIP_HT7315;
            aucRspData[OTA_DDCCI_PKT_NUM + 2] = RESULT_CODE_SUCCESS;

            if (OTA_PART_MAX == s_stDdcciOtaParam.enPart)
            {
                HV_LOG_DEBUG(DDCCI,"Got part error, cann't do ddcci ota");
                s_stDdcciOtaParam.bDdcciOtaMode = HV_FALSE;
                s_stDdcciOtaParam.enPart = OTA_PART_MAX;
                aucRspData[OTA_DDCCI_PKT_NUM + 2] = RESULT_CODE_FAILURE;
            }

            if (DDCCI_OTA_SHORT_PATCH == s_stDdcciOtaParam.enOTAType)
            {
#ifdef HV_FLASH_CONFIG_HOTFIX_START
                s_stDdcciOtaParam.enPart = OTA_PART_SHORT_PATCH;
#else
                HV_LOG_INFO(DDCCI,"SHORT PATCH ERROR, cann't do ddcci SHORT PATCH ota");
                s_stDdcciOtaParam.bDdcciOtaMode = HV_FALSE;
                s_stDdcciOtaParam.enPart = OTA_PART_MAX;
                aucRspData[OTA_DDCCI_PKT_NUM + 2] = RESULT_CODE_FAILURE;
#endif
            }

            aucRspData[OTA_DDCCI_PKT_NUM] = s_stDdcciOtaParam.enPart;
            aucRspData[OTA_DDCCI_LEN] = (DDCCI_CONTROL_STATUS_FLAG | (OTA_DDCCI_RSP_MSG_LEN + 1)) - DDCCI_HEADER_LEN;
            aucRspData[OTA_DDCCI_PKT_NUM + 3] = 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();
            HV_LOG_INFO(DDCCI,"OTA Get Part:%d  OTAType:%d ", s_stDdcciOtaParam.enPart, s_stDdcciOtaParam.enOTAType);
            break;
        }
        case DDCCI_OPCODE_VCP_OTA_GET_VALID_BLK:
        {
            HV_LOG_INFO(DDCCI,"OTA Get BLK Num");
            aucRspData[OTA_DDCCI_PKT_NUM] = HV_App_Ddcci_GetValidSBLK();
            aucRspData[OTA_DDCCI_PKT_NUM + 1] = RESULT_CODE_SUCCESS;
            aucRspData[OTA_DDCCI_LEN] = (DDCCI_CONTROL_STATUS_FLAG | (OTA_DDCCI_RSP_MSG_LEN)) - DDCCI_HEADER_LEN;
            aucRspData[OTA_DDCCI_PKT_NUM + 2] = Hv_App_Ddcci_CalCheckSum(aucRspData);
            Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN);
            break;
        }
        case DDCCI_OPCODE_VCP_OTA_GET_BLK:
        {
            HV_LOG_INFO(DDCCI,"OTA Get BLK");
            Hv_App_Ddcci_PrepareBLK(pucData[OTA_DDCCI_PKT_NUM], aucRspData);
            aucRspData[OTA_DDCCI_PKT_NUM + 1] = RESULT_CODE_SUCCESS;
            aucRspData[OTA_DDCCI_LEN] = (DDCCI_CONTROL_STATUS_FLAG | (OTA_DDCCI_RSP_MSG_LEN + 8)) - DDCCI_HEADER_LEN;
            aucRspData[OTA_DDCCI_PKT_NUM + 10] = Hv_App_Ddcci_CalCheckSum(aucRspData);
            Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN + 8);
            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_CRC] = Hv_App_Ddcci_CalCheckSum(aucRspData);
            Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN);
            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_PKT_NUM + 2] = RESULT_CODE_FAILURE;
                }
                else
                {
                    aucRspData[OTA_DDCCI_PKT_NUM + 2] = RESULT_CODE_SUCCESS;
                }
                aucRspData[OTA_DDCCI_PKT_NUM] = pucData[OTA_DDCCI_PKT_NUM];
                aucRspData[OTA_DDCCI_PKT_NUM + 1] = s_stDdcciOtaParam.uiPacketNum;
                aucRspData[OTA_DDCCI_LEN] = (DDCCI_CONTROL_STATUS_FLAG | (OTA_DDCCI_RSP_MSG_LEN + 1)) - DDCCI_HEADER_LEN;
                aucRspData[OTA_DDCCI_PKT_NUM + 3] = Hv_App_Ddcci_CalCheckSum(aucRspData);
                Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN + 1);
            }
            break;
        }
        case DDCCI_OPCODE_VCP_OTA_WRITE:
        {
            if (HV_TRUE == s_stDdcciOtaParam.bDdcciOtaMode)
            {
                aucRspData[OTA_DDCCI_CRC] = Hv_App_Ddcci_CalCheckSum(aucRspData);
                Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN);
                if (Hv_App_Ddcci_ExecuteOTA(pucData, ucLen) != HV_SUCCESS)
                {
                    s_stDdcciOtaParam.ucWrStatus = 0;
                }
                else
                {
                    s_stDdcciOtaParam.ucWrStatus = 2;
                    HV_LOG_INFO(DDCCI,"flash write ok");
                }

                s_aucCodeCrc[0] = pucData[OTA_DDCCI_PKT_NUM];
                s_aucCodeCrc[1] = pucData[OTA_DDCCI_PKT_NUM + 1];
                s_aucCodeCrc[2] = pucData[OTA_DDCCI_PKT_NUM + 2];
                s_aucCodeCrc[3] = pucData[OTA_DDCCI_PKT_NUM + 3];
                if (s_stDdcciOtaParam.uiFlashAddr == OTA_PART_A_ADDR || s_stDdcciOtaParam.uiFlashAddr == OTA_PART_B_ADDR)
                {
                    HV_LOG_INFO(DDCCI,"update boot code index and crc 0x%08x %2x %2x %2x %2x", s_stDdcciOtaParam.uiFlashAddr, s_aucCodeCrc[0], s_aucCodeCrc[1], s_aucCodeCrc[2], s_aucCodeCrc[3]);
                    Hv_Comm_OTA_UpdateParams(s_aucCodeCrc, OTA_SBLK_CRC_LEN);
                }
                else
                {
                    HV_LOG_INFO(DDCCI,"update other partition crc %2x %2x %2x %2x", s_aucCodeCrc[0], s_aucCodeCrc[1], s_aucCodeCrc[2], s_aucCodeCrc[3]);
                    Hv_Comm_OTA_UpdatePartitionCrcByAddr(s_aucCodeCrc, OTA_SBLK_CRC_LEN, s_stDdcciOtaParam.uiFlashAddr);
                }
            }
            break;
        }
        case DDCCI_OPCODE_VCP_OTA_TRANS_HDMI:
        {
            if (HV_TRUE == s_stDdcciOtaParam.bDdcciOtaMode)
            {
                UINT32 uiPktNum = (pucData[OTA_DDCCI_PKT_NUM_HDMI + 3] 
                                  | (pucData[OTA_DDCCI_PKT_NUM_HDMI + 2] << 8) 
                                  | (pucData[OTA_DDCCI_PKT_NUM_HDMI + 1] << 16) 
                                  | (pucData[OTA_DDCCI_PKT_NUM_HDMI] << 24));

                HV_LOG_INFO(DDCCI,"Trans:%d", uiPktNum);
                aucRspData[OTA_DDCCI_LEN] = (DDCCI_CONTROL_STATUS_FLAG | (OTA_DDCCI_RSP_MSG_LEN + 4)) - DDCCI_HEADER_LEN;

                if (Hv_App_Ddcci_TransferHdmiOTA(pucData, ucLen) != HV_SUCCESS)
                {
                    aucRspData[OTA_DDCCI_RESULT_CODE] = RESULT_CODE_FAILURE;
                }

                aucRspData[OTA_DDCCI_RESULT_CODE + 1] = pucData[OTA_DDCCI_PKT_NUM_HDMI];
                aucRspData[OTA_DDCCI_RESULT_CODE + 2] = pucData[OTA_DDCCI_PKT_NUM_HDMI + 1];
                aucRspData[OTA_DDCCI_RESULT_CODE + 3] = pucData[OTA_DDCCI_PKT_NUM_HDMI + 2];
                aucRspData[OTA_DDCCI_RESULT_CODE + 4] = pucData[OTA_DDCCI_PKT_NUM_HDMI + 3];
                aucRspData[OTA_DDCCI_RESULT_CODE + 5] = Hv_App_Ddcci_CalCheckSum(aucRspData);

                Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN + 4);
            }
            break;
        }
        case DDCCI_OPCODE_VCP_OTA_STOP:
        {
            HV_LOG_INFO(DDCCI,"OTA Stop");
            s_stDdcciOtaParam.bDdcciOtaMode = HV_FALSE;
            s_stDdcciOtaParam.uiPacketNum = 0;
            s_stDdcciOtaParam.uiReceived = 0;
            s_stDdcciOtaParam.enPart = OTA_PART_MAX;
            s_stDdcciOtaParam.uiAddrOffset = 0;
            s_stDdcciOtaParam.uiFlashAddr = 0;
            s_stDdcciOtaParam.uiBinLen = 0;
            s_stDdcciOtaParam.ucWrStatus = 0;
            aucRspData[OTA_DDCCI_CRC] = Hv_App_Ddcci_CalCheckSum(aucRspData);
            Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN);
            break;
        }
        case DDCCI_OPCODE_VCP_OTA_REBOOT:
        {
            HV_LOG_INFO(DDCCI,"OTA Reboot.");
            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_Vos_Delayus(300000);
            Hv_Drv_WDG_Enable();
            hv_chip_Reset();
            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_PKT_NUM + 1] = RESULT_CODE_SUCCESS;
                aucRspData[OTA_DDCCI_PKT_NUM + 2] = s_stDdcciOtaParam.ucWrStatus;
                aucRspData[OTA_DDCCI_PKT_NUM + 3] = 0;
                aucRspData[OTA_DDCCI_LEN] = (DDCCI_CONTROL_STATUS_FLAG | (OTA_DDCCI_RSP_MSG_LEN + 2)) - DDCCI_HEADER_LEN;
                aucRspData[OTA_DDCCI_PKT_NUM + 4] = Hv_App_Ddcci_CalCheckSum(aucRspData);
                Hv_App_Ddcci_SendData(ucSource, aucRspData, OTA_DDCCI_RSP_MSG_LEN + 2);
            }
            break;
        }
        default:
        {
            Hv_App_Ddcci_SendNullMsg(ucSource);
            HV_LOG_INFO(DDCCI,"default");
            break;
        }
    }
}

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

