/**
 * @Author: 李建
 * @Date: 2024/9/7 13:06
 * Description: 
 * Copyright: Copyright (©) 2024 永续绿建. All rights reserved.
 */
#include "ymodem_update.h"
#include "port.h"
#include "mb.h"
#include "flash.h"
#include <stdio.h>

static enum UPDATE_STATE Update_State = TO_START;
/* 设置升级的步骤 */
static inline void Set_State(enum UPDATE_STATE state)
{
    Update_State = state;
}
/* 查询升级的步骤 */
static inline uint8_t Get_State(void)
{
    return Update_State;
}
#define POLY        0x1021
/**
 * @brief CRC-16 校验
 * @param addr 开始地址
 * @param num   长度
 * @param crc   CRC
 * @return crc  返回CRC的值
 */
static uint16_t CRC16(uint8_t *addr, int num, uint16_t crc)
{
    int i;
    for (; num > 0; num--)					/* Step through bytes in memory */
    {
        crc = crc ^ (*addr++ << 8);			/* Fetch byte from memory, XOR into CRC top byte*/
        for (i = 0; i < 8; i++)				/* Prepare to rotate 8 bits */
        {
            if (crc & 0x8000)				/* b15 is set... */
                crc = (crc << 1) ^ POLY;  	/* rotate and XOR with polynomic */
            else                          	/* b15 is clear... */
                crc <<= 1;					/* just rotate */
        }									/* Loop for 8 bits */
        crc &= 0xFFFF;						/* Ensure CRC remains 16-bit value */
    }										/* Loop until num=0 */
    return(crc);							/* Return updated CRC */
}

/**
 * @brief 获取数据包的类型, 顺便进行校验
 * @param buf 开始地址
 * @param len 长度
 * @return 1 通过校验 0 没通过校验
 */
uint8_t Check_CRC(uint8_t* buf, int len)
{
    uint16_t crc = 0;

    /* 进行CRC校验 */
    crc = CRC16(buf+3, len - 5, crc);
    if(crc != (buf[131]<<8|buf[132]))
    {
        return 0;   /* 没通过校验 */
    }else{
        return 1;	/* 通过校验 */
    }
}

void Code_Storage_Done(void)
{
    uint32_t update_flag = Startup_Update;				// 对应bootloader的启动步骤
    flash_write((Application_2_Addr + Application_Size - 4), &update_flag,4 );   //在APP2中添加标记 0x801DFFC
}
/**
 * 串口写文件回调，内容为ymodem协议报文
 * @param header
 * @param pucRegBuffer
 * @return
 */
eMBErrorCode eMBWriteFileCB(UCHAR * header, UCHAR * pucRegBuffer, USHORT * usLen) {
    switch (*header) {
        case SOH:{
            static uint8_t Data_State = 0;
            static uint16_t pack_offset = 0;
            if(Check_CRC(pucRegBuffer, *usLen - 1)) {
                // 如果是起始帧
                if(Get_State() == TO_START && pucRegBuffer[1] == 0&&(pucRegBuffer[2] == (uint8_t)(~pucRegBuffer[1]))) {
                    Set_State(TO_RECEIVE_DATA);
                    Data_State = 0x01;
                    pack_offset = 1;
                    pucRegBuffer[0] = ACK;
                    flash_erase(Application_2_Addr, (Application_2_Addr + Application_Size) / FLASH_PAGE_SIZE);
                    DEBUG_PRINTF("flash_erase\r\n");
                } else if((Get_State()==TO_RECEIVE_END)&&(pucRegBuffer[1] == 0x00)&&(pucRegBuffer[2] == (uint8_t)(~pucRegBuffer[1])))
                {
                    Code_Storage_Done();	//APP2区代码存放完成
                    DEBUG_PRINTF("Code_Storage_Done\r\n");
                    Set_State(TO_START);	//标记可以继续接收Ymodem数据
                    pucRegBuffer[0] = ACK;
                    pack_offset = 0;
                    HAL_NVIC_SystemReset();	//重启系统
                } else if((Get_State()==TO_RECEIVE_DATA)&&(pucRegBuffer[1] == Data_State)&&(pucRegBuffer[2] == (uint8_t)(~pucRegBuffer[1]))) {
                    /* 烧录程序 */
                    flash_write((Application_2_Addr + (pack_offset -1) * 128), pucRegBuffer+3, 128);
                    pack_offset++;
                    if(Data_State++ == 0xFF) {
                        Data_State = 0x01;
                    }
                    pucRegBuffer[0] = ACK;
                    DEBUG_PRINTF("flash_write:%d\r\n", Data_State);
                }
            } else {
                pucRegBuffer[0] = NACK;
                DEBUG_PRINTF("crc error:%d\r\n", Data_State);
            }
        }
            break;
        case EOT: {
            if(Get_State()==TO_RECEIVE_DATA)
            {
                Set_State(TO_RECEIVE_EOT2);
                pucRegBuffer[0] = NACK;
            }
            else if(Get_State()==TO_RECEIVE_EOT2)
            {
                Set_State(TO_RECEIVE_END);
                pucRegBuffer[0] = ACK;
            }
            else
            {
                //Receive EOT, But error...
            }
        }
        break;
    }
    return MB_ENOERR;
}