upd_download.c
STUpdateTab 的读写封装4.2 字符串拷贝接口的边界处理4.3 固件落盘通道4.4 分区选择标志 s_u8_save_firm_in_app_flash_flag4.5 MD5 校核与升级结果发布 calculate_dev_FW_Flash_MD5()(:391-430)4.6 定时/调度时序与 uCOS-II 配合4.7 关键全局变量的读写与触发条件5. 对外接口本文件导出(全部声明于 net/upd_download.h:23-41,共 19 个,与实现一一对应)本文件导出但未在头文件声明本文件调用的外部函数 / 变量6. 依赖关系依赖(本文件 include,:13-22)被依赖(会用到本文件导出的符号)7. 备注与风险点源文件路径:
net/upd_download.c所属模块:net(网络应用层 — 通用设备固件升级) 代码行数:431
1. 文件职责
本文件是 EMS 整机固件升级的通用状态表与落盘服务层,被 EMS/BMS/逆变器/电表/充电桩/AFCI 等所有升级目标共用。
它维护 g_stEMSInfo.stUpt[] 这张升级描述表(STUpdateTab)的读写封装:文件掩码、软件版本、MD5、文件长度、下载偏移、强制更新标志、服务器已下发标志、特殊升级指令码等,全部通过本文件的 setter/getter 访问。
它提供两条固件二进制落盘通道:Save_Data_To_FW_Area_InSdram() 写 SDRAM 固件区(供需要先在内存中组包再转发的设备使用),save_data_to_fw_area_inFlash() 写外挂 SPI Flash(EMS 自身固件),两者都带 3 次重试。
它通过静态标志 s_u8_save_firm_in_app_flash_flag 决定落盘到主用分区(APP1)还是备份分区(APP2),实现双分区升级与回滚。
数据流位置上,它由 net/server.c(TCP 云端升级链路)和 midware/interface/modbus/modbus.c(Modbus 升级链路)驱动,是"收到数据包 → 落盘 → 推进偏移"链条中落盘与偏移维护的核心实现。
2. 关键数据结构与宏
本文件未定义结构体、枚举或联合体(其参数类型的定义位于 net/server.h)。使用的宏与外部类型如下。
| 名称 | 类型 | 说明 |
|---|---|---|
START_ADDR_SPI_FLASH_EMS_FW | 宏 | 0x00003000,主用 EMS 固件下载区起始(upd_download.h:16) |
END_ADDR_SPI_FLASH_EMS_FW | 宏 | 0x00102FFF,该区结束(:17),本文件未使用 |
START_ADDR_SPI_FLASH_EMS_APP2_FW | 宏 | 0x00106000,备份 EMS 固件下载区起始(:20) |
END_ADDR_SPI_FLASH_EMS_APP2_FW | 宏 | 0x00205FFF,该区结束(:21),本文件未使用 |
ENUpdateType | 枚举(外部 net/server.h:186-198) | UPDT_EMS=0, UPDT_BMS, UPDT_INV1, UPDT_INV2, UPDT_INV3, UPDT_Meter, UPDT_EVC, UPDT_AFCI, UPDT_MAX,本文件所有 API 的第一参数;注意 UPDT_Meter 是拼写不一致的枚举名 |
ENInfoType | 枚举(外部 net/ems.h:247-272) | 上报信息类型位标志,calculate_dev_FW_Flash_MD5 用它作 set_info_status 的实参,取值为 BMS_FIRM_DOWNLOAD=0x04、INVC_FIRM_DOWNLOAD=0x08、METER_FIRM_DOWNLOAD=0x20、EVC_FIRM_DOWNLOAD=0x80、INV_ARM_DOWNLOAD=0x100、AFCI_FIRM_DOWNLOAD=0x200000,另传 INFO_MAX 表示"不置位" |
STUpdateTab | 结构体(外部 net/server.h:216-236) | 单个升级目标的全部元信息,本文件全部 setter/getter 的操作对象 |
BOOLEAN | 类型(外部) | uCOS-II 布尔类型,本文件用于 4 个标志位接口 |
SDRAM_1K | 宏(外部 hardware/sdram/sdram.h:22) | 1*1024 |
MAX_LEN_SOFT_VER | 宏(外部 net/server.h:146) | 16,软件版本字符串上限(含 '\0') |
MAX_SIZE_Special_CMD_Update_Str | 宏(外部 net/server.h:80) | 16,特殊升级指令串长度上限 |
FLASH_OK | 枚举值(外部 hardware/flash/flash.h:107) | Flash_Write 成功返回码 |
DEBUG_TCP_CLIENT_STATE | 宏(外部) | 调试打印总开关 |
s_u8_save_firm_in_app_flash_flag | 全局静态 u8 | 分区选择标志,TRUE = 主用 APP1,FALSE = 备份 APP2,初值 TRUE(:27) |
g_stEMSInfo | 全局变量(外部 net/ems.h:467,类型 STEMSTab) | 本文件读写 stUpt[updType] 全表项 |
g_t_upgrade_run_info | 全局变量(外部 net/ems.h:496,类型 UPGRADE_INFO_T) | 本文件置 u16_upgrade_error.BIT.md5_error(位域定义见 net/ems.h:409) |
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
set_response_force_upd_status(ENUpdateType updType, BOOLEAN set) | 全局 | 写 stUpt[updType].bIsForceUpdtResponse,标记"已对服务器强制升级请求作出应答"(:41) |
get_response_force_upd_status(ENUpdateType updType) | 全局 | 读 bIsForceUpdtResponse(:57) |
set_flag_force_update(ENUpdateType updType, BOOLEAN bSet) | 全局 | 写 bIsUpdateForce,置位表示该设备进入强制/主动升级下载流程(:73) |
get_flag_force_update(ENUpdateType updType) | 全局 | 读 bIsUpdateForce(:89) |
clear_curr_offset_file_request(ENUpdateType updType) | 全局 | 把 u32OffsetFile 归零,开始一次新下载(:105) |
get_curr_offset_file_request(ENUpdateType updType) | 全局 | 读下载偏移 u32OffsetFile(:121) |
Save_Data_To_FW_Area_InSdram(ENUpdateType updType, u8 *fw_Bin, u32 Len, u8 *fw_sdram_baseaddr) | 全局 | 用当前偏移把一块固件写入指定 SDRAM 基址,最多重试 3 次,返回 0 成功 / 1 失败(:137) |
get_save_firm_in_app_flash_flag(void) | 全局 | 返回分区选择标志(:166) |
set_save_firm_in_app_flash_flag(u8 flag) | 全局 | 设置分区选择标志(:178) |
save_data_to_fw_area_inFlash(ENUpdateType updType, u8 *fw_Bin, u32 Len) | 全局 | 用当前偏移把一块固件写入外挂 SPI Flash 主/备固件区,最多重试 3 次,返回 0 成功 / 1 失败(:194) |
set_status_update_file_info(ENUpdateType updType, u8 value) | 全局 | 写 bIsGotUpdtFileInfo,标记"已从服务器取得升级文件信息"(:237) |
set_updt_file_mask(ENUpdateType updType, char *fileMask) | 全局 | 用 strncpy 把文件掩码串拷入 as8FileMask[8](:253) |
set_ver_from_server(ENUpdateType updType, char *ver, u8 len) | 全局 | 把服务器下发的软件版本字符串拷入 as8SoftVer[16],带长度截断与 '\0' 补齐(:269) |
set_id_code_for_updt_cmd(ENUpdateType updType, u8 *buff, u32 len) | 全局 | 把特殊升级指令码拷入 au8IDCMDUpdate[32],先清零(:304) |
get_length_file_updating(ENUpdateType updType) | 全局 | 读升级文件总长度 u32LenFile(:327) |
get_curr_md5_code_fw_file(ENUpdateType updType) | 全局 | 返回 as8MD5Code[32] 指针,服务器下发的 MD5 十六进制串(:343) |
Set_Status_Is_Updated_From_Server(ENUpdateType updType, u8 bSet) | 全局 | 写 bIsUpdatedFromServer,表示固件已就绪待升级(:359) |
Get_Status_Is_Updated_From_Server(ENUpdateType updType) | 全局 | 读 bIsUpdatedFromServer(:375) |
calculate_dev_FW_Flash_MD5(ENUpdateType updType, u8 *g_u8_fw_sdram, ENInfoType type) | 全局 | 对 SDRAM 中的整包固件做 MD5,与服务器 MD5 比对,成功则置位并上报设备升级状态,最后统一把偏移归零(:391) |
4. 核心逻辑解析
4.1 升级状态表 STUpdateTab 的读写封装
本文件绝大多数函数是单行 getter/setter,操作对象统一为 g_stEMSInfo.stUpt[updType]。按字段归类:
| 字段 | 类型 | 写入函数 | 读取函数 | 语义 |
|---|---|---|---|---|
bIsForceUpdtResponse | BOOLEAN | set_response_force_upd_status(:41) | get_response_force_upd_status(:57) | 已应答服务器强制升级请求 |
bIsUpdateForce | BOOLEAN | set_flag_force_update(:73) | get_flag_force_update(:89) | 主动/强制升级标志(被 IsDownloadEMS 复用为"正在下载") |
u32OffsetFile | u32 | clear_curr_offset_file_request(:105)、save_data_to_fw_area_inFlash 间接不写、calculate_dev_FW_Flash_MD5(:429) | get_curr_offset_file_request(:121)、Save_Data_To_FW_Area_InSdram(:143)、save_data_to_fw_area_inFlash(:202) | 已落盘字节偏移 |
u32LenFile | u32 | 无(由 net/server.c 解析响应时直写) | get_length_file_updating(:327) | 固件总长度 |
as8MD5Code[32] | char[] | 无(由 net/server.c / modbus.c 直写) | get_curr_md5_code_fw_file(:343) | 服务器下发的 MD5 十六进制串 |
bIsGotUpdtFileInfo | BOOLEAN | set_status_update_file_info(:237) | 无(由 net/server.c 直读) | 已取得文件信息 |
as8FileMask[8] | char[] | set_updt_file_mask(:253) | 无 | 升级文件掩码 |
as8SoftVer[16] | char[] | set_ver_from_server(:269) | 无 | 目标软件版本 |
au8IDCMDUpdate[32] | u8[] | set_id_code_for_updt_cmd(:304) | 无 | 特殊升级指令码 |
bIsUpdatedFromServer | BOOLEAN / u8 | Set_Status_Is_Updated_From_Server(:359)、calculate_dev_FW_Flash_MD5(:412) | Get_Status_Is_Updated_From_Server(:375) | 固件已就绪待升级 |
所有接口不做 updType 合法性检查,直接用作数组下标;越界调用会安静地破坏 g_stEMSInfo 之后的全局变量。
4.2 字符串拷贝接口的边界处理
set_ver_from_server()(:269-291) 是三个字符串接口中唯一做了完整边界保护的:
if (ver == NULL || len == 0) return; // :271-277 空指针/零长直接返回
if (len >= MAX_LEN_SOFT_VER) { // :278 len >= 16
memcpy(as8SoftVer, ver, MAX_LEN_SOFT_VER - 1); // 只拷 15 字节
as8SoftVer[MAX_LEN_SOFT_VER - 1] = '\0'; // [15] 置 0
} else {
memcpy(as8SoftVer, ver, len);
as8SoftVer[len] = '\0'; // [len] 置 0
}
注意调试打印(:289)里硬编码了 g_stEMSInfo.stUpt[UPDT_INV1].as8SoftVer 而不是 [updType],打印内容与参数无关,属调试遗留。
set_id_code_for_updt_cmd()(:304-314):
if (buff == NULL || len > MAX_SIZE_Special_CMD_Update_Str) return; // len > 16 才拒绝
memset(au8IDCMDUpdate, 0x00, sizeof(...)); // 先清 32 字节
memcpy(au8IDCMDUpdate, buff, len);
目标缓冲是 32 字节(net/server.h:229),但门限用的是 16(MAX_SIZE_Special_CMD_Update_Str,net/server.h:80)。安全性没问题,但 16 < len ≤ 32 的合法指令码会被静默丢弃,调用方(net/server.c:2358)无从感知。
set_updt_file_mask()(:253-256):
strncpy(g_stEMSInfo.stUpt[updType].as8FileMask, fileMask, sizeof(g_stEMSInfo.stUpt[updType].as8FileMask));
sizeof(as8FileMask) = 8,而所有 FILE_MASK_* 常量正好是 8 字符(net/server.h:83-91,如 "00000001")。strncpy 在源串长度 ≥ 目标长度时不写 '\0',因此 as8FileMask[8] 完全没有终止符。net/server.c:3227 的 strstr(uptInfo->as8FileMask, fileMask) 与 net/server.c:43/45 的 strlen(...as8FileMask) 都会越过 8 字节边界继续读取,直到碰巧遇到结构体中下一个 '\0'。
4.3 固件落盘通道
SDRAM 通道 Save_Data_To_FW_Area_InSdram()(:137-157)
l_u32AddrOffset = get_curr_offset_file_request(updType); // :143
l_u32WrAddr = (u32)(fw_sdram_baseaddr + l_u32AddrOffset); // :144
for (i = 0; i < 3; i++) {
l_u32Res = FMC_SDRAM_WriteBuffer_02(fw_Bin, l_u32WrAddr, Len); // :151
if (l_u32Res == Len) return 0; // 写满 Len 字节视为成功
}
return 1;
- 目标地址 = 调用方传入的 SDRAM 基址 + 当前全局下载偏移。函数本身不推进偏移,偏移由
net/server.c:3568 increase_current_offset_file_updt()在外部推进。 - 成功判据是
FMC_SDRAM_WriteBuffer_02返回值等于Len(该函数返回实际写入字节数,见hardware/sdram/sdram.h:107)。 - 返回
0成功 /1失败,与同工程BOOLEAN风格的TRUE/FALSE相反。 - 无
Len上限检查、无END_ADDR_SPI_FLASH_*越界保护。
SPI Flash 通道 save_data_to_fw_area_inFlash()(:194-224)
static u16 s_u16CntWrtieFlash; // :198 调试计数器
s_u16CntWrtieFlash++; // :201 永远递增,无上限
l_u32AddrOffset = get_curr_offset_file_request(updType); // :202
if (get_save_firm_in_app_flash_flag() == TRUE)
l_u32WrAddr = START_ADDR_SPI_FLASH_EMS_FW + l_u32AddrOffset; // :205 主用区
else
l_u32WrAddr = START_ADDR_SPI_FLASH_EMS_APP2_FW + l_u32AddrOffset; // :209 备份区
for (i = 0; i < 3; i++) {
l_u8Res = Flash_Write(fw_Bin, l_u32WrAddr, Len); // :218
if (l_u8Res == FLASH_OK) return 0;
}
return 1;
- 三个调用方(
net/server.c:3583的save_fw_to_sdram、net/server.c:3607的save_fw_to_flash、net/server.c:3642的save_fw_to_spi_flash)在调用前会把不足 1024 字节的尾包用0xFF补齐到LEN_REQ_FILE(1024),保证每次写入都是一致粒度。 s_u16CntWrtieFlash只在DEBUG_TCP_CLIENT_STATE == 1时被打印(:213),是纯调试残留;u16会回绕,但不影响功能。
4.4 分区选择标志 s_u8_save_firm_in_app_flash_flag
| 值 | 含义 | 落盘地址 |
|---|---|---|
TRUE(初值,:27) | 保存到 APP1(主用) | START_ADDR_SPI_FLASH_EMS_FW = 0x3000 |
FALSE | 保存到 APP2(备份) | START_ADDR_SPI_FLASH_EMS_APP2_FW = 0x106000 |
写入点:
midware/interface/modbus/modbus.c:8992— Modbus 升级流程每次默认置TRUE("默认EMS保存到APP1")midware/interface/modbus/modbus.c:9002— 当 Modbus 升级对象为UPGRADE_OBJECT_APP2_E时置FALSEnet/ems.c:4660— 云端升级检查前无条件置TRUE
读取点:net/ems_download.c:74(配置信息落盘)、net/ems_download.c:97(固件读取起点)、本文件 :203(固件落盘)。该标志同时决定"固件区"和"配置信息区",保证两者始终配对。
4.5 MD5 校核与升级结果发布 calculate_dev_FW_Flash_MD5()(:391-430)
IWDG_Feed(); // :396
MD5Init(&md5);
MD5Update(&md5, (char *)g_u8_fw_sdram, get_length_file_updating(updType)); // :398 一次性整包
MD5Final(&md5);
IWDG_Feed(); // :400
for (k = 0; k < 16; k++) sprintf(&file_Md5[k*2], "%02X", md5.digest[k]); // :402-405
if (memcmp(file_Md5, get_curr_md5_code_fw_file(updType), 32) == 0) { // :407
Set_Status_Is_Updated_From_Server(updType, TRUE); // :412
if (updType != UPDT_INV1)
set_info_status(type); // :414
if (updType == UPDT_Meter)
g_stEMSInfo.stUpt[UPDT_Meter].u32OffsetFile = 0; // :417
} else {
g_t_upgrade_run_info.u16_upgrade_error.BIT.md5_error = 1; // :422
}
g_stEMSInfo.stUpt[updType].u32OffsetFile = 0; // :429 无条件归零
要点:
- 调用方与
type实参的约定(全部调用点):
| 调用点 | updType | g_u8_fw_sdram | type |
|---|---|---|---|
net/server.c:3789 | UPDT_INV1 | g_u8_fw_sdram1 | INFO_MAX(故意无效,跳过 set_info_status) |
net/server.c:3793 | UPDT_INV2 | g_u8_fw_sdram2 | INVC_FIRM_DOWNLOAD |
net/server.c:3797 | UPDT_INV3 | g_u8_fw_sdram1 | INV_ARM_DOWNLOAD |
net/server.c:3801 | UPDT_BMS | g_u8_fw_sdram1 | BMS_FIRM_DOWNLOAD |
net/server.c:3805 | UPDT_Meter | g_u8_fw_sdram1 | METER_FIRM_DOWNLOAD |
net/server.c:3809 | UPDT_EVC | g_u8_fw_sdram1 | EVC_FIRM_DOWNLOAD |
net/server.c:3813 | UPDT_AFCI | g_u8_fw_sdram1 | AFCI_FIRM_DOWNLOAD |
midware/interface/modbus/modbus.c:9340 | UPDT_BMS | g_u8_fw_sdram1 | BMS_FIRM_DOWNLOAD |
midware/interface/modbus/modbus.c:9344 | UPDT_INV1 | g_u8_fw_sdram1 | INFO_MAX |
midware/interface/modbus/modbus.c:9348 | UPDT_INV2 | g_u8_fw_sdram2 | INVC_FIRM_DOWNLOAD |
midware/interface/modbus/modbus.c:9352 | UPDT_INV3 | g_u8_fw_sdram1 | INV_ARM_DOWNLOAD |
UPDT_INV1 用 INFO_MAX 作哨兵值来跳过 set_info_status,原因见风险点:set_info_status(INFO_MAX) 会置一个网侧无法识别的信息位且不被清除。这个约定完全靠调用方自觉,函数签名里没有任何说明。
- 调用方还有一层判断:
net/server.c:3786与midware/interface/modbus/modbus.c:9335只有在已收字节数 ≥u32LenFile时才调用本函数,因此它假定 SDRAM 中已经是完整固件。 - 偏移无条件归零(
:429):不论 MD5 成功还是失败都会把u32OffsetFile清 0,意味着 MD5 失败后无法从断点续传。 - Meter 特判冗余(
:416-418):成功分支里对UPDT_Meter单独把偏移清 0,但:429已经对所有类型清 0,该分支是死代码。 - 只有 SDRAM 通道:本函数不读 Flash,MD5 直接作用于
g_u8_fw_sdram1/2(各 3MB,net/server.h:258-259);EMS 自身固件走另一条路(net/ems_download.c:120的calculate_ems_flash_md5)。
4.6 定时/调度时序与 uCOS-II 配合
- 本文件不创建任务、不使用信号量或消息邮箱,也不含任何延时调用,全部是同步 getter/setter 与阻塞式 Flash/SDRAM 写入。
- 驱动方是 net 任务(TCP 升级,
net/server.c:3583-3813)和 Modbus 任务(midware/interface/modbus/modbus.c:8992-9352)。 - 唯一与调度器相关的交互发生在调用方:
net/server.c:3824在收包成功路径上OSSemPost(g_pUpdateSem),本文件不参与。 - 看门狗:
calculate_dev_FW_Flash_MD5()在整包 MD5 前后各喂一次(:396、:400),中间对最大 3MB 的缓冲区做单次MD5Update期间不喂狗;save_data_to_fw_area_inFlash()与Save_Data_To_FW_Area_InSdram()内部完全不喂狗,但每次写入粒度是 1024 字节(LEN_REQ_FILE),单次耗时短。 - 无临界区、无原子性保护:所有 getter/setter 都是裸读写,net 任务与 Modbus 任务可并发访问同一
stUpt[]表项(例如云端升级与 Modbus 升级同时进行时)。
4.7 关键全局变量的读写与触发条件
| 变量 | 读 | 写 | 触发条件 |
|---|---|---|---|
g_stEMSInfo.stUpt[updType].u32OffsetFile | ✔(:143、:202) | ✔(:107、:417、:429) | 开始新下载(clear_curr_offset_file_request)或 MD5 校核结束 |
g_stEMSInfo.stUpt[updType].u32LenFile | ✔(:329、:398) | — | 服务器 UpdateCheck 响应解析时写入 |
g_stEMSInfo.stUpt[updType].as8MD5Code | ✔(:345、:407) | — | 同上 |
g_stEMSInfo.stUpt[updType].bIsUpdateForce | ✔(:91) | ✔(:75) | 服务器下发强制升级指令或 Modbus 升级流程 |
g_stEMSInfo.stUpt[updType].bIsForceUpdtResponse | ✔(:59) | ✔(:43) | net/server.c:2370/2382 置位,net/server.c:151 消费后清零 |
g_stEMSInfo.stUpt[updType].bIsGotUpdtFileInfo | — | ✔(:239) | 服务器 UpgradeCheck 响应校验通过 |
g_stEMSInfo.stUpt[updType].bIsUpdatedFromServer | ✔(:377) | ✔(:361、:412) | MD5 校核通过 |
g_stEMSInfo.stUpt[updType].as8FileMask | — | ✔(:255) | 服务器下发文件掩码 |
g_stEMSInfo.stUpt[updType].as8SoftVer | — | ✔(:280/:285) | 服务器下发版本串 |
g_stEMSInfo.stUpt[updType].au8IDCMDUpdate | — | ✔(:312-313) | 服务器下发特殊升级指令码 |
g_t_upgrade_run_info.u16_upgrade_error.BIT.md5_error | — | ✔(:422) | MD5 不匹配 |
s_u8_save_firm_in_app_flash_flag | ✔(:168、:203) | ✔(:180) | Modbus APP2 对象 / net/ems.c:4660 |
5. 对外接口
本文件导出(全部声明于 net/upd_download.h:23-41,共 19 个,与实现一一对应)
| 符号 | 主要使用者 |
|---|---|
set_response_force_upd_status / get_response_force_upd_status | net/ems.c:2882、net/server.c:151、:2370、:2382 |
set_flag_force_update / get_flag_force_update | midware/afci/afci_upgrade.c:406、:452、midware/inverter/alphaess/alphaess_upgrade.c:538-539、:587-588、net/ems_download.c:219、net/server.c:2412 |
clear_curr_offset_file_request | net/server.c:3236 |
get_curr_offset_file_request | net/ems_download.c:230、net/server.c:3778 |
Save_Data_To_FW_Area_InSdram | net/server.c:3589、:3598(经 save_fw_to_sdram) |
save_data_to_fw_area_inFlash | net/server.c:3613、:3622(经 save_fw_to_flash) |
get_save_firm_in_app_flash_flag / set_save_firm_in_app_flash_flag | net/ems_download.c:74、:97;midware/interface/modbus/modbus.c:8992、:9002;net/ems.c:4660 |
set_status_update_file_info | net/server.c:2405、:3232 |
set_updt_file_mask | net/server.c:2418、:2430、:2439 |
set_ver_from_server | net/server.c:2422 |
set_id_code_for_updt_cmd | net/server.c:2358、:2359、:2361 |
get_length_file_updating | net/ems_download.c:129、:130、:172、net/ems_boot_handle.c:222、net/server.c:3786、midware/afci/afci_upgrade.c:263-271、本文件 :398 |
get_curr_md5_code_fw_file | net/ems_download.c:164、:167、:206、midware/inverter/alphaess/alphaess_upgrade.c:105、:107、本文件 :407、:410、:425 |
Set_Status_Is_Updated_From_Server / Get_Status_Is_Updated_From_Server | net/ems_download.c:180、:276-283、midware/afci/afci_upgrade.c:405、:451、:517、midware/bms/alpha/battery_upgrade.c:751、midware/bms/bms.c:2751、:2758、midware/evc/evc_upgrade.c:677、midware/interface/interface.c:318、midware/debug/ate.c:132-134、:166 |
calculate_dev_FW_Flash_MD5 | net/server.c:3789-3813、midware/interface/modbus/modbus.c:9340-9352 |
本文件导出但未在头文件声明
无(19 个实现与 19 条声明完全对应,接口契约完整)。
本文件调用的外部函数 / 变量
| 外部符号 | 来源模块 |
|---|---|
FMC_SDRAM_WriteBuffer_02 | hardware/sdram/sdram.h:107 |
Flash_Write | hardware/flash/flash.h:140 |
MD5Init / MD5Update / MD5Final / MD5_CTX | common/md5.h |
IWDG_Feed | hardware/iwdg/iwdg.h |
set_info_status | net/ems.c:2722 |
g_stEMSInfo | net/ems.h:467 |
g_t_upgrade_run_info | net/ems.h:496 |
g_u8_fw_sdram1 / g_u8_fw_sdram2(仅由调用方传入地址) | net/server.h:258-259 |
START_ADDR_SPI_FLASH_EMS_FW / START_ADDR_SPI_FLASH_EMS_APP2_FW | net/upd_download.h |
FLASH_OK | hardware/flash/flash.h:107 |
strncpy / memcpy / memset / sprintf / memcmp / printf | 标准库(经 includes.h) |
6. 依赖关系
依赖(本文件 include,:13-22)
ems.h、server.h、board_init.h、md5.h、flash.h、iwdg.h、crc.h、inverter.h、meter.h、upd_download.h。
其中 board_init.h、crc.h、inverter.h、meter.h 在本文件中并未实际使用,属冗余 include。
被依赖(会用到本文件导出的符号)
net/ems_download.c—get_save_firm_in_app_flash_flag、get_length_file_updating、get_curr_offset_file_request、get_curr_md5_code_fw_file、get_flag_force_update、get_curr_md5_code_fw_file、Set_Status_Is_Updated_From_Server、Get_Status_Is_Updated_From_Server(:74、:97、:129-130、:164、:172、:180、:219、:230、:276-283)net/ems_boot_handle.c—get_length_file_updating(:222)net/server.c— 几乎全部接口(:151、:283、:2358-2439、:3227-3451、:3568-3622、:3778-3813)net/ems.c—set_response_force_upd_status(:2882)、set_flag_force_update、set_save_firm_in_app_flash_flag(:4660)midware/interface/modbus/modbus.c—set_save_firm_in_app_flash_flag(:8992、:9002)、calculate_dev_FW_Flash_MD5(:9340-9352)midware/bms/bms.c、midware/bms/alpha/battery_upgrade.c、midware/inverter/alphaess/alphaess_upgrade.c、midware/evc/evc_upgrade.c、midware/afci/afci_upgrade.c、midware/interface/interface.c、midware/debug/ate.c— 各设备的Set/Get_Status_Is_Updated_From_Server、set/get_flag_force_update、get_curr_md5_code_fw_file、get_length_file_updating
7. 备注与风险点
set_updt_file_mask缺'\0'终止符(真实缺陷,较高风险)::255用strncpy(dst, fileMask, 8)拷贝,而所有FILE_MASK_*常量正好 8 字符(net/server.h:83-91),as8FileMask[8]不会被终止。下游net/server.c:3227的strstr(uptInfo->as8FileMask, fileMask)、net/server.c:43/45的strlen(...as8FileMask)都会越界读入结构体后续字段(as8FWType[10])。- 栈越界写(真实缺陷):
char file_Md5[32](:393)+for (k = 0; k < 16; k++) sprintf(&file_Md5[k*2], "%02X", ...)(:402-405)。k = 15时sprintf会在file_Md5[32]写入'\0',越界 1 字节。net/ems_download.c:122/159-162存在完全相同的写法。 INFO_MAX哨兵值约定脆弱:if (updType != UPDT_INV1) set_info_status(type);(:413-415)依赖调用方对 INV1 传INFO_MAX。INFO_MAX = 0x400000(net/ems.h:271之后),若被真正执行,set_info_status会置一个net/ems.c上报链中不存在分支处理的位,且没有任何清除路径。这个"隐式契约"在函数签名和注释里都没有记录。- 整包
MD5Update无喂狗::398一次性对最大 3MB 缓冲区做 MD5,前后各喂一次狗(:396、:400)。若某设备固件较大,可能踩到hardware/iwdg/iwdg.c:11的fwdgt_config(4000, 4)(约 0.4~0.5 s)看门狗上限。 - 无长度上限校验:
MD5Update的长度来自get_length_file_updating(updType)(:398),该值由服务器下发且未与g_u8_fw_sdram1/2的 3MB(net/server.h:258-259)或 SPI Flash 分区END_ADDR_SPI_FLASH_EMS_FW做任何比较。服务器下发超长值会导致 SDRAM 越界读 / Flash 区越界写。 END_ADDR_SPI_FLASH_*完全未使用::203-210只用 START 地址加偏移,:21/:17定义的结束地址在写入路径上没有生效,分区越界无任何保护。updType无合法性检查:所有接口直接g_stEMSInfo.stUpt[updType],updType >= UPDT_MAX会越界写g_stEMSInfo内的后续字段(pstCurrUpt等指针),后果严重。- 无并发保护:所有 getter/setter 都是裸访问。net 任务(TCP 升级)与 Modbus 任务可同时操作同一
stUpt[]表项,set_ver_from_server的memcpy+ 置'\0'也非原子。工程里已有g_pUpdateSem(net/server.c:3825OSSemPost),但本文件不使用。 - Meter 特判是死代码:
:416-418的清零被:429的无条件清零覆盖。 - 调试残留:
static u16 s_u16CntWrtieFlash(:198)只服务于一条printf(:213),且永远单调递增;set_ver_from_server的调试打印硬编码stUpt[UPDT_INV1](:289),与传入的updType无关。 - 注释已被破坏:
:162、:163、:174三处中文注释已退化为 UTF-8 替换字符(EF BF BD),原文(应为"1 保存到APP1 / 0 保存到APP2")不可读;这是项目在编码转换过程中留下的损坏。 - 文件头注释错误:
:2-5写的是@file :BMSUpdate.c、@date :2025/06/24,与实际文件名和内容不符(net/upd_download.h:2-3同样写着BMSUpdate.h,作者字段为Alpha g_stEMSInfo Team)。 - 返回值约定不统一:
save_data_to_fw_area_inFlash/Save_Data_To_FW_Area_InSdram返回0=成功 / 1=失败,而net/ems_download.c:63的save_updt_config_info_to_external_flash返回TRUE/FALSE;调用方net/server.c:3589用!= 0判断,风格混用。 Get_Status_Is_Updated_From_Server返回类型不一致:声明与定义为u8(:375、upd_download.h:40),但同族的Set_Status_Is_Updated_From_Server参数也是u8,而表项字段bIsUpdatedFromServer是BOOLEAN;调用方net/ems_download.c:276-283与net/ems.h的BOOLEAN混用,依赖TRUE == 1。- 重试不清理:
save_data_to_fw_area_inFlash(:216-221)与Save_Data_To_FW_Area_InSdram(:149-154)失败后重试时对同一地址重复擦写,不做任何失效清理;3 次全失败仅返回1,是否需要上报由调用方决定(net/server.c:3591只打印日志)。 - 枚举名拼写不一致:
UPDT_Meter(net/server.h:193)采用驼峰,与同枚举的UPDT_EMS、UPDT_BMS、UPDT_EVC、UPDT_AFCI风格不符,属历史遗留,改动需全工程同步。