| | |
| | | #include "HIDO_Debug.h" |
| | | #include "HIDO_Util.h" |
| | | #include "string.h" |
| | | #include "BootMark.h" // Boot标记管理模块 |
| | | #include "SoftCRC.h" // 软件CRC |
| | | |
| | | // 引入AES解密算法 |
| | | #include "../decryption/aes.h" |
| | |
| | | { |
| | | HIDO_INT32 i32Ret; |
| | | |
| | | HIDO_Debug("[OTA] Callback: RespCode=%d, DataLen=%d\r\n", _u32RespCode, _u32Len); |
| | | HIDO_Debug("[OTA] Callback: RespCode=%d, DataLen=%d, State=%d\r\n", |
| | | _u32RespCode, _u32Len, l_stOTAInfo.eState); |
| | | |
| | | // 如果已经进入解密/校验阶段,忽略后续的下载回调(HTTP连接关闭时的回调) |
| | | if (l_stOTAInfo.eState >= OTA_STATE_DECRYPTING) { |
| | | HIDO_Debug("[OTA] Ignoring download callback (already in decrypt/verify phase)\r\n"); |
| | | return HIDO_OK; |
| | | } |
| | | |
| | | // 检查HTTP响应码 |
| | | if (_u32RespCode != 200 && _u32RespCode != 206) { |
| | |
| | | i32Ret = OTA_FlashWriteAligned(OTA_ENCRYPTED_FLASH_ADDR, |
| | | l_u32FlashWrittenBytes, |
| | | _pu8Data, _u32Len); |
| | | |
| | | HIDO_Debug("[OTA-DBG] FlashWriteAligned ret=%d\r\n", i32Ret); |
| | | |
| | | if (i32Ret < 0) { |
| | | HIDO_Debug("[OTA] Flash write failed at offset: 0x%X\r\n", |
| | | l_stOTAInfo.u32DownloadedSize); |
| | |
| | | |
| | | // 每10%打印一次进度 |
| | | static HIDO_UINT8 s_u8LastLogProgress = 0; |
| | | if (l_stOTAInfo.u8Progress == 0) { |
| | | s_u8LastLogProgress = 0; |
| | | } |
| | | |
| | | if (l_stOTAInfo.u8Progress >= s_u8LastLogProgress + 10) { |
| | | HIDO_Debug("[OTA] Download progress: %d%% (%d/%d bytes)\r\n", |
| | | l_stOTAInfo.u8Progress, |
| | |
| | | } |
| | | } |
| | | |
| | | HIDO_Debug("[OTA-DBG] Check completion: Downloaded=%d, Total=%d\r\n", |
| | | l_stOTAInfo.u32DownloadedSize, l_stOTAInfo.u32TotalSize); |
| | | |
| | | // 下载完成 |
| | | if (l_stOTAInfo.u32DownloadedSize >= l_stOTAInfo.u32TotalSize && |
| | | l_stOTAInfo.u32TotalSize > 0) { |
| | | |
| | | HIDO_Debug("[OTA] Download complete check passed, flushing buffer...\r\n"); |
| | | |
| | | // 刷新缓冲区(写入剩余数据) |
| | | if (l_u32FlashWriteBufferLen > 0) { |
| | | HIDO_Debug("[OTA] Flushing %d bytes...\r\n", l_u32FlashWriteBufferLen); |
| | | OTA_FlushFlashWriteBuffer(OTA_ENCRYPTED_FLASH_ADDR, |
| | | l_u32FlashWrittenBytes, |
| | | &l_u32FlashWrittenBytes); |
| | | HIDO_Debug("[OTA] Flush completed\r\n"); |
| | | } |
| | | |
| | | l_stOTAInfo.eState = OTA_STATE_DOWNLOAD_COMPLETE; |
| | |
| | | |
| | | 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); |
| | | HIDO_Debug("[OTA] MCUFlash_Write failed at 0x%08X (TotalWritten=0x%X), ret=%d\r\n", |
| | | u32FlashAddr, _u32WrittenBytes + u32FlashWrittenThisCall, i32Ret); |
| | | return HIDO_ERR; |
| | | } |
| | | |
| | |
| | | HIDO_UINT32 *_pu32GlobalWrittenBytes) |
| | | { |
| | | if (l_u32FlashWriteBufferLen > 0) { |
| | | // 剩余部分用0xFF填充到32字节对齐 |
| | | HIDO_Debug("[OTA] Flush: bufLen=%d, addr=0x%08X\r\n", |
| | | l_u32FlashWriteBufferLen, _u32BaseAddr + _u32WrittenBytes); |
| | | |
| | | // 剩余部分用00xFF填充到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] Calling MCUFlash_Write...\r\n"); |
| | | HIDO_INT32 ret = MCUFlash_Write(u32FlashAddr, l_au8FlashWriteBuffer, OTA_FLASH_ALIGN_SIZE); |
| | | HIDO_Debug("[OTA] MCUFlash_Write returned: %d\r\n", ret); |
| | | |
| | | if (ret != HIDO_OK) { |
| | | HIDO_Debug("[OTA] Flush buffer failed at 0x%08X\r\n", u32FlashAddr); |
| | | return HIDO_ERR; |
| | | } |
| | |
| | | |
| | | /** |
| | | * @brief 启动固件解密 |
| | | * @details 判断当前运行的APP,决定目标APP地址,直接解密到目标APP区域 |
| | | */ |
| | | HIDO_INT32 OTA_StartDecrypt(HIDO_VOID) |
| | | { |
| | | HIDO_INT32 ret; |
| | | uint32_t vtor; |
| | | uint32_t targetAppAddr; |
| | | |
| | | // 状态检查 |
| | | if (l_stOTAInfo.eState != OTA_STATE_DOWNLOAD_COMPLETE) { |
| | |
| | | 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); |
| | | // 目标始终是暂存区 (Single Bank with Staging) |
| | | targetAppAddr = BOOT_OTA_STAGING_ADDR; |
| | | HIDO_Debug("[OTA] Target: Staging Area (0x%08X)\r\n", targetAppAddr); |
| | | |
| | | if (MCUFlash_Erase(OTA_DECRYPTED_FLASH_ADDR, OTA_DECRYPTED_FLASH_SIZE) != HIDO_OK) { |
| | | // 保存目标APP地址 |
| | | l_stOTAInfo.u32TargetAppAddr = targetAppAddr; |
| | | |
| | | // 擦除目标APP区域(256KB) |
| | | HIDO_Debug("[OTA] Erasing target APP area: 0x%08X, size: %d KB\r\n", |
| | | targetAppAddr, BOOT_APP_MAX_SIZE / 1024); |
| | | |
| | | if (MCUFlash_Erase(targetAppAddr, BOOT_APP_MAX_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; |
| | |
| | | |
| | | // 验证Flash是否已擦除(检查前64字节) |
| | | HIDO_UINT8 au8VerifyBuf[64]; |
| | | if (MCUFlash_Read(OTA_DECRYPTED_FLASH_ADDR, au8VerifyBuf, 64) == HIDO_OK) { |
| | | if (MCUFlash_Read(targetAppAddr, au8VerifyBuf, 64) == HIDO_OK) { |
| | | HIDO_BOOL bEraseOk = HIDO_TRUE; |
| | | for (HIDO_UINT32 i = 0; i < 64; i++) { |
| | | if (au8VerifyBuf[i] != 0xFF) { |
| | |
| | | if (l_stOTAInfo.u32DecryptedSize >= l_stOTAInfo.u32TotalSize) { |
| | | // 解密完成,刷新缓冲区 |
| | | if (l_u32FlashWriteBufferLen > 0) { |
| | | OTA_FlushFlashWriteBuffer(OTA_DECRYPTED_FLASH_ADDR, |
| | | OTA_FlushFlashWriteBuffer(l_stOTAInfo.u32TargetAppAddr, |
| | | l_u32DecryptFlashWrittenBytes, |
| | | &l_u32DecryptFlashWrittenBytes); |
| | | } |
| | |
| | | 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); |
| | | l_stOTAInfo.u32TargetAppAddr, l_u32DecryptFlashWrittenBytes, u32ReadLen); |
| | | } |
| | | |
| | | // 写入解密后的数据 |
| | | i32Ret = OTA_FlashWriteAligned(OTA_DECRYPTED_FLASH_ADDR, |
| | | // 写入解密后的数据到目标APP地址 |
| | | i32Ret = OTA_FlashWriteAligned(l_stOTAInfo.u32TargetAppAddr, |
| | | l_u32DecryptFlashWrittenBytes, |
| | | l_au8DecryptBuffer, u32ReadLen); |
| | | if (i32Ret < 0) { |
| | |
| | | HIDO_UINT32 u32SearchLen = l_stOTAInfo.u32TotalSize - u32SearchStart; |
| | | |
| | | // 读取文件末尾数据 |
| | | if (MCUFlash_Read(OTA_DECRYPTED_FLASH_ADDR + u32SearchStart, au8SearchBuf, u32SearchLen) != HIDO_OK) { |
| | | if (MCUFlash_Read(l_stOTAInfo.u32TargetAppAddr + 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; |
| | |
| | | |
| | | // 计算校验和(固件数据部分,不包括校验和和结束符) |
| | | for (i = 0; i < u32DataEndPos; i++) { |
| | | if (MCUFlash_Read(OTA_DECRYPTED_FLASH_ADDR + i, &u8Byte, 1) != HIDO_OK) { |
| | | if (MCUFlash_Read(l_stOTAInfo.u32TargetAppAddr + 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; |
| | |
| | | u32CalculatedSum &= 0xFFFFFFFF; |
| | | |
| | | // 读取存储的校验和(0x0D前4字节,小端序) |
| | | if (MCUFlash_Read(OTA_DECRYPTED_FLASH_ADDR + u32ChecksumPos, |
| | | if (MCUFlash_Read(l_stOTAInfo.u32TargetAppAddr + u32ChecksumPos, |
| | | au8ChecksumBytes, 4) != HIDO_OK) { |
| | | HIDO_Debug("[OTA] Read checksum failed\r\n"); |
| | | l_stOTAInfo.eState = OTA_STATE_VERIFY_FAILED; |
| | |
| | | l_stOTAInfo.eState = OTA_STATE_VERIFY_SUCCESS; |
| | | l_stOTAInfo.eLastError = OTA_ERR_NONE; |
| | | HIDO_Debug("[OTA] Firmware verification SUCCESS!\r\n"); |
| | | |
| | | // 校验成功后,更新Boot标记 |
| | | OTA_UpdateBootMark(); |
| | | |
| | | return HIDO_OK; |
| | | } else { |
| | | l_stOTAInfo.eState = OTA_STATE_VERIFY_FAILED; |
| | |
| | | |
| | | // 检查下载超时或失败 |
| | | if (l_stOTAInfo.eState == OTA_STATE_DOWNLOADING) { |
| | | // 检查是否已经下载完成但状态未更新(可能卡在回调函数中) |
| | | if (l_stOTAInfo.u32DownloadedSize >= l_stOTAInfo.u32TotalSize && |
| | | l_stOTAInfo.u32TotalSize > 0 && |
| | | u32ElapsedMs > 5000) { // 5秒后还没转换状态 |
| | | HIDO_Debug("[OTA] WARNING: Download finished but state not updated! Forcing complete...\r\n"); |
| | | l_stOTAInfo.eState = OTA_STATE_DOWNLOAD_COMPLETE; |
| | | } |
| | | |
| | | // 如果超过30秒没有进度,认为下载超时 |
| | | if (u32ElapsedMs > OTA_DOWNLOAD_TIMEOUT_MS) { |
| | | HIDO_Debug("[OTA] Download timeout! No data received for %d ms\r\n", u32ElapsedMs); |
| | |
| | | } |
| | | } |
| | | |
| | | // 处理下载失败,尝试重试 |
| | | if (l_stOTAInfo.eState == OTA_STATE_DOWNLOAD_FAILED) { |
| | | // 处理失败(下载、解密或校验失败),尝试重试 |
| | | if (l_stOTAInfo.eState == OTA_STATE_DOWNLOAD_FAILED || |
| | | l_stOTAInfo.eState == OTA_STATE_DECRYPT_FAILED || |
| | | l_stOTAInfo.eState == OTA_STATE_VERIFY_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); |
| | | HIDO_Debug("[OTA] Error detected (State=%d). Retry attempt %d/%d...\r\n", |
| | | l_stOTAInfo.eState, l_u8RetryCount, OTA_MAX_RETRY_COUNT); |
| | | |
| | | // 重置HTTP客户端状态(关闭之前的连接) |
| | | HIDO_Debug("[OTA] Resetting HTTP client...\r\n"); |
| | |
| | | } |
| | | } else { |
| | | // 已达到最大重试次数,彻底放弃 |
| | | HIDO_Debug("[OTA] Download failed after %d retries - GIVING UP\r\n", OTA_MAX_RETRY_COUNT); |
| | | HIDO_Debug("[OTA] Operation 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; // 重置重试计数 |
| | |
| | | OTA_ProcessDecryptTask(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * @brief 更新Boot标记(OTA升级成功后调用) |
| | | * @details 根据当前运行的APP,将新固件标记写入另一个APP区域 |
| | | */ |
| | | HIDO_VOID OTA_UpdateBootMark(HIDO_VOID) |
| | | { |
| | | int32_t ret; |
| | | BootMark_t bootMark; |
| | | uint32_t targetAppAddr; |
| | | uint32_t firmwareSize; |
| | | uint32_t firmwareCRC; |
| | | |
| | | HIDO_Debug("[OTA] Updating boot mark...\r\n"); |
| | | |
| | | // 使用解密阶段已确定的目标APP地址 (Staging) |
| | | targetAppAddr = l_stOTAInfo.u32TargetAppAddr; |
| | | |
| | | // 计算解密后固件的大小(不包含校验和和0x0D 0x0A) |
| | | // 固件实际大小 = u32TotalSize - 4字节校验和 - 2字节结束符 |
| | | firmwareSize = l_stOTAInfo.u32TotalSize - 6; |
| | | |
| | | // 计算固件的CRC32(从暂存区读取) |
| | | uint32_t wordCount = (firmwareSize + 3) / 4; // 向上取整到32位字 |
| | | firmwareCRC = SoftCRC_Calculate((uint32_t *)targetAppAddr, wordCount); |
| | | |
| | | HIDO_Debug("[OTA] Firmware size: %u bytes, CRC32: 0x%08X\r\n", |
| | | firmwareSize, firmwareCRC); |
| | | |
| | | // 读取当前Boot标记以获取版本号(如果有的话) |
| | | uint32_t newVersion = 1; |
| | | BootMark_t currentMark; |
| | | if (BootMark_Read(¤tMark) == BOOT_OK) { |
| | | newVersion = currentMark.u32Version + 1; |
| | | } |
| | | |
| | | // 填充新的Boot标记 |
| | | memset(&bootMark, 0, sizeof(BootMark_t)); |
| | | bootMark.u32Magic = BOOT_MARK_MAGIC; |
| | | bootMark.u32UpdateFlag = BOOT_UPDATE_PENDING; // 标记为待更新 |
| | | bootMark.u32FirmwareSize = firmwareSize; |
| | | bootMark.u32FirmwareCRC = firmwareCRC; |
| | | bootMark.u32Version = newVersion; |
| | | bootMark.u32SrcAddress = targetAppAddr; // 源:暂存区 |
| | | bootMark.u32DstAddress = BOOT_APP_ADDR; // 目标:APP运行区 |
| | | bootMark.u32BootMarkCRC = BootMark_CalculateCRC(&bootMark); |
| | | |
| | | // 调试:打印Boot标记内容 |
| | | HIDO_Debug("[OTA] BootMark before write:\r\n"); |
| | | HIDO_Debug(" Magic: 0x%08X\r\n", bootMark.u32Magic); |
| | | HIDO_Debug(" Update: 0x%08X\r\n", bootMark.u32UpdateFlag); |
| | | HIDO_Debug(" Size: %u\r\n", bootMark.u32FirmwareSize); |
| | | HIDO_Debug(" CRC: 0x%08X\r\n", bootMark.u32FirmwareCRC); |
| | | HIDO_Debug(" Version: %u\r\n", bootMark.u32Version); |
| | | HIDO_Debug(" Src: 0x%08X\r\n", bootMark.u32SrcAddress); |
| | | HIDO_Debug(" Dst: 0x%08X\r\n", bootMark.u32DstAddress); |
| | | HIDO_Debug(" MarkCRC: 0x%08X\r\n", bootMark.u32BootMarkCRC); |
| | | |
| | | // 写入Boot标记 |
| | | HIDO_Debug("[OTA] Writing boot mark (Request Update)...\r\n"); |
| | | ret = BootMark_Write(&bootMark); |
| | | if (ret != BOOT_OK) { |
| | | HIDO_Debug("[OTA] ERROR: Failed to write boot mark: %d\r\n", ret); |
| | | return; |
| | | } |
| | | |
| | | HIDO_Debug("[OTA] Boot mark updated successfully!\r\n"); |
| | | HIDO_Debug("[OTA] System will restart in 2 seconds...\r\n"); |
| | | |
| | | // 等待串口发送完成 |
| | | HAL_Delay(2000); |
| | | |
| | | // 系统重启 |
| | | HIDO_Debug("[OTA] Restarting system now!\r\n"); |
| | | HAL_Delay(100); // 等待最后一条日志发送完成 |
| | | |
| | | NVIC_SystemReset(); // 执行系统复位 |
| | | } |