/*****************************************************************************
|
* @file OTAUpgrade.c
|
* @brief 4G OTA固件升级模块实现
|
* @author AI Assistant
|
* @date 2025-12-20
|
*****************************************************************************/
|
|
#include "OTAUpgrade.h"
|
#include "HTTPClient.h"
|
#include "MCUFlash.h"
|
#include "Internet.h"
|
#include "HIDO_Debug.h"
|
#include "HIDO_Util.h"
|
#include "string.h"
|
|
// 引入AES解密算法
|
#include "../decryption/aes.h"
|
|
/*****************************************************************************
|
* 宏定义
|
*****************************************************************************/
|
#define OTA_AES_BLOCK_SIZE 16 // AES加密块大小
|
#define OTA_FLASH_ALIGN_SIZE 32 // Flash写入对齐大小
|
#define OTA_DECRYPT_BUFFER_SIZE 512 // 解密缓冲区大小(32的倍数)
|
#define OTA_FILE_TERMINATOR_CR 0x0D // 文件结束符:回车符
|
#define OTA_FILE_TERMINATOR_LF 0x0A // 文件结束符:换行符
|
|
/*****************************************************************************
|
* 本地变量
|
*****************************************************************************/
|
static ST_OTAInfo l_stOTAInfo; // OTA升级信息
|
static HIDO_CHAR l_acDownloadUrl[256]; // 下载URL
|
static HIDO_UINT8 l_au8FlashWriteBuffer[OTA_FLASH_ALIGN_SIZE]; // Flash写入缓冲区
|
static HIDO_UINT32 l_u32FlashWriteBufferLen = 0; // 缓冲区当前长度
|
static HIDO_UINT32 l_u32FlashWrittenBytes = 0; // 已写入Flash的字节数(不含缓冲区)
|
|
// 解密相关变量
|
static HIDO_UINT8 l_au8DecryptBuffer[OTA_DECRYPT_BUFFER_SIZE]; // 解密缓冲区
|
static HIDO_UINT8 l_au8AESChainBlock[OTA_AES_BLOCK_SIZE]; // AES CBC模式链接块
|
static HIDO_BOOL l_bDecryptTaskRunning = HIDO_FALSE; // 解密任务运行标志
|
static HIDO_UINT32 l_u32DecryptFlashWrittenBytes = 0; // 解密Flash已写入字节数
|
|
// 超时和重试相关变量
|
#define OTA_DOWNLOAD_TIMEOUT_MS 30000 // 下载超时时间:30秒
|
#define OTA_MAX_RETRY_COUNT 3 // 最大重试次数
|
static HIDO_UINT32 l_u32LastProgressTick = 0; // 最后一次有进度的时间戳
|
static HIDO_UINT32 l_u32LastDownloadedSize = 0; // 上次记录的下载大小
|
static HIDO_UINT8 l_u8RetryCount = 0; // 当前重试次数
|
|
/*****************************************************************************
|
* 本地函数声明
|
*****************************************************************************/
|
static HIDO_INT32 OTA_HTTPDownloadCallback(HIDO_UINT32 _u32RespCode,
|
ST_HTTPGetFileResponse *_pstResponse,
|
HIDO_UINT8 *_pu8Data,
|
HIDO_UINT32 _u32Len,
|
HIDO_VOID *_pArg);
|
static HIDO_INT32 OTA_FlashWriteAligned(HIDO_UINT32 _u32BaseAddr,
|
HIDO_UINT32 _u32WrittenBytes,
|
const HIDO_UINT8 *_pu8Data,
|
HIDO_UINT32 _u32Len);
|
static HIDO_INT32 OTA_FlushFlashWriteBuffer(HIDO_UINT32 _u32BaseAddr,
|
HIDO_UINT32 _u32WrittenBytes,
|
HIDO_UINT32 *_pu32GlobalWrittenBytes);
|
static HIDO_VOID OTA_ProcessDecryptTask(HIDO_VOID);
|
|
/*****************************************************************************
|
* 函数实现
|
*****************************************************************************/
|
|
/**
|
* @brief 初始化OTA升级模块
|
*/
|
HIDO_INT32 OTA_Init(HIDO_VOID)
|
{
|
HIDO_INT32 ret;
|
|
HIDO_Debug("[OTA] Initializing OTA module...\r\n");
|
|
// 确保MCUFlash已初始化
|
ret = MCUFlash_Init();
|
if (ret != HIDO_OK) {
|
HIDO_Debug("[OTA] ERROR: MCUFlash_Init failed: %d\r\n", ret);
|
return ret;
|
}
|
|
// 初始化数据结构
|
memset(&l_stOTAInfo, 0, sizeof(l_stOTAInfo));
|
l_stOTAInfo.eState = OTA_STATE_IDLE;
|
l_stOTAInfo.eLastError = OTA_ERR_NONE;
|
|
memset(l_acDownloadUrl, 0, sizeof(l_acDownloadUrl));
|
memset(l_au8FlashWriteBuffer, 0xFF, sizeof(l_au8FlashWriteBuffer));
|
l_u32FlashWriteBufferLen = 0;
|
l_u32FlashWrittenBytes = 0;
|
l_u32DecryptFlashWrittenBytes = 0;
|
l_bDecryptTaskRunning = HIDO_FALSE;
|
|
HIDO_Debug("[OTA] Module initialized successfully\r\n");
|
|
return HIDO_OK;
|
}
|
|
/**
|
* @brief 启动OTA固件下载
|
*/
|
HIDO_INT32 OTA_StartDownload(const HIDO_CHAR *_pcUrl)
|
{
|
ST_HTTPGetFileOpt stOpt;
|
|
// 参数检查
|
if (_pcUrl == HIDO_NULL || strlen(_pcUrl) == 0) {
|
HIDO_Debug("[OTA] Invalid URL\r\n");
|
return OTA_ERR_INVALID_PARAM;
|
}
|
|
// 状态检查
|
if (l_stOTAInfo.eState != OTA_STATE_IDLE &&
|
l_stOTAInfo.eState != OTA_STATE_DOWNLOAD_FAILED) {
|
HIDO_Debug("[OTA] Busy, current state: %d\r\n", l_stOTAInfo.eState);
|
return OTA_ERR_BUSY;
|
}
|
|
// 网络检查
|
if (Internet_IsIPReady() != HIDO_TRUE) {
|
HIDO_Debug("[OTA] Network not ready\r\n");
|
l_stOTAInfo.eLastError = OTA_ERR_NO_NETWORK;
|
return OTA_ERR_NO_NETWORK;
|
}
|
|
// 保存URL
|
HIDO_UtilSnprintf(l_acDownloadUrl, sizeof(l_acDownloadUrl), "%s", _pcUrl);
|
|
// 擦除加密固件存储区
|
HIDO_Debug("[OTA] Erasing encrypted flash area: 0x%08X, size: %d KB\r\n",
|
OTA_ENCRYPTED_FLASH_ADDR, OTA_ENCRYPTED_FLASH_SIZE / 1024);
|
|
if (MCUFlash_Erase(OTA_ENCRYPTED_FLASH_ADDR, OTA_ENCRYPTED_FLASH_SIZE) != HIDO_OK) {
|
HIDO_Debug("[OTA] Flash erase failed\r\n");
|
l_stOTAInfo.eState = OTA_STATE_DOWNLOAD_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_FLASH_ERASE_FAILED;
|
return OTA_ERR_FLASH_ERASE_FAILED;
|
}
|
|
// 重置状态
|
l_stOTAInfo.eState = OTA_STATE_DOWNLOADING;
|
l_stOTAInfo.eLastError = OTA_ERR_NONE;
|
l_stOTAInfo.u32TotalSize = 0;
|
l_stOTAInfo.u32DownloadedSize = 0;
|
l_stOTAInfo.u32DecryptedSize = 0;
|
l_stOTAInfo.u8Progress = 0;
|
l_u32FlashWriteBufferLen = 0;
|
memset(l_au8FlashWriteBuffer, 0xFF, sizeof(l_au8FlashWriteBuffer));
|
|
// 初始化超时检测
|
l_u32LastProgressTick = xTaskGetTickCount();
|
l_u32LastDownloadedSize = 0;
|
|
// 配置HTTP下载选项(支持断点续传)
|
memset(&stOpt, 0, sizeof(stOpt));
|
stOpt.m_u32RangeBegin = 0; // 从头开始下载
|
stOpt.m_u32RangeEnd = 0; // 下载到文件末尾
|
|
// 如果有ETag,使用ETag进行断点续传验证
|
if (strlen(l_stOTAInfo.acETag) > 0) {
|
HIDO_UtilSnprintf(stOpt.m_acIfRange, sizeof(stOpt.m_acIfRange),
|
"%s", l_stOTAInfo.acETag);
|
}
|
|
HIDO_Debug("[OTA] Starting download from: %s\r\n", _pcUrl);
|
|
// 启动HTTP下载
|
HIDO_INT32 i32Ret = HTTPClient_GetFile((HIDO_CHAR *)_pcUrl, OTA_HTTPDownloadCallback, &stOpt, HIDO_NULL);
|
HIDO_Debug("[OTA] HTTPClient_GetFile returned: %d\r\n", i32Ret);
|
|
if (i32Ret != HIDO_OK) {
|
HIDO_Debug("[OTA] HTTPClient_GetFile failed - HTTPClient may be busy\r\n");
|
l_stOTAInfo.eState = OTA_STATE_DOWNLOAD_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_HTTP_FAILED;
|
return OTA_ERR_HTTP_FAILED;
|
}
|
|
return HIDO_OK;
|
}
|
|
/**
|
* @brief HTTP下载回调函数
|
*/
|
static HIDO_INT32 OTA_HTTPDownloadCallback(HIDO_UINT32 _u32RespCode,
|
ST_HTTPGetFileResponse *_pstResponse,
|
HIDO_UINT8 *_pu8Data,
|
HIDO_UINT32 _u32Len,
|
HIDO_VOID *_pArg)
|
{
|
HIDO_INT32 i32Ret;
|
|
HIDO_Debug("[OTA] Callback: RespCode=%d, DataLen=%d\r\n", _u32RespCode, _u32Len);
|
|
// 检查HTTP响应码
|
if (_u32RespCode != 200 && _u32RespCode != 206) {
|
HIDO_Debug("[OTA] HTTP error, response code: %d\r\n", _u32RespCode);
|
l_stOTAInfo.eState = OTA_STATE_DOWNLOAD_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_HTTP_FAILED;
|
|
// 标记为需要重试(而不是立即放弃)
|
// Poll函数会检测到DOWNLOAD_FAILED状态并触发重试
|
return HIDO_ERR;
|
}
|
|
// 首次回调,获取文件总大小
|
if (l_stOTAInfo.u32TotalSize == 0 && _pstResponse != HIDO_NULL) {
|
l_stOTAInfo.u32TotalSize = _pstResponse->m_u32RangeTotal;
|
|
// 检查文件大小是否超过256KB
|
if (l_stOTAInfo.u32TotalSize > OTA_ENCRYPTED_FLASH_SIZE) {
|
HIDO_Debug("[OTA] File too large: %d bytes (max: %d bytes)\r\n",
|
l_stOTAInfo.u32TotalSize, OTA_ENCRYPTED_FLASH_SIZE);
|
l_stOTAInfo.eState = OTA_STATE_DOWNLOAD_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_FILE_TOO_LARGE;
|
return HIDO_ERR;
|
}
|
|
// 保存ETag用于后续断点续传
|
if (strlen(_pstResponse->m_acETag) > 0) {
|
HIDO_UtilSnprintf(l_stOTAInfo.acETag, sizeof(l_stOTAInfo.acETag),
|
"%s", _pstResponse->m_acETag);
|
}
|
|
HIDO_Debug("[OTA] File size: %d bytes (%.1f KB), ETag: %s\r\n",
|
l_stOTAInfo.u32TotalSize,
|
l_stOTAInfo.u32TotalSize / 1024.0f,
|
l_stOTAInfo.acETag);
|
}
|
|
// 接收数据
|
if (_pu8Data != HIDO_NULL && _u32Len > 0) {
|
// 更新进度时间戳(有数据到达)
|
l_u32LastProgressTick = xTaskGetTickCount();
|
l_u32LastDownloadedSize = l_stOTAInfo.u32DownloadedSize;
|
|
// 写入Flash(带对齐处理)- 传递已写入Flash的字节数
|
i32Ret = OTA_FlashWriteAligned(OTA_ENCRYPTED_FLASH_ADDR,
|
l_u32FlashWrittenBytes,
|
_pu8Data, _u32Len);
|
if (i32Ret < 0) {
|
HIDO_Debug("[OTA] Flash write failed at offset: 0x%X\r\n",
|
l_stOTAInfo.u32DownloadedSize);
|
l_stOTAInfo.eState = OTA_STATE_DOWNLOAD_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_FLASH_WRITE_FAILED;
|
return HIDO_ERR;
|
}
|
|
// 更新下载阶段的Flash写入计数器
|
l_u32FlashWrittenBytes += i32Ret;
|
|
l_stOTAInfo.u32DownloadedSize += _u32Len;
|
|
// 更新进度
|
if (l_stOTAInfo.u32TotalSize > 0) {
|
l_stOTAInfo.u8Progress = (HIDO_UINT8)((l_stOTAInfo.u32DownloadedSize * 100) /
|
l_stOTAInfo.u32TotalSize);
|
}
|
|
// 每10%打印一次进度
|
static HIDO_UINT8 s_u8LastLogProgress = 0;
|
if (l_stOTAInfo.u8Progress >= s_u8LastLogProgress + 10) {
|
HIDO_Debug("[OTA] Download progress: %d%% (%d/%d bytes)\r\n",
|
l_stOTAInfo.u8Progress,
|
l_stOTAInfo.u32DownloadedSize,
|
l_stOTAInfo.u32TotalSize);
|
s_u8LastLogProgress = l_stOTAInfo.u8Progress;
|
}
|
}
|
|
// 下载完成
|
if (l_stOTAInfo.u32DownloadedSize >= l_stOTAInfo.u32TotalSize &&
|
l_stOTAInfo.u32TotalSize > 0) {
|
|
// 刷新缓冲区(写入剩余数据)
|
if (l_u32FlashWriteBufferLen > 0) {
|
OTA_FlushFlashWriteBuffer(OTA_ENCRYPTED_FLASH_ADDR,
|
l_u32FlashWrittenBytes,
|
&l_u32FlashWrittenBytes);
|
}
|
|
l_stOTAInfo.eState = OTA_STATE_DOWNLOAD_COMPLETE;
|
l_stOTAInfo.u8Progress = 100;
|
HIDO_Debug("[OTA] Download completed: %d bytes\r\n", l_stOTAInfo.u32DownloadedSize);
|
}
|
|
return HIDO_OK;
|
}
|
|
/**
|
* @brief Flash写入(带32字节对齐处理)
|
* @param _u32BaseAddr Flash基地址(例如0x08100000)
|
* @param _u32WrittenBytes 已成功写入Flash的字节数(不包括缓冲区中的数据)
|
* @param _pu8Data 待写入的数据
|
* @param _u32Len 数据长度
|
* @return HIDO_INT32 成功写入Flash的字节数
|
*/
|
static HIDO_INT32 OTA_FlashWriteAligned(HIDO_UINT32 _u32BaseAddr,
|
HIDO_UINT32 _u32WrittenBytes,
|
const HIDO_UINT8 *_pu8Data,
|
HIDO_UINT32 _u32Len)
|
{
|
HIDO_UINT32 u32WriteLen;
|
HIDO_UINT32 u32Offset = 0;
|
HIDO_UINT32 u32FlashWrittenThisCall = 0; // 本次调用写入Flash的字节数
|
HIDO_INT32 i32Ret;
|
|
while (u32Offset < _u32Len) {
|
// 计算可以写入缓冲区的数据长度
|
u32WriteLen = OTA_FLASH_ALIGN_SIZE - l_u32FlashWriteBufferLen;
|
if (u32WriteLen > (_u32Len - u32Offset)) {
|
u32WriteLen = _u32Len - u32Offset;
|
}
|
|
// 复制到缓冲区
|
memcpy(l_au8FlashWriteBuffer + l_u32FlashWriteBufferLen,
|
_pu8Data + u32Offset, u32WriteLen);
|
l_u32FlashWriteBufferLen += u32WriteLen;
|
u32Offset += u32WriteLen;
|
|
// 缓冲区满了,写入Flash
|
if (l_u32FlashWriteBufferLen >= OTA_FLASH_ALIGN_SIZE) {
|
// 计算实际Flash写入地址 = 基地址 + 历史已写入字节 + 本次调用已写入字节
|
HIDO_UINT32 u32FlashAddr = _u32BaseAddr + _u32WrittenBytes + u32FlashWrittenThisCall;
|
|
// 调试:打印第一次写入的详细信息
|
static HIDO_BOOL s_bFirstWriteLogged = HIDO_FALSE;
|
if (!s_bFirstWriteLogged && u32FlashAddr >= OTA_DECRYPTED_FLASH_ADDR) {
|
HIDO_Debug("[OTA] First decrypt write: Addr=0x%08X, Data[0-7]=%02X %02X %02X %02X %02X %02X %02X %02X\r\n",
|
u32FlashAddr,
|
l_au8FlashWriteBuffer[0], l_au8FlashWriteBuffer[1], l_au8FlashWriteBuffer[2], l_au8FlashWriteBuffer[3],
|
l_au8FlashWriteBuffer[4], l_au8FlashWriteBuffer[5], l_au8FlashWriteBuffer[6], l_au8FlashWriteBuffer[7]);
|
s_bFirstWriteLogged = HIDO_TRUE;
|
}
|
|
i32Ret = MCUFlash_Write(u32FlashAddr, l_au8FlashWriteBuffer, OTA_FLASH_ALIGN_SIZE);
|
if (i32Ret != HIDO_OK) {
|
HIDO_Debug("[OTA] MCUFlash_Write failed at 0x%08X (TotalWritten=0x%X)\r\n",
|
u32FlashAddr, _u32WrittenBytes + u32FlashWrittenThisCall);
|
return HIDO_ERR;
|
}
|
|
u32FlashWrittenThisCall += OTA_FLASH_ALIGN_SIZE;
|
l_u32FlashWriteBufferLen = 0;
|
memset(l_au8FlashWriteBuffer, 0xFF, sizeof(l_au8FlashWriteBuffer));
|
}
|
}
|
|
// 返回本次调用写入Flash的字节数(由调用者更新相应计数器)
|
return u32FlashWrittenThisCall;
|
}
|
|
/**
|
* @brief 刷新Flash写入缓冲区(写入剩余数据)
|
* @param _u32BaseAddr Flash基地址
|
* @param _u32WrittenBytes 已写入的字节数
|
* @param _pu32GlobalWrittenBytes 全局写入计数器指针(用于更新)
|
*/
|
static HIDO_INT32 OTA_FlushFlashWriteBuffer(HIDO_UINT32 _u32BaseAddr,
|
HIDO_UINT32 _u32WrittenBytes,
|
HIDO_UINT32 *_pu32GlobalWrittenBytes)
|
{
|
if (l_u32FlashWriteBufferLen > 0) {
|
// 剩余部分用0xFF填充到32字节对齐
|
memset(l_au8FlashWriteBuffer + l_u32FlashWriteBufferLen, 0xFF,
|
OTA_FLASH_ALIGN_SIZE - l_u32FlashWriteBufferLen);
|
|
HIDO_UINT32 u32FlashAddr = _u32BaseAddr + _u32WrittenBytes;
|
if (MCUFlash_Write(u32FlashAddr, l_au8FlashWriteBuffer, OTA_FLASH_ALIGN_SIZE) != HIDO_OK) {
|
HIDO_Debug("[OTA] Flush buffer failed at 0x%08X\r\n", u32FlashAddr);
|
return HIDO_ERR;
|
}
|
|
// 更新全局写入计数器
|
if (_pu32GlobalWrittenBytes != HIDO_NULL) {
|
*_pu32GlobalWrittenBytes += OTA_FLASH_ALIGN_SIZE;
|
}
|
|
l_u32FlashWriteBufferLen = 0;
|
memset(l_au8FlashWriteBuffer, 0xFF, sizeof(l_au8FlashWriteBuffer));
|
}
|
|
return HIDO_OK;
|
}
|
|
/**
|
* @brief 启动固件解密
|
*/
|
HIDO_INT32 OTA_StartDecrypt(HIDO_VOID)
|
{
|
HIDO_INT32 ret;
|
|
// 状态检查
|
if (l_stOTAInfo.eState != OTA_STATE_DOWNLOAD_COMPLETE) {
|
HIDO_Debug("[OTA] Cannot start decrypt, state: %d\r\n", l_stOTAInfo.eState);
|
return HIDO_ERR;
|
}
|
|
// 擦除解密固件存储区
|
HIDO_Debug("[OTA] Erasing decrypted flash area: 0x%08X, size: %d KB\r\n",
|
OTA_DECRYPTED_FLASH_ADDR, OTA_DECRYPTED_FLASH_SIZE / 1024);
|
|
if (MCUFlash_Erase(OTA_DECRYPTED_FLASH_ADDR, OTA_DECRYPTED_FLASH_SIZE) != HIDO_OK) {
|
HIDO_Debug("[OTA] Flash erase failed\r\n");
|
l_stOTAInfo.eState = OTA_STATE_DECRYPT_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_FLASH_ERASE_FAILED;
|
return OTA_ERR_FLASH_ERASE_FAILED;
|
}
|
|
HIDO_Debug("[OTA] Flash erased, waiting for stabilization...\r\n");
|
vTaskDelay(100); // 延迟100ms等待Flash稳定
|
|
// 重新初始化Flash(确保擦除后状态正确)
|
ret = MCUFlash_Init();
|
if (ret != HIDO_OK) {
|
HIDO_Debug("[OTA] Flash re-init failed: %d\r\n", ret);
|
l_stOTAInfo.eState = OTA_STATE_DECRYPT_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_FLASH_ERASE_FAILED;
|
return OTA_ERR_FLASH_ERASE_FAILED;
|
}
|
HIDO_Debug("[OTA] Flash re-initialized\r\n");
|
|
// 验证Flash是否已擦除(检查前64字节)
|
HIDO_UINT8 au8VerifyBuf[64];
|
if (MCUFlash_Read(OTA_DECRYPTED_FLASH_ADDR, au8VerifyBuf, 64) == HIDO_OK) {
|
HIDO_BOOL bEraseOk = HIDO_TRUE;
|
for (HIDO_UINT32 i = 0; i < 64; i++) {
|
if (au8VerifyBuf[i] != 0xFF) {
|
bEraseOk = HIDO_FALSE;
|
HIDO_Debug("[OTA] Erase verify FAILED at offset 0x%X: value=0x%02X\r\n", i, au8VerifyBuf[i]);
|
// 打印前16字节内容
|
HIDO_Debug("[OTA] First 16 bytes: ");
|
for (HIDO_UINT32 j = 0; j < 16; j++) {
|
HIDO_Debug("%02X ", au8VerifyBuf[j]);
|
}
|
HIDO_Debug("\r\n");
|
break;
|
}
|
}
|
if (bEraseOk) {
|
HIDO_Debug("[OTA] Erase verification PASSED (first 64 bytes all 0xFF)\r\n");
|
} else {
|
HIDO_Debug("[OTA] WARNING: Flash may not be fully erased, write may fail\r\n");
|
}
|
} else {
|
HIDO_Debug("[OTA] Erase verification read failed\r\n");
|
}
|
|
// 初始化AES解密
|
aesDecInit();
|
memset(l_au8AESChainBlock, 0, sizeof(l_au8AESChainBlock)); // CBC模式IV为全0
|
|
l_stOTAInfo.eState = OTA_STATE_DECRYPTING;
|
l_stOTAInfo.u32DecryptedSize = 0;
|
l_stOTAInfo.u8Progress = 0;
|
l_u32FlashWriteBufferLen = 0;
|
l_u32DecryptFlashWrittenBytes = 0; // 重置解密Flash写入计数器
|
memset(l_au8FlashWriteBuffer, 0xFF, sizeof(l_au8FlashWriteBuffer));
|
l_bDecryptTaskRunning = HIDO_TRUE;
|
|
HIDO_Debug("[OTA] Decryption started, size: %d bytes\r\n", l_stOTAInfo.u32TotalSize);
|
|
return HIDO_OK;
|
}
|
|
/**
|
* @brief 处理解密任务(分块执行,避免阻塞)
|
*/
|
static HIDO_VOID OTA_ProcessDecryptTask(HIDO_VOID)
|
{
|
HIDO_UINT32 u32ReadLen;
|
HIDO_UINT32 i;
|
HIDO_INT32 i32Ret;
|
|
if (!l_bDecryptTaskRunning) {
|
return;
|
}
|
|
// 每次处理一块数据(512字节)
|
if (l_stOTAInfo.u32DecryptedSize >= l_stOTAInfo.u32TotalSize) {
|
// 解密完成,刷新缓冲区
|
if (l_u32FlashWriteBufferLen > 0) {
|
OTA_FlushFlashWriteBuffer(OTA_DECRYPTED_FLASH_ADDR,
|
l_u32DecryptFlashWrittenBytes,
|
&l_u32DecryptFlashWrittenBytes);
|
}
|
|
l_bDecryptTaskRunning = HIDO_FALSE;
|
l_stOTAInfo.eState = OTA_STATE_DECRYPT_COMPLETE;
|
l_stOTAInfo.u8Progress = 100;
|
HIDO_Debug("[OTA] Decryption completed: %d bytes\r\n", l_stOTAInfo.u32DecryptedSize);
|
return;
|
}
|
|
// 读取加密数据
|
u32ReadLen = OTA_DECRYPT_BUFFER_SIZE;
|
if (l_stOTAInfo.u32DecryptedSize + u32ReadLen > l_stOTAInfo.u32TotalSize) {
|
u32ReadLen = l_stOTAInfo.u32TotalSize - l_stOTAInfo.u32DecryptedSize;
|
}
|
|
i32Ret = MCUFlash_Read(OTA_ENCRYPTED_FLASH_ADDR + l_stOTAInfo.u32DecryptedSize,
|
l_au8DecryptBuffer, u32ReadLen);
|
if (i32Ret != HIDO_OK) {
|
HIDO_Debug("[OTA] Read encrypted flash failed at offset: 0x%X\r\n",
|
l_stOTAInfo.u32DecryptedSize);
|
l_stOTAInfo.eState = OTA_STATE_DECRYPT_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_DECRYPT_FAILED;
|
l_bDecryptTaskRunning = HIDO_FALSE;
|
return;
|
}
|
|
// 调试:打印第一次读取的前16字节
|
if (l_stOTAInfo.u32DecryptedSize == 0) {
|
HIDO_Debug("[OTA] First encrypted block: %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
|
l_au8DecryptBuffer[0], l_au8DecryptBuffer[1], l_au8DecryptBuffer[2], l_au8DecryptBuffer[3],
|
l_au8DecryptBuffer[4], l_au8DecryptBuffer[5], l_au8DecryptBuffer[6], l_au8DecryptBuffer[7]);
|
}
|
|
// AES-CBC解密(每16字节一块)
|
for (i = 0; i < u32ReadLen; i += OTA_AES_BLOCK_SIZE) {
|
aesDecrypt(l_au8DecryptBuffer + i, l_au8AESChainBlock);
|
}
|
|
// 调试:打印第一次解密的前16字节
|
if (l_stOTAInfo.u32DecryptedSize == 0) {
|
HIDO_Debug("[OTA] First decrypted block: %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
|
l_au8DecryptBuffer[0], l_au8DecryptBuffer[1], l_au8DecryptBuffer[2], l_au8DecryptBuffer[3],
|
l_au8DecryptBuffer[4], l_au8DecryptBuffer[5], l_au8DecryptBuffer[6], l_au8DecryptBuffer[7]);
|
HIDO_Debug("[OTA] Writing to: BaseAddr=0x%08X, WrittenBytes=0x%X, Len=0x%X\r\n",
|
OTA_DECRYPTED_FLASH_ADDR, l_u32DecryptFlashWrittenBytes, u32ReadLen);
|
}
|
|
// 写入解密后的数据
|
i32Ret = OTA_FlashWriteAligned(OTA_DECRYPTED_FLASH_ADDR,
|
l_u32DecryptFlashWrittenBytes,
|
l_au8DecryptBuffer, u32ReadLen);
|
if (i32Ret < 0) {
|
HIDO_Debug("[OTA] Write decrypted flash failed at offset: 0x%X\r\n",
|
l_stOTAInfo.u32DecryptedSize);
|
l_stOTAInfo.eState = OTA_STATE_DECRYPT_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_FLASH_WRITE_FAILED;
|
l_bDecryptTaskRunning = HIDO_FALSE;
|
return;
|
}
|
|
// 更新解密阶段的Flash写入计数器
|
l_u32DecryptFlashWrittenBytes += i32Ret;
|
|
l_stOTAInfo.u32DecryptedSize += u32ReadLen;
|
|
// 更新进度
|
l_stOTAInfo.u8Progress = (HIDO_UINT8)((l_stOTAInfo.u32DecryptedSize * 100) /
|
l_stOTAInfo.u32TotalSize);
|
|
// 每10%打印一次进度
|
static HIDO_UINT8 s_u8LastLogProgress = 0;
|
if (l_stOTAInfo.u8Progress >= s_u8LastLogProgress + 10) {
|
HIDO_Debug("[OTA] Decrypt progress: %d%% (%d/%d bytes)\r\n",
|
l_stOTAInfo.u8Progress,
|
l_stOTAInfo.u32DecryptedSize,
|
l_stOTAInfo.u32TotalSize);
|
s_u8LastLogProgress = l_stOTAInfo.u8Progress;
|
}
|
}
|
|
/**
|
* @brief 启动固件校验
|
*/
|
HIDO_INT32 OTA_VerifyFirmware(HIDO_VOID)
|
{
|
HIDO_UINT32 u32CalculatedSum = 0;
|
HIDO_UINT32 u32StoredSum = 0;
|
HIDO_UINT32 i;
|
HIDO_UINT8 u8Byte;
|
HIDO_UINT8 au8ChecksumBytes[4];
|
|
// 状态检查
|
if (l_stOTAInfo.eState != OTA_STATE_DECRYPT_COMPLETE) {
|
HIDO_Debug("[OTA] Cannot verify, state: %d\r\n", l_stOTAInfo.eState);
|
return HIDO_ERR;
|
}
|
|
l_stOTAInfo.eState = OTA_STATE_VERIFYING;
|
HIDO_Debug("[OTA] Verifying firmware checksum...\r\n");
|
|
// 文件格式:[固件数据][校验和4字节][0x0D][0x0A]
|
// 从文件末尾向前查找 0x0D 0x0A 结束符
|
HIDO_UINT32 u32TerminatorPos = 0; // 0x0D的位置
|
HIDO_BOOL bFoundTerminator = HIDO_FALSE;
|
HIDO_UINT8 au8SearchBuf[64]; // 只搜索末尾64字节
|
HIDO_UINT32 u32SearchStart = (l_stOTAInfo.u32TotalSize > 64) ? (l_stOTAInfo.u32TotalSize - 64) : 0;
|
HIDO_UINT32 u32SearchLen = l_stOTAInfo.u32TotalSize - u32SearchStart;
|
|
// 读取文件末尾数据
|
if (MCUFlash_Read(OTA_DECRYPTED_FLASH_ADDR + u32SearchStart, au8SearchBuf, u32SearchLen) != HIDO_OK) {
|
HIDO_Debug("[OTA] Read flash failed for terminator search\r\n");
|
l_stOTAInfo.eState = OTA_STATE_VERIFY_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_DECRYPT_FAILED;
|
return HIDO_ERR;
|
}
|
|
// 从后向前搜索 0x0D 0x0A
|
for (i = u32SearchLen; i >= 2; i--) {
|
if (au8SearchBuf[i-2] == OTA_FILE_TERMINATOR_CR &&
|
au8SearchBuf[i-1] == OTA_FILE_TERMINATOR_LF) {
|
u32TerminatorPos = u32SearchStart + i - 2; // 0x0D的位置
|
bFoundTerminator = HIDO_TRUE;
|
HIDO_Debug("[OTA] Found terminator (0x0D 0x0A) at offset: 0x%X\r\n", u32TerminatorPos);
|
break;
|
}
|
}
|
|
// 未找到结束符
|
if (!bFoundTerminator) {
|
HIDO_Debug("[OTA] ERROR: Terminator (0x0D 0x0A) not found in file\r\n");
|
l_stOTAInfo.eState = OTA_STATE_VERIFY_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_DECRYPT_FAILED;
|
return HIDO_ERR;
|
}
|
|
// 校验文件最小长度:至少需要6字节(校验4字节+0x0D+0x0A)
|
if (u32TerminatorPos < 4) {
|
HIDO_Debug("[OTA] ERROR: File too short, terminator at 0x%X\r\n", u32TerminatorPos);
|
l_stOTAInfo.eState = OTA_STATE_VERIFY_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_DECRYPT_FAILED;
|
return HIDO_ERR;
|
}
|
|
// 校验和位置:0x0D前4字节
|
HIDO_UINT32 u32ChecksumPos = u32TerminatorPos - 4;
|
// 固件数据结束位置:校验和之前
|
HIDO_UINT32 u32DataEndPos = u32ChecksumPos;
|
|
HIDO_Debug("[OTA] Firmware data range: 0x0 - 0x%X (%d bytes)\r\n",
|
u32DataEndPos - 1, u32DataEndPos);
|
HIDO_Debug("[OTA] Checksum position: 0x%X - 0x%X\r\n",
|
u32ChecksumPos, u32ChecksumPos + 3);
|
|
// 计算校验和(固件数据部分,不包括校验和和结束符)
|
for (i = 0; i < u32DataEndPos; i++) {
|
if (MCUFlash_Read(OTA_DECRYPTED_FLASH_ADDR + i, &u8Byte, 1) != HIDO_OK) {
|
HIDO_Debug("[OTA] Read flash failed at offset: 0x%X\r\n", i);
|
l_stOTAInfo.eState = OTA_STATE_VERIFY_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_DECRYPT_FAILED;
|
return HIDO_ERR;
|
}
|
u32CalculatedSum += u8Byte;
|
}
|
u32CalculatedSum &= 0xFFFFFFFF;
|
|
// 读取存储的校验和(0x0D前4字节,小端序)
|
if (MCUFlash_Read(OTA_DECRYPTED_FLASH_ADDR + u32ChecksumPos,
|
au8ChecksumBytes, 4) != HIDO_OK) {
|
HIDO_Debug("[OTA] Read checksum failed\r\n");
|
l_stOTAInfo.eState = OTA_STATE_VERIFY_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_DECRYPT_FAILED;
|
return HIDO_ERR;
|
}
|
|
// 小端序转换
|
u32StoredSum = au8ChecksumBytes[0] |
|
(au8ChecksumBytes[1] << 8) |
|
(au8ChecksumBytes[2] << 16) |
|
(au8ChecksumBytes[3] << 24);
|
|
HIDO_Debug("[OTA] Calculated checksum: 0x%08X\r\n", u32CalculatedSum);
|
HIDO_Debug("[OTA] Stored checksum: 0x%08X\r\n", u32StoredSum);
|
|
// 对比校验和
|
if (u32CalculatedSum == u32StoredSum) {
|
l_stOTAInfo.eState = OTA_STATE_VERIFY_SUCCESS;
|
l_stOTAInfo.eLastError = OTA_ERR_NONE;
|
HIDO_Debug("[OTA] Firmware verification SUCCESS!\r\n");
|
return HIDO_OK;
|
} else {
|
l_stOTAInfo.eState = OTA_STATE_VERIFY_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_CHECKSUM_MISMATCH;
|
HIDO_Debug("[OTA] Firmware verification FAILED! Checksum mismatch\r\n");
|
return OTA_ERR_CHECKSUM_MISMATCH;
|
}
|
}
|
|
/**
|
* @brief 获取OTA升级信息
|
*/
|
HIDO_INT32 OTA_GetInfo(ST_OTAInfo *_pstInfo)
|
{
|
if (_pstInfo == HIDO_NULL) {
|
return HIDO_ERR;
|
}
|
|
memcpy(_pstInfo, &l_stOTAInfo, sizeof(ST_OTAInfo));
|
return HIDO_OK;
|
}
|
|
/**
|
* @brief 获取当前OTA状态
|
*/
|
E_OTAState OTA_GetState(HIDO_VOID)
|
{
|
return l_stOTAInfo.eState;
|
}
|
|
/**
|
* @brief 获取下载进度百分比
|
*/
|
HIDO_UINT8 OTA_GetProgress(HIDO_VOID)
|
{
|
return l_stOTAInfo.u8Progress;
|
}
|
|
/**
|
* @brief 取消当前OTA操作
|
*/
|
HIDO_INT32 OTA_Cancel(HIDO_VOID)
|
{
|
if (l_stOTAInfo.eState == OTA_STATE_DOWNLOADING) {
|
HTTPClient_Reset();
|
}
|
|
l_bDecryptTaskRunning = HIDO_FALSE;
|
l_stOTAInfo.eState = OTA_STATE_IDLE;
|
|
HIDO_Debug("[OTA] Operation cancelled\r\n");
|
return HIDO_OK;
|
}
|
|
/**
|
* @brief 重置OTA模块到空闲状态
|
*/
|
HIDO_VOID OTA_Reset(HIDO_VOID)
|
{
|
l_bDecryptTaskRunning = HIDO_FALSE;
|
memset(&l_stOTAInfo, 0, sizeof(l_stOTAInfo));
|
l_stOTAInfo.eState = OTA_STATE_IDLE;
|
l_stOTAInfo.eLastError = OTA_ERR_NONE;
|
memset(l_acDownloadUrl, 0, sizeof(l_acDownloadUrl));
|
|
HIDO_Debug("[OTA] Module reset\r\n");
|
}
|
|
/**
|
* @brief OTA模块周期性任务
|
*/
|
HIDO_VOID OTA_Poll(HIDO_VOID)
|
{
|
HIDO_UINT32 u32CurrentTick = xTaskGetTickCount();
|
HIDO_UINT32 u32ElapsedMs = u32CurrentTick - l_u32LastProgressTick;
|
|
// 检查下载超时或失败
|
if (l_stOTAInfo.eState == OTA_STATE_DOWNLOADING) {
|
// 如果超过30秒没有进度,认为下载超时
|
if (u32ElapsedMs > OTA_DOWNLOAD_TIMEOUT_MS) {
|
HIDO_Debug("[OTA] Download timeout! No data received for %d ms\r\n", u32ElapsedMs);
|
HIDO_Debug("[OTA] Downloaded: %d/%d bytes\r\n",
|
l_stOTAInfo.u32DownloadedSize, l_stOTAInfo.u32TotalSize);
|
|
// 标记为失败,触发重试逻辑
|
l_stOTAInfo.eState = OTA_STATE_DOWNLOAD_FAILED;
|
}
|
}
|
|
// 处理下载失败,尝试重试
|
if (l_stOTAInfo.eState == OTA_STATE_DOWNLOAD_FAILED) {
|
// 检查是否还有重试机会
|
if (l_u8RetryCount < OTA_MAX_RETRY_COUNT) {
|
l_u8RetryCount++;
|
HIDO_Debug("[OTA] Retry attempt %d/%d...\r\n",
|
l_u8RetryCount, OTA_MAX_RETRY_COUNT);
|
|
// 重置HTTP客户端状态(关闭之前的连接)
|
HIDO_Debug("[OTA] Resetting HTTP client...\r\n");
|
HTTPClient_Reset();
|
|
// 重新开始下载
|
l_stOTAInfo.eState = OTA_STATE_IDLE;
|
vTaskDelay(2000); // 延迟2秒确保连接完全关闭
|
|
if (OTA_StartDownload(l_acDownloadUrl) != HIDO_OK) {
|
HIDO_Debug("[OTA] Retry failed to start download\r\n");
|
l_stOTAInfo.eState = OTA_STATE_DOWNLOAD_FAILED;
|
l_stOTAInfo.eLastError = OTA_ERR_HTTP_FAILED;
|
// 注意:不要在这里return,让它继续循环重试
|
}
|
} else {
|
// 已达到最大重试次数,彻底放弃
|
HIDO_Debug("[OTA] Download failed after %d retries - GIVING UP\r\n", OTA_MAX_RETRY_COUNT);
|
l_stOTAInfo.eState = OTA_STATE_IDLE; // 回到空闲状态,停止重试
|
l_stOTAInfo.eLastError = OTA_ERR_HTTP_FAILED;
|
l_u8RetryCount = 0; // 重置重试计数
|
}
|
}
|
|
// 如果下载成功完成,重置重试计数
|
if (l_stOTAInfo.eState == OTA_STATE_DOWNLOAD_COMPLETE) {
|
if (l_u8RetryCount > 0) {
|
HIDO_Debug("[OTA] Download completed after %d retry(ies)\r\n", l_u8RetryCount);
|
l_u8RetryCount = 0;
|
}
|
}
|
|
// 处理解密任务
|
if (l_stOTAInfo.eState == OTA_STATE_DECRYPTING && l_bDecryptTaskRunning) {
|
OTA_ProcessDecryptTask();
|
}
|
}
|