安全升级通道(SD/USB)
安全升级通道(SD/USB)是杰理(Jieli)AC791N/AC792N 等芯片 OTA Loader 体系中,通过 SD/TF 卡或 USB 存储介质完成固件升级的端到端能力:涵盖升级介质、Loader 驱动、文件系统、加密校验、Flash 双 Bank 写入与上位机工具链。
Purpose and Scope
本页面向「SD/USB 安全升级通道」这一完整能力,覆盖:
- OTA Loader 中三个升级通道工程(
sd_ota_update、sd_sec_ota_update、uart_user_update)的职责划分; - 升级模式枚举、触发机制(I/O 检测 / 软件复位 magic);
- 固件包格式(
JL_FILE_HEAD、FW_FILE_HEAD_V1、JL_FLASH_HEAD_V2)、CRC 校验与 UFW 加密固件的安全机制(AES-CBC / xor_swap / 芯片密钥); - 双 Bank 升级、SD 卡与 USB Host 驱动接口、升级错误码体系;
- 上位机工具(win-uart / win-usb_hid / android-usb_hid)与
uboot升级使用说明中的配置约定。
边界说明:串口(UART)升级通道、蓝牙 BT OTA、上位机 PC 工具内部实现、SDK 应用侧 OTA 触发逻辑分别属于各自目录页的内容,本页仅在涉及模式选择与触发接口时交叉引用。升级核心逻辑(fs_v2_update.c 等)在本仓库中以预编译静态库(update.a、ota_loader_driver.a)形式发布,本页将基于库导出符号、目录结构与官方协议文档给出可验证的说明,并对无法从源码直接确认的实现细节如实标注。
Overview
在量产与售后场景中,设备可能没有连接 PC 或手机的条件,SD 卡 / USB 存储因此成为最便捷的离线升级介质:用户将升级固件(jl_isd.bin 或加密 UFW 包)拷贝到 SD 卡/U 盘,插入设备并触发升级,Loader 在 uboot 阶段完成固件校验、解密与写入。
设计意图与关键概念:
- 安全升级(Secure OTA):
sd_sec_ota_update工程是安全通道的代表——升级包可被加密(AES-CBC、xor_swap 等算法由register_crypto_aes_handle/register_crypto_xor_swap_handle注册),配合芯片 eFuse 密钥(burn_chip_key/get_chip_key)、CRC 校验与产品匹配检查(UPDATE_ERR_PRODUCT_ID_NOT_MATCH)防止固件被篡改或误刷。 - 双 Bank 升级:通过
jlfs_dual_bank_check、jlfs_dual_bank_choose_code、jlfs_updata_dual_bank_info把新固件写入空闲 Bank,升级失败可回退,避免"刷死"。 - 升级模式:库内导出
UPGRADE_NULL、UPGRADE_USB_HARD_KEY、UPGRADE_USB_SOFT_KEY、UPGRADE_UART_SOFT_KEY、UPGRADE_UART_ONE_WIRE_HARD_KEY,SD/USB 通道通常配合 USB 相关模式使用,区分"硬按键触发"与"软件(magic)触发"。 - 离线可操作:升级由 Loader 在无操作系统依赖的 uboot/loader 阶段完成,配 LCD 时通过
refresh_upgrade_progress_ui反馈进度。
Architecture
flowchart TD
subgraph sg_Media["升级介质层"]
SD["SD/TF 卡"]
USB["USB 存储设备<br/>(U盘/读卡器)"]
end
subgraph sg_Loader["OTA Loader (uboot/loader 阶段)"]
SDDrv["SD 驱动<br/>sd_init_card / sd_read_card / JL_SDC"]
USBDrv["USB Host 驱动<br/>usb_h_ep_read_async"]
FS["JLFS 文件系统<br/>jlfs_mount / jlfs_fopen_by_name"]
Crypto["加密引擎<br/>AES-CBC / xor_swap / CRC16"]
UpdateEngine["升级引擎 update.a<br/>fs_v2_update.c"]
FlashDrv["Flash 驱动<br/>norflash_write / jlfs_dual_bank"]
end
subgraph sg_Flash["目标存储"]
BankA["Bank A (当前固件)"]
BankB["Bank B (新固件)"]
end
SD --> SDDrv
USB --> USBDrv
SDDrv --> FS
USBDrv --> FS
FS --> UpdateEngine
Crypto --> UpdateEngine
UpdateEngine --> FlashDrv
FlashDrv --> BankA
FlashDrv --> BankB
架构分层说明:
- 介质层:SD/TF 卡经 SDIO 控制器访问;USB 存储经 USB Host(
usb_h_ep_read_async等批量端点接口)访问,两者都向 JLFS 提供块设备语义。 - 驱动层:SD 驱动导出
sd_init_card、sd_read_card、sd_write_card、sd_ctnu_read_enter/write_enter等接口,JL_SDC为 SD 控制器封装;USB Host 驱动导出usb0/1_h_ep_read_async、usb_h_ep_write_async等端点读写接口(参见 ota_loader_driver.a 符号表)。 - 文件系统层:JLFS(
jlfs_*系列)负责挂载介质、按文件名(jlfs_fopen_by_name)或地址(jlfs_fopen_by_addr)打开升级文件,并管理 Flash 分区与双 Bank 信息。 - 升级引擎:
update.a(编译自fs_v2_update.c)实现fs_v2_update文件系统升级主流程,导出jl_check_upgrade、update_loop_run、goto_mask_usb_updata、jump_ota_entry_addr等入口。 - 安全层:加密引擎提供 AES-CBC(
AES_cbc_encrypt、AES_set_encrypt_key)、xor_swap(xor_swap_encrypt/decrypt)、CRC16/CRC8,并通过register_crypto_aes_handle/register_crypto_xor_swap_handle允许注册自定义加解密句柄;芯片密钥来自 eFuse(burn_chip_key/get_chip_key)。
升级通道工程结构
flowchart TD
subgraph sg_Common["公共库 (wl83)"]
Lib["update.a<br/>fs_v2_update.c 升级引擎"]
Driver["ota_loader_driver.a<br/>jl_check_upgrade / update_loop_run"]
end
subgraph sg_Projects["ac792n-ota-loader 升级通道工程"]
P1["sd_ota_update<br/>SD 普通升级"]
P2["sd_sec_ota_update<br/>SD 安全升级 (含LCD界面)"]
P3["uart_user_update<br/>UART 用户升级"]
end
Lib --> P1
Lib --> P2
Lib --> P3
Driver --> P1
Driver --> P2
Driver --> P3
三个工程共享同一套 wl83 预编译库,差异在应用层:
| 工程目录 | 通道 | 特点 |
|---|---|---|
| sd_ota_update | SD 卡 | 基础 SD 通道,include_lib/liba/wl83/ 下同样包含 update.a 与 ota_loader_driver.a |
| sd_sec_ota_update | SD 卡(安全) | 面向安全升级场景,工程含完整 LCD 显示栈(app/src/common/lcd/lcd_driver/ 支持 mcu/mipi/rgb/spi 各型屏),升级时可显示进度 |
| uart_user_update | UART | 用户串口通道,与 SD/USB 共享 UPGRADE_* 模式与错误码体系 |
说明:升级引擎与驱动以预编译
.a发布,本页无法逐行展开fs_v2_update.c内部实现;但可从库内嵌符号(如update.a中的UPDATE_ERR_*、UPDATE_PART_*、EXT_*、fs_v2_update.c编译路径)验证其功能组成,详见后续章节。
升级模式与触发机制
升级模式枚举
ota_loader_driver.a 内嵌符号显示升级模式枚举(编译自 ota_loader/lib/driver 的 upgrade.c):
UPGRADE_NULL
UPGRADE_USB_HARD_KEY
UPGRADE_USB_SOFT_KEY
UPGRADE_UART_SOFT_KEY
UPGRADE_UART_ONE_WIRE_HARD_KEY
设计意图:模式区分"硬件按键/IO 触发"(HARD_KEY)与"软件复位触发"(SOFT_KEY)。SD/USB 安全升级通常对应 UPGRADE_USB_* 模式——设备上电后 Loader 检测 USB 设备枚举与升级文件,或收到 SDK 写入的 magic 后进入对应通道。update.a 中另有通道枚举 USB_UPDATA、SD0_UPDATA、SD1_UPDATA、PC_UPDATA、UART_UPDATA、BT_UPDATA,用于标识当前升级来源(参见 update.a 符号表)。
两种触发方式
根据 uboot升级使用说明v1.1.2.md,升级触发方式有两种:
- I/O 口检测触发:进入 uboot 后,
main函数检测指定 I/O 的电平状态,决定是否跳转升级流程(适用于产线硬触发)。 - SDK 软件复位触发:SDK 侧写入 magic 标志到
nvram_list保留区,再软复位进入 Loader。SDK 侧需使能USE_UPGRADE_MAGIC宏,并实现如下两个函数:
extern u32 nvram_list[];
#define NV_RAM_LIST_ADDR nvram_list
static u8 uboot_uart_upgrade_mode_magic[8] = {'u', 'b', 'o', 'o', 't', 0x5a, 's', 't', };
static u8 uboot_uart_upgrade_succ_magic[8] = {'u', 'b', 'o', 'o', 't', 0xa5, 'o', 'k', };
void check_uboot_uart_upgrade() //检查 uboot 升级是否成功,须在 memory_init();前检测该标志
{
if (memcmp((char *)NV_RAM_LIST_ADDR, uboot_uart_upgrade_succ_magic, sizeof(uboot_uart_upgrade_succ_magic)) == 0) {
memset((char *)NV_RAM_LIST_ADDR, 0, sizeof(uboot_uart_upgrade_succ_magic));
log_info("uboot uart upgrade succ\n");
}
}
void hw_mmu_disable(void);
void chip_reboot_entry_uboot_uart_upgrade_mode() // uboot 升级跳转函数
{
memcpy((char *)NV_RAM_LIST_ADDR, uboot_uart_upgrade_mode_magic, sizeof(uboot_uart_upgrade_mode_magic));
hw_mmu_disable();
cpu_reset();
}
这段代码揭示了安全升级通道的关键设计:magic 握手协议。0x5a 's' 't' 表示"进入升级模式",0xa5 'o' 'k' 表示"升级成功";成功标志由 Loader 写入、SDK 在 memory_init() 之前读取并清零,从而保证升级结果在系统初始化早期即可被感知,且标志不会残留造成重复触发。hw_mmu_disable() 后 cpu_reset() 确保以干净的地址映射进入 Loader。
nvram_list 的保留区地址按芯片型号在链接脚本中定义(maskron_stubs.ld 配置):
nvram_list = ABSOLUTE(0x800); // BD19
nvram_list = ABSOLUTE(0x10800); // BR23
nvram_list = ABSOLUTE(0x10880); // BR25
nvram_list = ABSOLUTE(0x180800); // BR28
nvram_list = ABSOLUTE(0x28800); // BR30 / BR34
nvram_list = ABSOLUTE(0x7ee4); // SH54
nvram_list = ABSOLUTE(0x4ee4); // SH55
不同芯片的保留区地址不同,这是移植安全升级通道到新平台时必须核对的第一项参数。
固件包格式与安全校验
update.a 内嵌类型符号(update.a 符号表)给出了升级包的核心数据结构:
| 结构/常量 | 字段 | 用途 |
|---|---|---|
JL_FILE_HEAD | DataCrc(u16)、Address(u32)、Attributes(u8)、zFileName | 文件系统级文件头,携带数据 CRC 与目标地址 |
FW_FILE_HEAD_V1 | FileType(u8)、Index(u16)、Addr/Length、EncryptedAddr/EncryptedLength(u32)、name | 固件文件头:区分类型、索引,描述明文/加密区段 |
JL_FLASH_HEAD_V2 | SizeForBurner(u16)、vid、FlashSize(u32)、FsVersion、BlockAlingnModulus、SpecialOptFlag、pid | Flash 整包头:容量、文件系统版本、产品 PID/VID(用于 UPDATE_ERR_PRODUCT_ID_NOT_MATCH 校验) |
FW_SYD_HEAD_V1 | Crc、CrcOfSydFileHead、FileLength、FileCount、Version、szChipName | 多文件打包头(SYD 包),含文件数与芯片名 |
_UPDATA_PARM | parm_crc、parm_type、parm_result、magic、file_patch、ota_addr、ext_arg_len/crc | Loader 与 SDK 间传递的升级参数块 |
_BANKCB | bankNum、size、bankaddr、crc、crc1 | 双 Bank 控制块:各 Bank 地址、大小与校验值 |
update_part_info_t | file_name、flag、start_addr、update_func、err_code | 分区升级表项:按分区(BT_CFG/TONE/RESERVED/ANC 等)执行更新 |
文件命名常量 APP_CODE_FILE_NAME、APP_CODE_FILE1_NAME、SYS_CONFIG_FILE_NAME、UBOOT_FILE_NAME、USER_API_BIN_NAME 表明一次 SD/USB 升级可以同时更新 app 代码、系统配置、uboot 与用户 API bin——这正是"整机升级"能力的来源:Loader 按 g_a_part_of_update_flag / update_part_tab 逐分区处理。
加密与安全机制
库符号明确暴露了两套加解密路径(ota_loader_driver.a 符号表):
// AES-CBC 路径
AES_set_encrypt_key / AES_set_decrypt_key / AES_encrypt / AES_decrypt / AES_cbc_encrypt
// xor_swap 路径
xor_swap_generate_encrypt_key / xor_swap_encrypt / xor_swap_decrypt
// 注册点(扩展点)
register_crypto_aes_handle / register_crypto_xor_swap_handle
// USB 协议加密(CDB 命令块 / ACK 应答 / 数据)
crypto_usb_cdb_decrypt / crypto_usb_ack_encrypt / crypto_usb_data_decrypt / crypto_usb_data_encrypt
// 芯片密钥
burn_chip_key / get_chip_key
设计意图与安全模型:
- 算法可插拔:
register_crypto_aes_handle/register_crypto_xor_swap_handle允许方案商替换默认加解密实现(如更换密钥派生方式),而升级引擎只调用统一句柄,兼顾安全性与灵活性。 - 密钥来源于芯片:
burn_chip_key/get_chip_key表明密钥可烧录于 eFuse(一次性可编程),使每个设备持有私有密钥,即使固件包被提取也无法在其他设备上解密——这是"安全升级通道"相对普通 SD 升级的核心差异(sd_sec_ota_updatevssd_ota_update)。 - USB 通道逐包加密:
crypto_usb_cdb_decrypt/crypto_usb_ack_encrypt/crypto_usb_data_decrypt表明 USB 升级协议对命令块、应答与数据分别加解密,防止中间人篡改。 - 多层 CRC:
chip_crc16、crc16_sw、crypto_crc8、crc_encode等覆盖文件头、数据区与协议包,配合JL_FILE_HEAD.DataCrc/FW_SYD_HEAD_V1.Crc形成端到端完整性校验。
错误码体系
update.a 导出的错误码(UPDATE_ERR_*)与结果码(UPDATE_RESULT_*)构成升级通道的完整失败语义:
| 类别 | 代表错误码 | 含义 |
|---|---|---|
| 头/CRC | UPDATE_ERR_UFW_HEAD_CRC_ERR、UPDATE_ERR_FLASH_HEAD_CRC_ERR、UPDATE_ERR_LOCAL_FILE_HEAD_CRC_ERR、UPDATE_ERR_LOCAL_FILE_DATA_CRC_ERR | 升级包头或数据 CRC 校验失败 |
| 文件查找 | UPDATE_ERR_NOT_FIND_LOADER_FILE、UPDATE_ERR_NOT_FIND_TARGET_FILE、UPDATE_ERR_NOT_FIND_RESERVE_DIR_FILE、UPDATE_ERR_NOT_FIND_FLASH_BIN | 介质上缺少指定文件 |
| 安全校验 | UPDATE_ERR_KEY_ERR、UPDATE_ERR_PRODUCT_ID_NOT_MATCH、UPDATE_ERR_UBOOT_NOT_MATCH、UPDATE_ERR_CODE_VERIFY_ERR、UPDATE_ERR_RES0_VERIFY_ERR | 密钥、产品 ID、uboot 匹配或代码校验失败 |
| 写入 | UPDATE_ERR_ERASE_FAIL、UPDATE_ERR_LOADER_WRITE_ERR、UPDATE_ERR_LOADER_VERIFY_ERR | Flash 擦写或写后校验失败 |
| 分区 | UPDATE_ERR_BT_CFG_UPDATE_ERR、UPDATE_ERR_TONE_UPDATE_ERR、UPDATE_ERR_ANC_CFG_UPDATE_ERR、UPDATE_ERR_RESERVED_ZONE_UPDATE_ERR | 特定分区更新失败 |
| 其他 | UPDATE_ERR_MALLOC_ERR、UPDATE_ERR_PARAM_ERR、UPDATE_ERR_CONN_ERR、UPDATE_ERR_UPDATE_FILE_SIZE_ERR | 内存、参数、连接与文件大小异常 |
来源:update.a 符号表
错误码的分层设计(查找 → 校验 → 写入 → 分区)使上层 UI(如 sd_sec_ota_update 的 LCD 界面)和上位机能够将失败精确归类,并在产线上快速定位是介质问题、固件包问题还是硬件问题。
核心升级流程
一次 SD/USB 安全升级的端到端时序如下:
sequenceDiagram
participant User as 用户/上位机
participant Media as SD卡/USB设备
participant Loader as OTA Loader
participant FS as JLFS
participant Crypto as 加密引擎
participant Flash as 内部Flash
User->>Loader: 触发升级 (IO检测 / 软件复位magic)
Loader->>Loader: 初始化外设 (SDIO / USB Host)
Loader->>Media: 挂载文件系统 (jlfs_mount)
Media-->>Loader: 介质就绪
Loader->>FS: 查找升级文件 (jlfs_fopen_by_name)
FS-->>Loader: 打开 jl_isd.bin / UFW 包
Loader->>FS: 读取文件头 (JL_FILE_HEAD / FW_FILE_HEAD_V1)
Loader->>Crypto: 校验头 CRC + PID/VID 匹配
Crypto-->>Loader: 通过
loop 按分区逐块处理 (update_part_tab)
Loader->>Crypto: 解密数据 (AES-CBC / xor_swap)
Loader->>Flash: 写入空闲 Bank (jlfs_dual_bank)
Loader->>Flash: 回读校验 (verify)
end
Loader->>FS: 更新 dual_bank_info
Loader->>User: 写入成功 magic (0xa5 'o' 'k') + 复位
User->>Loader: 从新 Bank 启动
关键步骤说明:
- 触发:Loader 的
main流程调用jl_check_upgrade()检查升级条件(I/O 电平或nvram_list中的 magic),命中后进入升级流程而不是正常启动。goto_mask_usb_updata()用于跳转到 USB 升级入口(ota_loader_driver.a 符号表)。 - 介质初始化:SD 通道走
sd_init_card+JL_SDC;USB 通道走usb_h_sie_init/usb_h_ep_read_async枚举存储设备。 - 文件系统挂载:
jlfs_mount挂载介质上的 FAT/JLFS 文件系统,jlfs_fopen_by_name定位APP_CODE_FILE_NAME等升级文件,jlfs_get_flen_by_name获取文件长度用于进度计算(update_total_len/update_cur_len)。 - 头校验与安全验证:解析
FW_FILE_HEAD_V1的明文/加密区段描述(EncryptedAddr/EncryptedLength),校验 CRC、产品 PID/VID、uboot 匹配,失败即返回对应UPDATE_ERR_*并终止。 - 分区写入:升级引擎按
update_part_tab遍历分区(代码、配置、uboot、tone、ANC 等),对加密区段调用 AES/xor_swap 解密后写入 Flash;写后回读校验(UPDATE_ERR_LOADER_VERIFY_ERR)。 - 收尾:
jlfs_updata_dual_bank_info更新 Bank 切换信息;写入成功 magic,复位后从新 Bank 启动;SDK 在memory_init()前调用check_uboot_uart_upgrade()确认结果。
双 Bank 升级机制
升级的可靠性由双 Bank 机制保证,相关接口(ota_loader_driver.a 符号表):
jlfs_dual_bank_check // 检查双 Bank 布局是否有效
jlfs_dual_bank_choose_code // 选择当前运行的代码 Bank
jlfs_updata_dual_bank_info // 升级完成后更新 Bank 信息
jlfs_get_idle_bank_info // 获取空闲 Bank(升级写入目标)
get_sdk_swap_addr // 获取 SDK 交换地址
flowchart TD
Start([升级开始]) --> Check{"jlfs_dual_bank_check<br/>布局有效?"}
Check -->|"否"| Direct["单 Bank 直接覆盖写入"]
Check -->|"是"| Idle["jlfs_get_idle_bank_info<br/>获取空闲 Bank"]
Idle --> Write["写入空闲 Bank + 回读校验"]
Write --> Result{"校验通过?"}
Result -->|"否"| Keep["保留旧 Bank<br/>继续运行旧固件"]
Result -->|"是"| Swap["jlfs_updata_dual_bank_info<br/>切换启动 Bank"]
Swap --> Reboot([复位进入新固件])
Direct --> Reboot
设计意图:写入目标永远是"非当前运行"的 Bank,配合 _BANKCB(bankaddr/crc)记录,任何时刻 Flash 中都至少存在一个可启动的固件。升级中途掉电、写坏、校验失败都不会破坏当前固件,这是 SD/USB 离线升级能面向非专业用户开放的前提。
SD 卡与 USB 驱动层
SD 通道的底层能力(ota_loader_driver.a 符号表):
sd_hw_init / sd_init_card // 初始化 SD 控制器并完成卡识别
sd_set_data_width / sd_set_buad // 总线宽度与时钟配置
sd_read_card / sd_write_card // 扇区级读写
sd_ctnu_read_enter / sd_ctnu_read_exit // 连续读模式(提升吞吐)
sd_ctnu_write_enter / sd_ctnu_write_exit // 连续写模式
sd_stop_card / JL_SDC // 停止卡 / 控制器封装
USB 通道的底层能力:
usb_h_sie_init / usb_h_sie_close // USB Host SIE 初始化/关闭
usb_h_chirp_and_reset / usb_h_force_reset // 总线复位与枚举
usb0_h_ep_read_async / usb1_h_ep_read_async // 批量端点异步读
usb_h_ep_write_async / usb_h_ep_write // 批量端点写
usb_set_timeout / usb_host_timeout // 传输超时控制
性能设计要点:SD 连续读写模式(sd_ctnu_*_enter)在升级大文件时避免逐扇区命令开销;USB 批量端点异步读(usb_h_ep_read_async)与写 Flash 流水线化,减少总线空闲。usb_set_timeout / usb_host_timeout 防止劣质读卡器/U 盘挂死升级流程。
USB_HID 通道约束(来自 uboot升级使用说明):选择 usb_hid 升级时,HID 传输包长最大 64 字节,写 flash 命令还占用若干字节,因此实际写入 flash 的数据长度 = 64 − 写 flash 命令长度。上位机与 Loader 必须按此协商分包,否则出现数据错位——这是 USB HID 升级最常见的移植坑。
配置选项
以下配置项来自 uboot升级使用说明v1.1.2.md 与 ota_loader_driver.a 符号:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
USB_MODE | 宏(编译期) | 0 | uboot 升级模式选择:0=串口升级,1=USB_HID 升级 |
__DEBUG | 宏(编译期) | 未定义 | 使能 Loader 调试打印 |
usb_vid / usb_pid | 常量 | 芯片工程内置 | uboot 与上位机(win-usb_hid / android-usb_hid)必须一致才能枚举 |
communication_key | 变量 | 12345678(十进制) | 串口升级通信加密密钥,在 user.c 中修改 |
USE_UPGRADE_MAGIC | 宏 | 关闭 | SDK 侧使能软件复位触发升级 |
nvram_list | 链接脚本符号 | 按芯片(BD19: 0x800 … SH55: 0x4ee4) | magic 标志保留区地址 |
EOFFSET=1 | isd_config.ini 配置 | 无 | 存在时需配合 GENERATE_TWO_BIN = YES 生成 jl_isd_4K.bin / jl_isd_0K.bin |
| 升级包长度 | 协议参数 | 串口 4K / HID 64B | 串口包长自定义默认 4K;HID 受 64 字节限制 |
API 参考
升级引擎与驱动在本仓库中以预编译库形式发布,以下签名来自库导出符号,参数与返回值的精确声明需以 SDK 头文件(
include_lib/update/update_main.h、include_lib/update/fs_v2_update.h等)为准;此处列出的是本仓库可验证的符号与语义。
jl_check_upgrade()(ota_loader_driver.a)
Loader 启动早期调用的升级检测入口:检查 I/O 触发条件与 nvram_list magic,判定是否进入升级流程。
返回值:升级条件满足时返回进入升级流程的指示,否则走正常启动。
update_loop_run()(ota_loader_driver.a)
升级主循环,驱动整个升级状态机(查找文件 → 校验 → 解密 → 写入 → 回读校验)。
goto_mask_usb_updata() / goto_mask_uart_upgrade()(ota_loader_driver.a)
跳转到 maskrom 掩码升级入口(USB / UART),用于 Loader 自身无法完成升级时的兜底通道。
jump_ota_entry_addr()(ota_loader_driver.a)
跳转到 OTA 应用入口地址,升级完成后引导新固件。
JLFS 文件系统接口(ota_loader_driver.a)
| 函数 | 语义 |
|---|---|
jlfs_mount() | 挂载 Flash/介质文件系统 |
jlfs_fopen_by_name() / jlfs_fopen_by_addr() | 按文件名或地址打开文件 |
jlfs_fread() / jlfs_fseek() / jlfs_get_flen_by_name() | 读、定位、取长度 |
jlfs_dual_bank_check() / jlfs_dual_bank_choose_code() / jlfs_updata_dual_bank_info() | 双 Bank 检查、选 Bank、更新信息 |
jlfs_get_flash_head() / jlfs_check_all_head() | 读取/校验 Flash 头 |
加密接口(ota_loader_driver.a)
| 函数 | 语义 |
|---|---|
AES_cbc_encrypt() / AES_set_encrypt_key() / AES_encrypt() | AES-CBC 加密路径 |
xor_swap_generate_encrypt_key() / xor_swap_encrypt() / xor_swap_decrypt() | xor_swap 轻量加密路径 |
register_crypto_aes_handle() / register_crypto_xor_swap_handle() | 注册自定义加密句柄(扩展点) |
crypto_usb_cdb_decrypt() / crypto_usb_ack_encrypt() / crypto_usb_data_decrypt() / crypto_usb_data_encrypt() | USB 升级协议逐包加解密 |
burn_chip_key() / get_chip_key() | eFuse 芯片密钥烧录/读取 |
失败模式、边界情况与并发
失败模式(对应错误码):
- 介质问题:卡/U 盘未插入或枚举失败 → 升级不启动或
UPDATE_ERR_CONN_ERR;文件系统损坏 →UPDATE_ERR_NOT_FIND_TARGET_FILE。 - 固件包问题:头 CRC 错 →
UPDATE_ERR_UFW_HEAD_CRC_ERR;数据 CRC 错 →UPDATE_ERR_LOCAL_FILE_DATA_CRC_ERR;文件大小不符 →UPDATE_ERR_UPDATE_FILE_SIZE_ERR。 - 安全校验失败:密钥不匹配 →
UPDATE_ERR_KEY_ERR;PID/VID 不匹配 →UPDATE_ERR_PRODUCT_ID_NOT_MATCH;uboot 不匹配 →UPDATE_ERR_UBOOT_NOT_MATCH;代码校验失败 →UPDATE_ERR_CODE_VERIFY_ERR。设计上这些校验先于任何 Flash 写入执行,杜绝半写入状态。 - 写入失败:擦除失败 →
UPDATE_ERR_ERASE_FAIL;写后回读校验失败 →UPDATE_ERR_LOADER_VERIFY_ERR。双 Bank 下失败可安全回退。
边界情况:
- 掉电:双 Bank 机制保证任意时刻存在可启动固件;
_BANKCB的crc字段使 Loader 能识别未完成写入的 Bank 并回退。 - EOFFSET 变体:
EOFFSET=1时需按upgrade_eoffset(4K/0K)选择jl_isd_4K.bin/jl_isd_0K.bin,选错会导致烧录后启动异常(文档说明)。 - magic 残留:成功标志必须在
memory_init()前读取并清零,否则重复复位会误报升级成功。
并发与中断:
- Loader 升级流程是单线程状态机(
update_loop_run),通过wdt_clear(看门狗喂狗)与usb_set_timeout/usb_host_timeout管理超时;中断路径(spi_block_data_deal_irq、sys_tmr_isr等)只做数据搬运,不参与升级状态切换。 - USB 批量端点异步读(
usb_h_ep_read_async)与 Flash 写入之间通过 DMA 完成度同步,不存在多线程竞争;异常路径由超时机制兜底。
性能与运维考虑
- 吞吐关键路径:SD 连续读写模式(
sd_ctnu_read_enter/sd_ctnu_write_enter)与 USB 异步批量读是升级速度的主要决定因素;加密(AES/xor_swap)与 CRC 计算在软件中完成,对大包有 CPU 开销,建议按实际包大小评估。 - 进度反馈:引擎维护
update_total_len/update_cur_len,通过refresh_upgrade_progress_ui驱动 LCD 进度条(sd_sec_ota_update工程内置完整 LCD 栈)。 - 产线建议:遵循文档测试流程——备份程序 A,烧录程序 B,再通过 SD/USB 从 A 升级回 B,验证双向升级与回退;每更换芯片型号先核对
nvram_list地址与communication_key。
扩展点
- 自定义加密算法:
register_crypto_aes_handle/register_crypto_xor_swap_handle注册自定义句柄,替换默认 AES/xor_swap,适合要求私有算法的方案。 - 密钥管理:
burn_chip_key/get_chip_key支持 eFuse 密钥方案,可在产线烧录时写入,使升级包与具体设备绑定。 - 升级分区扩展:
UPDATE_PART_*(UPDATE_PART_BT_CFG、UPDATE_PART_TONE、UPDATE_PART_RESERVED、UPDATE_PART_ANC_CFG、UPDATE_PART_ANC_COEF、UPDATE_PART_ALL_FLASH)与update_part_info_t表结构允许扩展新分区。 - 扩展参数:
EXT_ARG_TYPE、EXT_BT_MAC_ADDR、EXT_RF_PA_INFO、EXT_LDO_TRIM_RES、EXT_SD_IO_INFO等扩展参数机制允许升级时顺带写入 MAC、射频参数、LDO 校准等产线数据。 - 上位机:
fw-Bootloader/update_tools下的 win-uart、win-usb_hid、android-usb_hid 工具开放源码(pc_demo\usb_hid\main.cpp),可二次开发对接产线系统。
测试覆盖
uboot升级使用说明v1.1.2.md 的测试流程 定义了 SD/USB 通道的验收方法:
- 编译 uboot 工程生成
uboot.boot,复制到 SDK 下载目录; - 编译下载 SDK 生成程序 A(备份);
- 修改 SDK 后再次编译下载得到程序 B(设备上运行的版本);
- 用上位机(串口或 USB_HID)将程序 A 升级回设备,验证升级与版本回退;
- 若使用 I/O 触发,验证上电电平触发;若使用软件触发,验证
chip_reboot_entry_uboot_uart_upgrade_mode()跳转与check_uboot_uart_upgrade()成功标志。
该流程同时覆盖了正常升级、版本回退、触发方式三种核心场景,是安全升级通道发布前的标准回归项。