/******************************************************************************
  * file    Src/common.c 
  * author  YuLiang
  * version 1.0.0
  * date    10-Jun-2022
  * brief   This file provides all the common functions.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT(c) 2022 LandPower</center></h2>
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  *******************************************************************************/ 

/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#include "main.h"
#include "menu.h"

#include "common.h"
#include "flash_if.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* 中断中打印限制长度. */
#define USART_PRINT_BUF_LEN 128

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
dev_info_t dev_info;
dev_record_t dev_record;
uint8_t number_str[NUM_LEN];

static uint32_t crc32Table[256];
static uint32_t dev_magic;
static uint32_t dev_magic_bak;

extern uint8_t is_watchdog;
extern uint8_t aPacketData[PACKET_1K_SIZE + PACKET_DATA_INDEX + PACKET_TRAILER_SIZE];

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Convert an Integer to a string
  * @param  p_str: The string output pointer
  * @param  intnum: The integer to be converted
  * @retval None
  */
void Int2Str(uint8_t *p_str, uint32_t intnum)
{
  uint32_t i, divider = 1000000000, pos = 0, status = 0;

  for (i = 0; i < 10; i++)
  {
    p_str[pos++] = (intnum / divider) + 48;

    intnum = intnum % divider;
    divider /= 10;
    if ((p_str[pos-1] == '0') & (status == 0))
    {
      pos = 0;
    }
    else
    {
      status++;
    }
  }

  p_str[pos] = 0;
}

/**
  * @brief  Convert a string to an integer
  * @param  p_inputstr: The string to be converted
  * @param  p_intnum: The integer value
  * @retval 1: Correct
  *         0: Error
  */
uint32_t Str2Int(uint8_t *p_inputstr, uint32_t *p_intnum)
{
  uint32_t i = 0, res = 0;
  uint32_t val = 0;

  if ((p_inputstr[0] == '0') && ((p_inputstr[1] == 'x') || (p_inputstr[1] == 'X')))
  {
    i = 2;
    while ( ( i < 11 ) && ( p_inputstr[i] != '\0' ) )
    {
      if (ISVALIDHEX(p_inputstr[i]))
      {
        val = (val << 4) + CONVERTHEX(p_inputstr[i]);
      }
      else
      {
        /* Return 0, Invalid input */
        res = 0;
        break;
      }
      i++;
    }

    /* valid result */
    if (p_inputstr[i] == '\0')
    {
      *p_intnum = val;
      res = 1;
    }
  }
  else /* max 10-digit decimal input */
  {
    while ( ( i < 11 ) && ( res != 1 ) )
    {
      if (p_inputstr[i] == '\0')
      {
        *p_intnum = val;
        /* return 1 */
        res = 1;
      }
      else if (((p_inputstr[i] == 'k') || (p_inputstr[i] == 'K')) && (i > 0))
      {
        val = val << 10;
        *p_intnum = val;
        res = 1;
      }
      else if (((p_inputstr[i] == 'm') || (p_inputstr[i] == 'M')) && (i > 0))
      {
        val = val << 20;
        *p_intnum = val;
        res = 1;
      }
      else if (ISVALIDDEC(p_inputstr[i]))
      {
        val = val * 10 + CONVERTDEC(p_inputstr[i]);
      }
      else
      {
        /* return 0, Invalid input */
        res = 0;
        break;
      }
      i++;
    }
  }

  return res;
}

/**
  * @brief  Print a string on the HyperTerminal
  * @param  p_string: The string to be printed
  * @retval None
  */
void Serial_PutString(uint8_t *p_string)
{
  uint16_t length = 0;

  while (p_string[length] != '\0')
  {
    length++;
  }
  HAL_GPIO_WritePin(B_485_EN_GPIO_Port, B_485_EN_Pin, GPIO_PIN_SET);
  HAL_UART_Transmit(UartHandle, p_string, length, TX_TIMEOUT);
  HAL_GPIO_WritePin(B_485_EN_GPIO_Port, B_485_EN_Pin, GPIO_PIN_RESET);
}

/**
  * @brief  Transmit a byte to the HyperTerminal
  * @param  param The byte to be sent
  * @retval HAL_StatusTypeDef HAL_OK if OK
  */
HAL_StatusTypeDef Serial_PutByte( uint8_t param )
{
  HAL_StatusTypeDef rv = HAL_ERROR;

  /* May be timeouted... */
  if ( UartHandle->gState == HAL_UART_STATE_TIMEOUT )
  {
    UartHandle->gState = HAL_UART_STATE_READY;
  }
  HAL_GPIO_WritePin(B_485_EN_GPIO_Port, B_485_EN_Pin, GPIO_PIN_SET);
  rv = HAL_UART_Transmit(UartHandle, &param, 1, TX_TIMEOUT);
  HAL_GPIO_WritePin(B_485_EN_GPIO_Port, B_485_EN_Pin, GPIO_PIN_RESET);

  return rv;
}

/**
  * @brief  Transmit a bytes to the HyperTerminal
  * @param  param The byte to be sent
  * @retval HAL_StatusTypeDef HAL_OK if OK
  */
HAL_StatusTypeDef Serial_PutBytes(uint8_t* buf, uint16_t len, uint32_t timeout)
{
  HAL_StatusTypeDef rv = HAL_ERROR;
  
  /* May be timeouted... */
  if ( UartHandle->gState == HAL_UART_STATE_TIMEOUT )
  {
    UartHandle->gState = HAL_UART_STATE_READY;
  }
  HAL_GPIO_WritePin(B_485_EN_GPIO_Port, B_485_EN_Pin, GPIO_PIN_SET);
  rv = HAL_UART_Transmit(UartHandle, buf, len, timeout);
  HAL_GPIO_WritePin(B_485_EN_GPIO_Port, B_485_EN_Pin, GPIO_PIN_RESET);

  return rv;
}

/* 通过 usart1 输出格式化字符. 原地等待发送, 与操作系统无关,
 * 可以在中断中使用, 尽量不要在中断中使用, 太耗时间. */
void vty_print(char *format, ...)
{
    static char buf[USART_PRINT_BUF_LEN];

    va_list ap;
    va_start(ap, format);    

    while(HAL_UART_STATE_BUSY_TX == UartHandle->gState)
        continue;

    if (vsnprintf(buf, USART_PRINT_BUF_LEN, format, ap) > 0)
    {
        HAL_GPIO_WritePin(B_485_EN_GPIO_Port, B_485_EN_Pin, GPIO_PIN_SET);
        HAL_UART_Transmit(UartHandle, (uint8_t*)buf, strlen(buf), 1000);
        HAL_GPIO_WritePin(B_485_EN_GPIO_Port, B_485_EN_Pin, GPIO_PIN_RESET);
    }
     
    va_end(ap);
}

/* 计算crc32 table值. */
static uint32_t _crc32_reflect( uint32_t value, uint8_t bits )
{
    uint32_t reflection = 0;
    
    for(uint8_t i = 0; i < bits ; ++i)
    {
        if (value & (1u << i ))
            reflection |= 1 << (bits - 1 - i);
    }

    return reflection;
}

/**
  * @brief  初始化crc32表.
  * @param  none
  * @retval none
  */
void crc32_table_init(void)
{
    uint32_t int1 = 1; 
    uint32_t const value_high_bit = int1 << 31u;
    uint8_t dividend = 0;
    
    do
    {
        uint32_t remainder = 0;
        for(uint8_t mask = 1u << (8 - 1u); mask; mask >>= 1)
        {
            if (dividend & mask)
                remainder ^= value_high_bit;

            if (remainder & value_high_bit)
            {
                remainder <<= 1;
                remainder ^= 0x04C11DB7u;
            }
            else
                remainder <<= 1;
        }

        crc32Table[_crc32_reflect(dividend, 8)] = _crc32_reflect(remainder, 32);
    }
    while(++dividend);
}

/**
  * @brief  计算crc32值.
  * @param  buf: 数据.
  * @param  len: 数据长度.
  * @retval crc32值.
  */
uint32_t crc32_update(uint32_t crc, char* buf, uint32_t len)
{
    uint32_t i = 0;

    for(i = 0; i < len; i++)
        crc = crc32Table[(crc ^ buf[i]) & 0xff] ^ (crc >> 8);

    return crc;
}

/* 读取config主份存储区. */
static HAL_StatusTypeDef _dev_info_read(void)
{
    uint8_t i = 0;
    HAL_StatusTypeDef rv = HAL_ERROR;

    memset(&dev_info, 0, sizeof(dev_info_t));

    /* 读取主存储区. */
    for(i = 0; i < DEV_FLASH_REPETITION; i++)
    {
        rv = spi_flash_read(INFO_ADDRESS, (uint8_t*)(&dev_info), sizeof(dev_info_t));
        if (HAL_OK == rv)
            break;

        HAL_Delay(5000);
    }

    return rv;
}

/* 读取info备份存储区. */
static HAL_StatusTypeDef _dev_info_read_bak(void)
{
    uint8_t i = 0;
    HAL_StatusTypeDef rv = HAL_ERROR;

    memset(&dev_info, 0, sizeof(dev_info_t));

    /* 读取备存储区. */
    for(i = 0; i < DEV_FLASH_REPETITION; i++)
    {
        rv = spi_flash_read(INFO_ADDRESS_BAK, (uint8_t*)(&dev_info), sizeof(dev_info_t));
        if (HAL_OK == rv)
            break;

        HAL_Delay(5000);
    }

    if (HAL_OK == rv)
    {
        dev_magic = dev_info.magic;
        dev_magic_bak = dev_info.magic_bak;
    }

    return rv;
}

/* 保存信息文件. */
static HAL_StatusTypeDef _dev_info_save(uint32_t addr)
{
    uint8_t i = 0;
    HAL_StatusTypeDef rv = HAL_OK;

    /* 存多次失败才算失败. */
    for(i = 0; i < DEV_FLASH_REPETITION; i++)
    {
        rv = spi_flash_erase(addr, SPI_CMD_SECTOR_ERASE);
        rv |= spi_flash_write(addr, (uint8_t*)(&dev_info), sizeof(dev_info_t));
        if (HAL_OK == rv)
            break;
    }

    Serial_PutString("Info(");
    Int2Str(number_str, addr);
    Serial_PutString(number_str);
    Serial_PutString(") save ");
    Int2Str(number_str, rv);
    Serial_PutString(number_str);
    Serial_PutString(".\r\n");
    return rv;
}

/* 恢复出厂信息. */
static HAL_StatusTypeDef _dev_info_default_set(void)
{
    HAL_StatusTypeDef rv = HAL_OK;

    memset(&dev_info, 0, sizeof(dev_info_t));
    dev_info.magic = INIT_DONE_MAGIC;
    dev_info.magic_bak = INIT_DONE_MAGIC;

    dev_info.type_m = 0x1;
    dev_info.type_s = 0x1;
    dev_info.mac[0] = 0x00; dev_info.mac[1] = 0x19; dev_info.mac[2] = 0x20;
    dev_info.mac[3] = 0x22; dev_info.mac[4] = 0x11; dev_info.mac[5] = 0x01;
    dev_info.id[0] = dev_info.mac[5]; dev_info.id[1] = dev_info.mac[4];
    dev_info.id[2] = dev_info.mac[3]; dev_info.id[3] = dev_info.mac[2];
    dev_info.ip[0] = 10;
    memcpy(&dev_info.ip[1], &dev_info.mac[3], 3);
    dev_info.ip_mask[0] = 255;
    dev_info.ip_gw[0] = 10; dev_info.ip_gw[3] = 1;
    dev_info.server_ip[0] = 10; dev_info.server_ip[3] = 1;
    dev_info.wireless_server_ip[0] = 111;dev_info.wireless_server_ip[1] = 47;
    dev_info.server_port = 17777;
    dev_info.wireless_server_ip[2] = 21;dev_info.wireless_server_ip[3] = 141;
    dev_info.wireless_server_port = 9456;
    
    snprintf((char*)dev_info.boot_version, DEV_VERSION_STR_LEN, "V%d.%d.%d.%d", dev_info.type_m, dev_info.type_s,
        VERSION_MAJOR, VERSION_MINOR);
    strncpy((char*)dev_info.boot_compile_time, COMPILE_TIME, DEV_COMPILE_TIME_LEN - 1);
    dev_info.csg_version = 0x0300;
    snprintf((char*)dev_info.csg_id, 8, "LD5678");

    rv = dev_info_save();

    Serial_PutString("Info set default ");
    Int2Str(number_str, rv);
    Serial_PutString(number_str);
    Serial_PutString(".\r\n");
    return rv;
}

/* 判断分区是否完整. */
static int32_t _dev_info_is_intact(uint32_t magic, uint32_t magic_bak)
{
    if (magic != INIT_DONE_MAGIC && magic != UPDATE_MAGIC && magic != UPDATE_OK_MAGIC && magic != UPDATE_ERROR_MAGIC)
        return FALSE;
    if (magic_bak != INIT_DONE_MAGIC)
        return FALSE;

    return TRUE;
}

/**
  * @brief  初始化设备信息文件.
  * @param  none
  * @retval HAL_xx.
  */
HAL_StatusTypeDef dev_info_init(void)
{
    HAL_StatusTypeDef rv = HAL_OK;

    /* 检查结构体大小. */
    if (sizeof(dev_info_t) != DEV_INFO_SIZE)
    {
        Serial_PutString("Info size is not 512.\r\n");
        while(1)
        {
            HAL_GPIO_TogglePin(LED_RUN_GPIO_Port, LED_RUN_Pin);
            HAL_Delay(50);
        }
    }

    /* 读取失败不做处理. */
    _dev_info_read_bak();
    _dev_info_read();

    /* 主备存储区都正常,什么都不用做. */
    if (_dev_info_is_intact(dev_info.magic, dev_info.magic_bak) && _dev_info_is_intact(dev_magic, dev_magic_bak))
        NULL;
    /* 主正常,备不正常,使用主配置覆盖备配置. */
    else if(_dev_info_is_intact(dev_info.magic, dev_info.magic_bak) && !(_dev_info_is_intact(dev_magic, dev_magic_bak)))
            rv = _dev_info_save(INFO_ADDRESS_BAK);
    /* 主不正常,备正常,使用备配置覆盖主配置. */
    else if(!(_dev_info_is_intact(dev_info.magic, dev_info.magic_bak)) && _dev_info_is_intact(dev_magic, dev_magic_bak))
    {
        /* 如果读取备失败,恢复出厂配置. */
        if ((rv = _dev_info_read_bak()) != HAL_OK)
            _dev_info_default_set();
        else
            rv = _dev_info_save(INFO_ADDRESS);
    }
    /* 其他情况一律视为flash没有被初始化. */
    else
        rv = _dev_info_default_set();

    /* 版本发生变化,保存版本信息. */
    if (IS_VERSION_CHG)
    {
        snprintf((char*)dev_info.boot_version, DEV_VERSION_STR_LEN, "V%d.%d.%d.%d", dev_info.type_m, dev_info.type_s,
            VERSION_MAJOR, VERSION_MINOR);
        strncpy((char*)dev_info.boot_compile_time, COMPILE_TIME, DEV_COMPILE_TIME_LEN - 1);
    
        rv |= dev_info_save();
    }

    return rv;
}

/**
  * @brief  读取record主份存储区.
  * @param  none
  * @retval HAL_xx.
  */
HAL_StatusTypeDef dev_record_read(void)
{
    uint8_t i = 0;
    HAL_StatusTypeDef rv = HAL_ERROR;

    memset(&dev_record, 0, sizeof(dev_record_t));

    /* 读取存储区. */
    for(i = 0; i < DEV_FLASH_REPETITION; i++)
    {
        rv = spi_flash_read(RECORD_ADDRESS, (uint8_t*)(&dev_record), sizeof(dev_record_t));
        if (HAL_OK == rv)
            break;

        HAL_Delay(5000);
    }

    return rv;
}


/**
  * @brief  保存设备信息文件.
  * @param  none
  * @retval HAL_xx.
  */
HAL_StatusTypeDef dev_info_save(void)
{
    HAL_StatusTypeDef rv = HAL_OK;

    rv |= _dev_info_save(INFO_ADDRESS);
    rv |= _dev_info_save(INFO_ADDRESS_BAK);

    return rv;
}

/* 对需要写入内部flash的数据进行CRC32校验. */
void _updata_crc_check(uint32_t addr, uint32_t size)
{
    uint16_t write_size = 0;
    uint32_t crc32_value = 0xffffffff;
    bin_pkt_tail_t *pkt_tail = NULL;

    /* 计算crc32. */
    size = size - 4;
    while(size > 0)
    {
        /* 计算每次写入的数据大小. */
        if (size > PACKET_1K_SIZE)
            write_size = PACKET_1K_SIZE;
        else
            write_size = size;

        /* 从spi flash读取升级包. */
        if (spi_flash_read(addr, aPacketData, write_size) != HAL_OK)
            goto CRC_CHECK_ERROR;

        crc32_value = crc32_update(crc32_value, (char*)aPacketData, write_size);
        
        /* 重新计算flash地址和剩余数据大小. */
        addr += write_size;
        size -= write_size;
    }

    /* 从spi flash读取报文头. */
    if (spi_flash_read(addr + 4 - sizeof(bin_pkt_tail_t), aPacketData, sizeof(bin_pkt_tail_t)) != HAL_OK)
        goto CRC_CHECK_ERROR;
    pkt_tail = (bin_pkt_tail_t*)aPacketData;

    /* 比较报文头信息 */
    if (crc32_value != pkt_tail->crc32
        || dev_info.type_m != pkt_tail->dev_type_m
        || pkt_tail->file_type != 2)
    {
        vty_print("Type %d %d %d %x %x\r\n", dev_info.type_m, dev_info.type_s, pkt_tail->file_type, pkt_tail->crc32, crc32_value);
        goto CRC_CHECK_ERROR;
    }

    return;
        
CRC_CHECK_ERROR:
    Serial_PutString(" Programming failure start with old APP!\n\r");
    /* 设置升级标志. */
    dev_info.magic = UPDATE_ERROR_MAGIC;
    dev_info_save();
    start_app();
}

/* 擦除内部app,并升级app. */
static void _updata_process(void)
{
    uint32_t spi_flash_address = TFTP_APP_ADDRESS;
    uint32_t flash_write_address = APPLICATION_ADDRESS;
    uint32_t fireware_size = dev_info.spi_fireware_size;
    uint16_t write_size = 0;
    uint16_t write_flash_size = 0;

    Serial_PutString("\r\nEntry updata.\r\n");

    /* 擦除内部flash. */
    if (FLASH_If_Erase(APPLICATION_ADDRESS) != FLASHIF_OK)
        goto UPDATA_ERROR;

    /* 循环写入内部flash. */
    while(fireware_size > 0)
    {
        /* 计算每次写入的数据大小. */
        if (fireware_size > PACKET_1K_SIZE)
            write_size = PACKET_1K_SIZE;
        else
            write_size = fireware_size;

        /* 从spi flash复制到内部flash. */
        if (spi_flash_read(spi_flash_address, aPacketData, write_size) != HAL_OK)
            goto UPDATA_ERROR;

        /* 因为内部flash是4byte写的,如果img不是4byte对齐的可能最后几个字节写漏了,这里保证最后几个字节可以写入. */
        write_flash_size = write_size / 4;
        if (write_size % 4 != 0)
            write_flash_size++;
        
        if (FLASH_If_Write(flash_write_address, (uint32_t*)(aPacketData), write_flash_size) != FLASHIF_OK)
            goto UPDATA_ERROR;

        /* 重新计算flash地址和剩余数据大小. */
        spi_flash_address += write_size;
        flash_write_address += write_size;
        fireware_size -= write_size;
    }

    /* 设置升级标志. */
    fireware_size = spi_flash_address - TFTP_APP_ADDRESS;
    dev_info.magic = INIT_DONE_MAGIC;
    dev_info.fireware_size = fireware_size;
    dev_info_save();

    /* 打印升级信息. */
    Serial_PutString(" Programming completed.\n\r");
    Int2Str(number_str, fireware_size);
    Serial_PutString(" Size: ");
    Serial_PutString(number_str);
    Serial_PutString(" Bytes\r\n");
    start_app();

UPDATA_ERROR:
    Serial_PutString(" Programming failure reset try again!\n\r");
    /* 设置升级标志. */
    dev_info.magic = UPDATE_ERROR_MAGIC;
    dev_info_save();
    HAL_NVIC_SystemReset();
}

/* 判断APP是否完整. */
static int32_t _is_app_intact(void)
{
    bin_pkt_tail_t pkt_tail;
    uint32_t size = 0;
    uint32_t crc32_value = 0xffffffff;

    /* 大小不对. */
    if (dev_info.fireware_size < 1024)
        return FALSE;

    /* 计算crc. */
    size = dev_info.fireware_size - 4;
    crc32_value = crc32_update(crc32_value, (char*)APPLICATION_ADDRESS, size);

    /* 读取包头. */
    memcpy(&pkt_tail, (void*)(APPLICATION_ADDRESS + dev_info.fireware_size - sizeof(bin_pkt_tail_t)), sizeof(bin_pkt_tail_t));

    /* 比较报文头信息 */
    //vty_print("1 %x %x\r\n", crc32_value, pkt_tail.crc32);
    if (crc32_value != pkt_tail.crc32
        || dev_info.type_m != pkt_tail.dev_type_m
        || pkt_tail.file_type != 2)
    {
        return FALSE;
    } 

    return TRUE;
}

/**
  * @brief  选择进入哪种IAP处理方式.
  * @param  none
  * @retval none
  */
void menu_entry(void)
{
    uint8_t key = 0;
    uint8_t i = 0;

    /* 如果软件设置了网口升级, 进入网口升级. */
    if (UPDATE_MAGIC == dev_info.magic)
    {
        _updata_crc_check(TFTP_APP_ADDRESS, dev_info.spi_fireware_size);
        _updata_process();
        return;
    }

    /* 1. 如果APP校验不正确;
       2. 看门狗重启;
       3. 命令行iap模式重启.
       以上3中情况则出现IAP启动进度条, 用户输入ctrl+P进入IAP, 超时自动启动APP. */
    if (_is_app_intact()
        && dev_record.reset_type != RESET_IAP
        && dev_record.reset_type != RESET_WATCHDOG)
    {
        //HAL_Delay(1000);
        start_app();
    }

    Serial_PutString("Wait to boot 3s...");

    /* 等待串口命令, 是否强制进入IAP. */
    for(i = 0; i < ENTRY_IAP_DELAY_COUNTER; i++)
    {
        if (HAL_UART_Receive(UartHandle, &key, 1, ENTRY_IAP_RX_TIMEOUT) != HAL_OK)
            continue;

        /* 键盘输入 "ctrl+P" 或者 "*" 进入串口 boot. */
        if (CONTROL('P') == key || '*' == key)
            Main_Menu();
        /* 键盘输入 "ctrl+U" 强制进入升级. */
        else if(CONTROL('U') == key)
        {
            _updata_crc_check(TFTP_APP_ADDRESS, dev_info.spi_fireware_size);
            _updata_process();
            return;
        }
    }

    /* 超时直接启动应用. */
    start_app();
}

void watchdog_refresh(void)
{
    HAL_GPIO_WritePin(WDG_GPIO_Port, WDG_Pin, GPIO_PIN_SET);
    HAL_Delay(1);
    HAL_GPIO_WritePin(WDG_GPIO_Port, WDG_Pin, GPIO_PIN_RESET);
    is_watchdog = TRUE;
}

#if BYTE_ORDER == LITTLE_ENDIAN
#if !defined(iap_htons)
/**
 * Convert an u16_t from host- to network byte order.
 *
 * @param n u16_t in host byte order
 * @return n in network byte order
 */
uint16_t iap_htons(uint16_t n)
{
    return (uint16_t)PP_HTONS(n);
}
#endif /* iap_htons */

#if !defined(iap_htonl)
/**
 * Convert an u32_t from host- to network byte order.
 *
 * @param n u32_t in host byte order
 * @return n in network byte order
 */
uint32_t iap_htonl(uint32_t n)
{
    return (uint32_t)PP_HTONL(n);
}
#endif /* iap_htonl */
#endif /* BYTE_ORDER == LITTLE_ENDIAN */
/************************ (C) COPYRIGHT LandPower *****END OF FILE****/
