升级框架总览 (code_v1 / code_v2)
本页概述 AW30N BLE SDK 中的设备固件升级(OTA/烧录)框架,涵盖 code_v1 与 code_v2 两套升级代码的实现、数据结构、升级通道、参数传递与核心流程,是理解 SDK 升级能力的入口页面。
Purpose and Scope
本页面向需要理解或二次开发 AW30N SDK 升级功能的工程师,系统讲解:
- 升级框架的整体架构:code_v1 与 code_v2 两套实现的定位与差异;
- UFW 固件包格式:
ufw_syd_head_v1/ufw_file_head_v1等头部结构; - 升级参数传递机制:SDK 与 U-Boot 之间通过
UPDATA_PARM传递升级意图; - 升级通道体系:USB、UART、SD 卡、BLE/RCSP、TestBox、NOR Flash 等;
- 升级目标注册机制:
REGISTER_UPDATE_TARGET与.update_target段; - 升级状态机与结果上报:
UPDATE_STATE_T、UPDATA_RESULT与DEVICE_FIRST_START等标志。
以下内容属于兄弟页面,不在本页展开:具体 BLE OTA 的 RCSP 协议细节(参见 RCSP 升级/通道下载相关页面)、具体外设驱动(UART/SD/Flash 驱动)、U-Boot 侧实现。
Overview
升级框架是 SDK 中负责"将新固件安全地写入 Flash、并在重启后由引导程序完成搬运"的整套机制。它解决的核心问题是:在应用运行时如何可靠地把新固件(.ufw 文件)下发、校验、落盘,并把升级意图与参数传递给下一阶段(U-Boot/Loader),最终实现系统自更新。
SDK 中并存两套升级代码,分别位于 sdk/apps/include_lib/update/code_v1/ 与 sdk/apps/include_lib/update/code_v2/:
- code_v1(
update_v1.h):早期实现,面向 SD 卡 / FAT 文件系统 + UFW 文件 的升级路径。它直接解析 UFW 文件(check_ufw_file),再调用try_to_upgrade完成升级,错误通过枚举(FAT_MOUNT_ERROR、FIND_OTA_ERROR等)返回。 - code_v2(
update.h):新一代框架,抽象出 升级通道(UPDATA_TYPE)、升级目标(update_target)、升级状态机(UPDATE_STATE_T) 与 参数传递(UPDATA_PARM) 等概念,支持 USB/UART/SD/BLE/TestBox/NOR Flash 等更多通道,是当前 SDK 的主力实现。
两者并非互斥:code_v2 在底层复用 Flash/SFC 等基础设施,而 code_v1 的 UFW 文件解析逻辑也以 USER_NORFLASH_UFW_UPDATA 等通道形式被 code_v2 兼容。
升级的宏观流程为:获取升级文件 → 校验文件头 → 进入升级模式(复位到 Loader/U-Boot)→ Loader 按参数搬运固件 → 重启后校验结果并上报。update.c 是 code_v2 的应用层入口实现(update.c),其中定义了升级文件名、升级前需要关闭的外设(ram_protect_close、hwi_all_close、ll_hci_destory 等)以及看门狗处理。
Architecture
flowchart TD
subgraph sg_App["应用层 (App)"]
APP_CONFIG["app_config.h<br/>TFG_DEV_UPGRADE_SUPPORT<br/>TFG_UPGRADE_FILE_NAME"]
UPDATE_C["update.c<br/>app_update_init / app_update_handle"]
end
subgraph sg_V2["code_v2 框架 (update.h)"]
MODE_API["update_mode_api_v2<br/>选择升级通道"]
PARM["UPDATA_PARM<br/>magic=0x5441 参数传递"]
TARGET["update_target 注册表<br/>.update_target 段"]
STATE["UPDATE_STATE_T 状态机"]
RESULT["UPDATA_RESULT 结果上报"]
end
subgraph sg_V1["code_v1 框架 (update_v1.h)"]
UFW_CHECK["check_ufw_file<br/>解析 UFW 文件头"]
UFW_PARSE["ufw_syd_head_v1<br/>ufw_file_head_v1"]
TRY_UP["try_to_upgrade<br/>SD 卡升级入口"]
end
subgraph sg_CH["升级通道 (UPDATA_TYPE)"]
CH_USB["USB_UPDATA / USB_HID_UPDATA"]
CH_UART["UART_UPDATA / TESTBOX_UART_UPDATA"]
CH_SD["SD0_UPDATA / SD1_UPDATA"]
CH_BLE["BLE_APP_UPDATA / SPP_APP_UPDATA"]
CH_NOR["NORFLASH_UPDATA / DUAL_BANK_UPDATA"]
end
subgraph sg_BOOT["引导阶段 (U-Boot/Loader)"]
LOADER["LOADER.BIN"]
FLASH["Flash 分区写入"]
end
APP_CONFIG --> UPDATE_C
UPDATE_C --> MODE_API
MODE_API --> PARM
MODE_API --> CH_USB
MODE_API --> CH_UART
MODE_API --> CH_SD
MODE_API --> CH_BLE
MODE_API --> CH_NOR
TARGET --> MODE_API
STATE --> RESULT
PARM --> LOADER
LOADER --> FLASH
TRY_UP --> UFW_CHECK
UFW_CHECK --> UFW_PARSE
CH_NOR --> TRY_UP
RESULT --> UPDATE_C
架构说明:
- 应用层:
app_config.h通过TFG_DEV_UPGRADE_SUPPORT开关使能升级功能,TFG_UPGRADE_FILE_NAME指定升级文件名(默认/update.ufw,见 app_config.h)。update.c提供app_update_init/app_update_handle作为应用入口(update.c)。 - code_v2 框架:以
update_mode_api_v2(type, priv_param_fill_hdl, priv_update_jump_handle)为统一入口,按UPDATA_TYPE选择通道,构造UPDATA_PARM传递给引导程序;update_target注册表用于登记每个通道关闭外设驱动的回调;状态机UPDATE_STATE_T管理升级任务生命周期;UPDATA_RESULT用于结果上报。 - code_v1 框架:直接面向 UFW 文件,
check_ufw_file校验文件头后try_to_upgrade执行升级,适用于 SD 卡 + FAT 文件系统场景,其 UFW 解析被 code_v2 的 NOR Flash 通道复用。 - 引导阶段:SDK 侧只负责"准备"与"复位跳转",真正的固件搬运由 U-Boot/Loader(
LOADER.BIN)依据UPDATA_PARM(含ota_addr、文件路径)完成,重启后 SDK 再通过update_success_boot_check/update_result_deal处理结果。
code_v1 与 code_v2 的定位与差异
两套代码分别对应 SDK 演进的两个阶段,理解其差异有助于在阅读具体升级代码时快速定位。
| 维度 | code_v1 (update_v1.h) | code_v2 (update.h) |
|---|---|---|
| 设计思路 | 面向文件的单一路径(SD 卡 + UFW) | 面向通道的抽象框架(USB/UART/SD/BLE/TestBox/NOR) |
| 入口 API | try_to_upgrade(dev_name, up_file_path) | update_mode_api_v2(type, ...) |
| 文件校验 | check_ufw_file 解析 UFW 头部 | 由各通道负责,兼容 UFW(USER_NORFLASH_UFW_UPDATA) |
| 参数传递 | UPDATA_PARM(magic 0x5441) | UPDATA_PARM + UPDATA_EXT_PARM + ext_arg_t |
| 通道注册 | 无(硬编码路径) | REGISTER_UPDATE_TARGET + .update_target 段 |
| 状态管理 | 同步返回错误码 | UPDATE_STATE_T 状态机 + UPDATA_RESULT 结果上报 |
| 结果标志 | — | DEVICE_FIRST_START(BIT31) / DEVICE_UPDATE_KEY_ERR(BIT30) |
设计意图:code_v1 诞生于早期芯片方案,升级路径固定为"读 SD 卡里的 UFW 文件";code_v2 将"从哪里拿固件"(通道)与"如何升级"(目标/参数)解耦,使同一套升级核心逻辑可以复用多个通道,新增通道只需注册一个 update_target 并实现 driver_close 回调,无需改动框架本身。
UFW 固件包格式(code_v1 定义)
UFW(Update FirmWare)文件是升级固件的打包格式,code_v1 在 update_v1.h 中定义了系统头与文件头:
struct ufw_syd_head_v1 {
u16 Crc; // crc16 for this struct
u16 CrcOfSydFileHead;
u32 FileLength; // file length
u16 FileCount; // 此FW文件中包含的子文件个数
u16 Version;
u16 HeadAlignmentSize;
u16 Res;
char szChipName[16]; // 此FW文件对应的芯片类型,如AC690X,AC691X
u32 Res2[4];
u32 Res3[4];
} _GNU_PACKED_;
Source: update_v1.h
系统头的关键字段:Crc/CrcOfSydFileHead 提供两级 CRC 校验(头部自身 + 文件头区域);FileCount 表示包内含子文件数量;szChipName 限定芯片型号,防止刷错固件。_GNU_PACKED_ 保证结构体按 1 字节对齐,与打包工具的内存布局一致。
每个子文件由 ufw_file_head_v1 描述:
struct ufw_file_head_v1 {
u8 FileType; // 文件类型
u8 Res; // 保留
u16 Index; // 文件索引号
u16 Crc; // 明文数据的校验码
u16 Version; // 本结构体版本
u32 Addr; // 地址
u32 Length; // 数据长度
u32 AllLength; // 数据长度+尾部对齐的数据长度
u32 EncryptedAddr; // 加密数据的地址偏移(相对u32Addr的地址)
u32 EncryptedLength; // 加密的数据长度
union {
// 当文件类型是FILE_TYPE_FW_RESERVE_ZONE_FILE有效
struct {
u32 ReserveZoneAddress;
u32 ReserveZoneLength;
char szReserveZoneName[12];
} _GNU_PACKED_;
struct {
u32 Res0; // 保留
u32 Res1[4]; // 保留
u32 Res2[4]; // 保留
} _GNU_PACKED_;
};
char name[16];
} _GNU_PACKED_;
Source: update_v1.h
文件头设计要点:
Addr/Length/AllLength:指明数据落盘地址、明文长度与对齐后长度,Loader 据此把数据写入 Flash 对应分区;EncryptedAddr/EncryptedLength:支持密文区与明文区分离(安全升级),解密由 Loader 侧完成;- 联合体:当文件类型为保留区文件(
FILE_TYPE_FW_RESERVE_ZONE_FILE)时,前 20 字节解析为保留区地址/长度/名称;否则为保留字段。这种"按类型复用头部"的做法节省了头部空间; name[16]:短文件名(8+3 结构),与update.c中"升级文件路径必须是短文件名(8+3),仅支持 2 层目录"的注释约束一致(update.c)。
升级参数传递:UPDATA_PARM
SDK 与 U-Boot 之间通过固定地址处的 UPDATA_PARM 结构传递升级意图。code_v2 在 update.h 中定义:
#define UPDATE_PARAM_MAGIC 0x5441
typedef struct _UPDATA_PARM {
u16 parm_crc;
u16 parm_type; //UPDATA_TYPE:sdk pass parm to uboot
u16 parm_result; //UPDATA_TYPE:uboot return result to sdk
u16 magic; //0x5441
//8byte
union {
struct {
u8 file_path[32]; //updata file path
};
struct {
u8 file_patch[32]; //updata file path
};
};
u8 parm_priv[32]; //sd updata
//64byte
u32 ota_addr;
u16 ext_arg_len;
u16 ext_arg_crc;
//8 byte
} UPDATA_PARM;
Source: update.h
该结构是 SDK → U-Boot → SDK 的双向约定:parm_type 由 SDK 写入(取 UPDATA_TYPE 枚举值,如 UART_UPDATA),parm_result 由 U-Boot 写回升级结果;magic 固定为 0x5441(UPDATE_PARAM_MAGIC),用于防止在 parm_crc 恰好为 0 时误判有效性。file_path 指向升级文件路径(如 /update.ufw),ota_addr 给出 OTA 数据地址,ext_arg_len/ext_arg_crc 描述附加参数区。
code_v2 还定义了扩展参数机制(update.h):
enum EXT_ARG_TYPE {
EXT_LDO_TRIM_RES = 0,
EXT_JUMP_FLAG,
EXT_KEEP_ROMIO_INFO = 0x10,
EXT_TYPE_MAX = 0xff,
};
struct ext_arg_t {
u8 type;
u8 len;
u8 *data;
};
Source: update.h
扩展参数以 TLV(type-len-data)形式存在:EXT_LDO_TRIM_RES 传递 LDO trim 校准值(通过 update_param_priv_fill 填充,update.h),EXT_JUMP_FLAG 控制跳转行为,EXT_KEEP_ROMIO_INFO(0x10)用于在升级时保持 ROM IO 配置(对应 update_keep_io 与 update_get_latch_romio API)。code_v1 中的 UPDATA_EXT_PARM 则以固定结构传递 SD 端口与 IO 方向配置(update_v1.h)。
升级结果与标志位
code_v2 在固定内存区维护升级状态(update.h):
extern u32 UPDATA_BEG;
extern u32 UPDATA_SIZE;
#define UPDATA_FLAG_ADDR ((void *)((u32)&UPDATA_BEG + 0x08))
#define BOOT_STATUS_ADDR ((void *)((u32)&UPDATA_BEG)) //预留8个bytes
#define UPDATA_MAGIC (0x5A00) //防止CRC == 0 的情况
Source: update.h
UPDATA_BEG 为链接脚本保留的专用内存区:前 8 字节是 BOOT_STATUS_ADDR(启动状态),UPDATA_FLAG_ADDR 存放升级标志。UPDATA_MAGIC = 0x5A00 被同时用作 UPDATA_RESULT 与 UPDATA_TYPE 的枚举基值,即"结果/类型的合法值都带 magic 前缀",避免与未初始化内存(0x00/0xFF)混淆:
typedef enum {
UPDATA_NON = UPDATA_MAGIC, //0x5A00
UPDATA_READY, //0x5A01
UPDATA_SUCC, //0x5A02
UPDATA_PARM_ERR, //0x5A03
UPDATA_DEV_ERR, //0x5A04
UPDATA_KEY_ERR, //0x5A05
} UPDATA_RESULT;
Source: update.h
结果处理由 update_result_set / update_result_get / update_result_deal / update_clear_result / update_success_boot_check 一组 API 完成;update.c 中定义了应用层辅助标志(update.c):
#define LOADER_NAME "LOADER.BIN"
#define DEVICE_UPDATE_KEY_ERR BIT(30)
#define DEVICE_FIRST_START BIT(31)
Source: update.c
DEVICE_FIRST_START(BIT31):首次启动标志——升级成功后设备第一次运行,应用据此做首次启动初始化(device_is_first_start()可查询);DEVICE_UPDATE_KEY_ERR(BIT30):升级按键错误标志,用于区分"升级失败"与"未满足升级条件";LOADER_NAME:Loader 文件名,ota_loader_patch相关路径即指向它(code_v1 的UPDATA_PARM.ota_loader_patch[32],update_v1.h)。
升级通道体系(UPDATA_TYPE)
code_v2 用 UPDATA_TYPE 枚举统一标识所有升级通道(update.h):
typedef enum {
USB_UPDATA = UPDATA_MAGIC, //0x5A00
SD0_UPDATA, //0x5A01
SD1_UPDATA,
PC_UPDATA,
UART_UPDATA,
BT_UPDATA,
BLE_APP_UPDATA,
SPP_APP_UPDATA,
DUAL_BANK_UPDATA,
BLE_TEST_UPDATA,
NORFLASH_UPDATA,
TESTBOX_UART_UPDATA,
TESTBOX_UART_UPDATA_2,
//NOTE:以上的定义不要调整,新升级方式在此添加,注意加在USER_NORFLASH_UFW_UPDATA之前;
USER_NORFLASH_UFW_UPDATA,
USER_LC_FLASH_UFW_UPDATA,
USB_HID_UPDATA,
NON_DEV = 0xFFFF,
} UPDATA_TYPE;
Source: update.h
枚举设计的关键约束(注释中明确写出):已有定义的顺序不能调整,新增通道必须加在 USER_NORFLASH_UFW_UPDATA 之前。这是因为 parm_type 通过 UPDATA_PARM 传递给 U-Boot,两边按枚举值硬编码约定;插入到枚举末尾附近可保持与旧 Loader 的兼容性。NON_DEV = 0xFFFF 作为"无设备"哨兵值。
通道间的互斥与触发方式由 UPGRADE_TYPE 描述(update.h):
typedef enum {
UPGRADE_NULL = 0,
UPGRADE_USB_HARD_KEY,
UPGRADE_USB_SOFTKEY,
UPGRADE_UART_SOFT_KEY,
UPGRADE_UART_ONE_WIRE_HARD_KEY,
} UPGRADE_TYPE;
Source: update.h
UPGRADE_TYPE 区分 硬件按键触发(UPGRADE_USB_HARD_KEY、UPGRADE_UART_ONE_WIRE_HARD_KEY)与 软件命令触发(UPGRADE_USB_SOFTKEY、UPGRADE_UART_SOFT_KEY)。硬件触发面向产线/售后(上电检测按键进入升级),软件触发面向应用内 OTA。pmu_flag.h 中的 disable_uart_upgrade 位可进一步关闭 UART 升级(见补丁包中该定义),说明框架支持按产品配置裁剪通道。
各通道的硬件参数通过独立结构体配置:
- SD 卡通道(
_UPDATA_SD):control_type(控制器,SD_CONTROLLER_0/1)、control_io(SD0_IO_A~SD0_IO_F)、max_data_baud、wDevTimeOutMax、io_det_func(在线检测函数指针)、hc_mode等(update.h); - UART 通道(
_UPDATA_UART):control_io_tx/control_io_rx指定 IO 对接、control_baud波特率、control_timeout超时(单位 10ms),结构体注释"共12个bytes"体现了对内存布局的严格约束(update.h); - U 盘通道(
_UPDATE_UDISK):内部联合体用 32 字节缓冲承载 LDO trim 参数(rx_ldo_trim/tx_ldo_trim)(update.h)。
升级目标注册机制(update_target)
code_v2 的一个关键设计是把"通道外设驱动关闭"抽象为可注册的目标(update.h):
typedef void(*update_handler_t)(void);
typedef struct _UPDATE_STATE_T {
UPDATE_TASK_INIT,
UPDATE_CH_INIT,
UPDATE_CH_SUCESS_REPORT,
UPDATE_CH_EXIT,
} UPDATE_STATE_T;
struct update_target {
char *name;
update_handler_t driver_close;
};
#define REGISTER_UPDATE_TARGET(target) \
const struct update_target target sec(.update_target)
extern const struct update_target update_target_begin[];
extern const struct update_target update_target_end[];
#define list_for_each_update_target(p) \
for (p = update_target_begin; p < update_target_end; p++)
Source: update.h
工作原理:REGISTER_UPDATE_TARGET(target) 把 struct update_target 放入链接脚本的 .update_target 自定义段;链接后 update_target_begin[] 到 update_target_end[] 之间的连续数组即"全部注册目标",list_for_each_update_target 可遍历它们。这是典型的段收集(section collection)注册模式——模块只需在自己的 .c 文件里声明一个 REGISTER_UPDATE_TARGET 实例,无需修改框架注册表;框架升级前遍历所有目标,依次调用其 driver_close 回调关闭对应外设驱动(如 HCI、WiFi、看门狗等),确保升级期间外设处于安全状态。
update.c 中升级前关闭的外设清单印证了这一点(update.c):ll_hci_destory(蓝牙 HCI 销毁)、hci_controller_destory(控制器销毁)、ram_protect_close(RAM 保护关闭)、hwi_all_close(全部硬件中断关闭)、wifi_det_close(弱符号,WiFi 检测关闭)。
升级状态机与任务生命周期
code_v2 用 UPDATE_STATE_T 描述升级任务状态(update.h):
stateDiagram-v2
[*] --> UPDATE_TASK_INIT
UPDATE_TASK_INIT --> UPDATE_CH_INIT
UPDATE_CH_INIT --> UPDATE_CH_SUCESS_REPORT
UPDATE_CH_SUCESS_REPORT --> UPDATE_CH_EXIT
UPDATE_CH_EXIT --> [*]
UPDATE_CH_INIT --> UPDATE_CH_EXIT : 通道初始化失败
UPDATE_TASK_INIT:升级任务创建、参数准备;UPDATE_CH_INIT:通道初始化(打开 SD/UART/USB 等设备、解析文件);UPDATE_CH_SUCESS_REPORT:成功后上报结果(写UPDATA_RESULT);UPDATE_CH_EXIT:退出升级任务,释放资源。
失败路径直接由 UPDATE_CH_INIT 跳转 UPDATE_CH_EXIT,保证错误时也能走统一的收尾逻辑。任务级入口为 app_update_init / app_update_handle(msg)(update.h)。
核心升级流程
sequenceDiagram
participant APP as 应用 (app_update_handle)
participant V2 as code_v2 (update_mode_api_v2)
participant TGT as update_target 遍历
participant PARM as UPDATA_PARM (magic 0x5441)
participant UBOOT as U-Boot / Loader (LOADER.BIN)
participant FLASH as Flash 分区
APP->>V2: 触发升级 (UPDATA_TYPE)
V2->>V2: 填充 parm_type / file_path / ota_addr
V2->>TGT: list_for_each_update_target
TGT-->>V2: 依次调用 driver_close
V2->>V2: 关闭外设/中断/看门狗 (ram_protect_close, hwi_all_close)
V2->>PARM: 写入参数区 (update_param_priv_fill)
V2->>UBOOT: 复位跳转进入升级模式
UBOOT->>FLASH: 按 parm 搬运固件/校验 CRC
UBOOT-->>PARM: 写回 parm_result
FLASH-->>UBOOT: 完成
UBOOT-->>APP: 重启
APP->>APP: update_success_boot_check / update_result_deal
APP->>APP: 设置 DEVICE_FIRST_START 处理首次启动
分步说明:
- 触发:应用调用
update_mode_api_v2(type, priv_param_fill_hdl, priv_update_jump_handle),type指定UPDATA_TYPE(如UART_UPDATA);回调priv_param_fill_hdl允许调用方填充私有参数,priv_update_jump_handle在跳转前回调; - 参数准备:构造
UPDATA_PARM,写入parm_type、文件路径、ota_addr与扩展参数(update_param_priv_fill); - 外设收尾:遍历
update_target段调用所有driver_close,随后关闭 RAM 保护、硬件中断与相关控制器(update.c中common_update_before_jump_reset_handle汇总此类操作,update.h); - 跳转:复位进入 U-Boot/Loader 升级模式,
update_enter_cb_register注册的update_enter_callback会被触发(update.h); - Loader 执行:U-Boot 读取
UPDATA_PARM,按file_path/ota_addr搬运固件并校验(UFW 头 CRC / 数据 CRC),结果写回parm_result; - 结果处理:重启后
update_success_boot_check判断是否升级成功,update_result_deal消费结果,device_is_first_start结合DEVICE_FIRST_START标志驱动首次启动流程。
code_v1 的流程更短:check_ufw_file(dev_name, up_file_path) 校验 UFW 文件(FAT_MOUNT_ERROR 等错误在此返回),随后 try_to_upgrade / try_to_upgrade_api(..., check) 直接执行升级(update_v1.h)。SDK 内建 ONLINE_SUB_OP_ENTER_UPGRADE_MODE(0x26)命令用于在线进入升级模式(cfg_tools.h),该命令经 RCSP/串口工具下发,对应升级工具链的"在线升级"能力。
使用示例
示例一:code_v1 文件升级入口(SD 卡 + UFW)
u32 check_ufw_file(char *dev_name, char *up_file_path);
u32 try_to_upgrade(char *dev_name, char *up_file_path);
u32 try_to_upgrade_api(char *dev_name, char *up_file_path, bool check);
Source: update_v1.h
典型调用序列:先 check_ufw_file 校验文件(返回 NO_ERROR 才继续),再 try_to_upgrade 执行;try_to_upgrade_api 的 check 参数控制是否先做校验。dev_name 为设备名(如 mnt/sd0),up_file_path 为升级文件路径。
示例二:code_v2 升级模式入口
void update_mode_api_v2(UPDATA_TYPE type, void (*priv_param_fill_hdl)(UPDATA_PARM *p), void (*priv_update_jump_handle)(int type));
void update_param_priv_fill(UPDATA_PARM *p, void *priv, u16 priv_len);
Source: update.h
update_mode_api_v2 是 code_v2 的统一升级入口:传入通道类型(如 UART_UPDATA、BLE_APP_UPDATA),并通过两个回调注入私有参数填充逻辑与跳转前处理逻辑。update_param_priv_fill 用于向 UPDATA_PARM 的 parm_priv[32] 区填充自定义私有数据(如校准信息)。
示例三:注册升级目标(外设驱动关闭回调)
#define REGISTER_UPDATE_TARGET(target) \
const struct update_target target sec(.update_target)
struct update_target {
char *name;
update_handler_t driver_close;
};
Source: update.h
模块中声明一个 REGISTER_UPDATE_TARGET(xxx_target) 实例并实现 driver_close 函数,框架遍历 update_target_begin~update_target_end 时自动调用它。新增升级通道时无需修改框架代码,这是本框架最主要的扩展点。
示例四:升级结果查询与处理
u16 update_result_get(void);
int update_result_deal();
void update_result_set(u16 result);
void update_clear_result();
bool update_success_boot_check(void);
bool device_is_first_start();
Source: update.h
应用启动时通常组合使用:update_success_boot_check() 判断本次启动是否为升级成功后的启动;device_is_first_start() 判断 DEVICE_FIRST_START 标志,用于首次启动初始化(如清标志、恢复默认配置)。
配置选项
| 配置项 | 类型 | 默认值 | 说明 | 位置 |
|---|---|---|---|---|
TFG_DEV_UPGRADE_SUPPORT | 宏 | ENABLE | 总开关,使能设备升级功能 | app_config.h |
TFG_UPGRADE_FILE_NAME | 宏 | "/update.ufw" | 升级文件路径(短文件名 8+3,仅 2 层目录) | app_config.h |
UPDATA_MAGIC | 宏 | 0x5A00 | 升级标志/结果 magic 值,防止 CRC==0 误判 | update.h |
UPDATA_KEEP_IO_ENABLE | 宏 | 0 | 升级时是否保持 IO 功能 | update.h |
UPDATE_PARAM_MAGIC | 宏 | 0x5441 | UPDATA_PARM 有效性魔数 | update.h |
UPDATE_PRIV_PARAM_LEN | 宏 | 32 | 私有参数区长度 | update.h |
UPDATA_PARM_SIZE | 宏 | 256 | 参数结构总大小 | update.h |
TESTBOX_UART_UPDATE_EN | 宏 | 视工程 | 使能 TestBox UART 升级(testbox_uart_update.h 条件编译) | update.c |
CONFIG_APP_OTA_EN | 宏 | 视工程 | 使能 RCSP 蓝牙 OTA(rcsp_bluetooth.h 条件编译) | update.c |
CONFIG_UPDATE_STORAGE_DEV_EN | 符号 | 视工程 | 存储设备升级使能(extern const int) | update.h |
CONFIG_UPDATE_TESTBOX_UART_EN | 符号 | 视工程 | TestBox UART 升级使能 | update.h |
CONFIG_UPDATE_APP_OTA_EN | 符号 | 视工程 | 应用 OTA 使能 | update.h |
CONFIG_UPDATE_TESTBOX_BLE_EN | 符号 | 视工程 | TestBox BLE 升级使能 | update.h |
API 参考
code_v2(update.h)
| 函数签名 | 说明 |
|---|---|
void update_mode_api_v2(UPDATA_TYPE type, void (*priv_param_fill_hdl)(UPDATA_PARM *p), void (*priv_update_jump_handle)(int type)) | 统一升级入口:按 type 选择通道,priv_param_fill_hdl 填充私有参数,priv_update_jump_handle 在跳转前回调 |
void update_param_priv_fill(UPDATA_PARM *p, void *priv, u16 priv_len) | 向 UPDATA_PARM.parm_priv 填充私有数据 |
u16 update_result_get(void) | 读取升级结果(UPDATA_RESULT 值) |
bool device_is_first_start(void) | 查询 DEVICE_FIRST_START 标志,判断是否升级后首次启动 |
int update_result_deal(void) | 处理升级结果(消费并清理) |
void update_result_set(u16 result) | 写入升级结果 |
void update_clear_result(void) | 清除升级结果 |
bool update_success_boot_check(void) | 检查本次启动是否为升级成功启动 |
void update_enter_cb_register(void *callback) | 注册进入升级模式的回调(存于 update_enter_callback) |
void app_update_handle(int msg) | 应用层升级消息处理 |
int app_update_init(void) | 应用层升级模块初始化 |
void update_keep_io(UPDATA_PARM *p) | 保持 IO 功能(配合 UPDATA_KEEP_IO_ENABLE) |
u32 update_get_latch_romio(void *data) | 获取锁存的 ROM IO 配置(配合 EXT_KEEP_ROMIO_INFO) |
void common_update_before_jump_reset_handle(void) | 跳转复位前的公共收尾处理 |
int get_ble_connect_handle(void) | 获取 BLE 连接句柄(升级前判断连接状态) |
code_v1(update_v1.h)
| 函数签名 | 说明 |
|---|---|
u32 check_ufw_file(char *dev_name, char *up_file_path) | 校验 UFW 文件(打开设备/挂载 FAT/解析头部),返回 NO_ERROR 表示通过 |
u32 try_to_upgrade(char *dev_name, char *up_file_path) | 执行升级,返回错误枚举值 |
u32 try_to_upgrade_api(char *dev_name, char *up_file_path, bool check) | 带 check 开关的升级入口,check=true 时先校验再升级 |
失败模式、边界情况与并发
code_v1 错误码体系
code_v1 以同步枚举返回升级各阶段错误(update_v1.h):
enum {
NO_ERROR,
SFC_OPEN_ERROR, // SFC 打开失败
DEVIVE_OPEN_ERROR, // 设备打开失败
FAT_MOUNT_ERROR, // FAT 文件系统挂载失败
FILE_OPEN_ERROR, // 升级文件打开失败
FIND_FLASH_BIN_ERROR, // 未找到 flash bin
FIND_FLASH2_BIN_ERROR, // 未找到 flash2 bin
NO_NEED_UPGRADE, // 无需升级(版本相同/不满足条件)
FIND_OTA_ERROR, // 未找到 OTA 数据
FIND_LOADER0_ERROR, // 未找到 Loader0
FLASH_SIZE_ERROR, // Flash 容量不足/不匹配
};
Source: update_v1.h
该枚举覆盖了升级链路的典型故障点:介质层(SFC_OPEN_ERROR)、文件系统层(FAT_MOUNT_ERROR)、文件层(FILE_OPEN_ERROR)、内容层(FIND_OTA_ERROR、FIND_LOADER0_ERROR)与容量层(FLASH_SIZE_ERROR)。NO_NEED_UPGRADE 是正常分支而非错误:当目标固件版本与当前一致时,框架应安全退出而非反复升级。
code_v2 结果与标志的边界
- magic 防误判:
UPDATA_MAGIC = 0x5A00与UPDATE_PARAM_MAGIC = 0x5441都刻意避开 0x00/0xFF/全零,防止未初始化内存或 CRC==0 被误认为有效升级请求; - 首次启动与按键错误:
DEVICE_FIRST_START(BIT31)与DEVICE_UPDATE_KEY_ERR(BIT30)共用一个g_updata_flag变量,属于位标志语义,处理时需按位判断、按位清除,避免互相覆盖; NON_DEV = 0xFFFF:作为通道枚举的"无设备"值,当检测不到升级设备(如未插 SD 卡、未接 USB)时应返回该值,避免误进入升级模式;- 文件路径约束:升级文件必须是短文件名(8+3)且最多两层目录(update.c),长文件名/深层目录会导致
FILE_OPEN_ERROR——这是 FAT 环境下为节省内存而做的硬性限制。
并发与一致性
- 升级过程是单任务、独占式的:进入升级模式前必须关闭其他活动(
ll_hci_destory、hci_controller_destory、hwi_all_close),避免外设中断/蓝牙栈在固件搬运期间干扰 Flash 写入; update_target的driver_close回调在遍历时被顺序调用,注册顺序即关闭顺序,因此依赖顺序的驱动应在注册宏的声明位置体现先后;- 升级参数区(
UPDATA_BEG附近)是 SDK 与 U-Boot 共享的固定内存,复位不丢失;parm_result由 U-Boot 写回,SDK 侧读写需注意内存屏障/缓存一致性(_GNU_PACKED_结构体访问建议按字节或 volatile 处理); - 补丁包中出现
disable_uart_upgrade位(pmu_flag.h),说明量产固件常禁用 UART 升级以缩小攻击面,只保留产线/售后专用通道——安全与便利的权衡由该标志位控制。
性能与运维要点
- CRC 校验开销:UFW 头部含两级 CRC(
Crc与CrcOfSydFileHead),文件较大时校验集中在 Loader 阶段执行,应用侧check_ufw_file只做头部级快速校验,避免重复全量校验拖慢启动; - 升级期间看门狗:
update.c引入wdt.h,升级跳转前需正确喂狗或关闭看门狗,防止长耗时搬运触发复位(common_update_before_jump_reset_handle统一处理此类收尾); - 超时配置:UART 通道
control_timeout单位为 10ms(update.h),SD 通道wDevTimeOutMax用于设备响应超时,产线调试时优先检查这些参数; - 首次启动流程:升级成功后的首次启动通过
DEVICE_FIRST_START区分,用于执行校准恢复、标志清理等一次性操作,漏清标志会导致每次开机都走首次启动流程; - 通道裁剪:
CONFIG_UPDATE_STORAGE_DEV_EN、CONFIG_UPDATE_TESTBOX_UART_EN、CONFIG_UPDATE_APP_OTA_EN、CONFIG_UPDATE_TESTBOX_BLE_EN四个链接期符号(extern const int)用于裁剪通道,ROM 紧张的方案可按需关闭(补丁包中TFG_DEV_UPGRADE_SUPPORT 0的做法即整体关闭升级功能)。
扩展点
- 新增升级通道:在
UPDATA_TYPE枚举中USER_NORFLASH_UFW_UPDATA之前追加新值(保持旧值不变以兼容 Loader),实现通道参数结构体(参考_UPDATA_SD/_UPDATA_UART)并在update_mode_api_v2的分发逻辑中接入; - 注册驱动关闭目标:模块内
REGISTER_UPDATE_TARGET(名字)+ 实现driver_close,框架自动遍历执行,无需改动框架; - 私有参数扩展:通过
update_param_priv_fill填充parm_priv[32],或使用ext_arg_tTLV 扩展EXT_ARG_TYPE新类型(如新增产线校准参数); - 进入升级回调:
update_enter_cb_register注册update_enter_callback,可在跳转前执行自定义逻辑(如保存日志、通知对端); - code_v1 兼容路径:SD 卡 + UFW 场景可直接调用
try_to_upgrade_api;code_v2 的USER_NORFLASH_UFW_UPDATA/USER_LC_FLASH_UFW_UPDATA通道则把 UFW 文件解析复用到 NOR Flash/LC Flash 介质上。
相关实现文件
- sdk/apps/include_lib/update/code_v1/update_v1.h — code_v1 头文件:UFW 格式、错误枚举、升级 API
- sdk/apps/include_lib/update/code_v2/update.h — code_v2 头文件:通道枚举、参数结构、状态机、注册机制
- sdk/apps/app/bsp/common/update/update.c — 应用层升级实现:入口、外设收尾、标志位
- sdk/apps/app/bsp/common/update/dev_update.c — 设备升级实现
- sdk/apps/app/bsp/common/update/testbox_uart_update.c — TestBox UART 升级通道
- sdk/apps/app/bsp/common/update/testbox_update.c — TestBox 升级通道
- sdk/apps/app/bsp/common/third_party_profile/jieli/JL_rcsp/rcsp_update/rcsp_user_update.c — RCSP 用户升级(BLE OTA 应用侧)
- sdk/apps/app/bsp/common/third_party_profile/jieli/JL_rcsp/rcsp_update/rcsp_ch_loader_download.c — RCSP 通道 Loader 下载
- sdk/apps/app/src/mbox_flash/app_config.h — 升级功能开关与升级文件名配置
- sdk/apps/include_lib/config/cfg_tools.h — 在线进入升级模式命令(0x26)
Related Links
- BLE OTA / RCSP 升级:参见 RCSP 升级与通道下载相关页面(
rcsp_user_update.c/rcsp_ch_loader_download.c归属的 RCSP 能力页) - UART/TestBox 产线升级:参见 TestBox 升级通道相关页面
- U-Boot 与 Loader:引导程序侧的固件搬运与
parm_result回写,参见 Boot/Loader 相关文档 - SDK 配置总览:
app_config.h各功能开关(TFG_DEV_UPGRADE_SUPPORT所在配置体系)