# Banban OTA-ready 工厂基线 ## 1. 目标和边界 本基线用于尚未量产的 `EG800AKCN_11LG` 设备。目标是在不改变现有对话、音频、 MQTT、NFC、GNSS、按键、休眠和 LED 业务流程的前提下,一次性固定后续本地暂存式 OTA 所依赖的底层条件。 本基线完成: - 将 `customer_fs` 从 256 KiB 扩大到 640 KiB; - 保持 `customer_app` 为 460 KiB; - 将 APP 物理/XIP 地址固定为 `0x002C2000` / `0x802C2000`; - 保留 SDK 已有的 `QUEC_APP_FULL_FOTA` 与 FULLOTA2 公共 API; - 为设备和构建产物加入唯一布局身份; - 在构建前后执行 fail-closed 校验。 本基线暂不完成: - 不把当前 `ql_fullfota_app_start()` 在线升级流程切换成 FULLOTA2; - 不增加签名 manifest、SHA-256 下载校验、断点续传或后台灰度; - 不提供 A/B APP 分区,也不承诺自动版本回滚; - 不在线更新 kernel、分区表或 `customer_fs`。 这样安排是为了先冻结量产后难以改变的 Flash 布局,同时避免在生产前仓促替换业务 OTA 状态机。后续 FULLOTA2 逻辑只需通过 `customer_app` 版本迭代即可加入。 > 生产门禁:从本工厂基线开始量产,到 FULLOTA2 业务 APP 完成真机验收并正式发布之前, > 后端必须暂停向生产设备下发 OTA 命令。当前 `ql_fullfota_app_start()` 入口仍是原有 > FULLOTA1 流程,新的分区基线不会自动消除其覆盖下载风险。 ## 2. 固定身份与 Flash 布局 设备布局身份: ```text eg800akcn_11lg-fs640-app2c2000 ``` 构建 profile: ```text ota_ready_fs640 ``` 4 MiB 内部 Flash 的尾部分区固定如下: | 分区 | 起始地址 | 结束地址 | 大小 | | --- | ---: | ---: | ---: | | reserved | `0x00287000` | `0x002C2000` | 236 KiB | | customer_app | `0x002C2000` | `0x00335000` | 460 KiB | | customer_fs | `0x00335000` | `0x003D5000` | 640 KiB | | nvm | `0x003D5000` | `0x003F5000` | 128 KiB | | erase_rd | `0x003F5000` | `0x003F7000` | 8 KiB | | fota_param | `0x003F7000` | `0x003FA000` | 12 KiB | | factory_a | `0x003FA000` | `0x00400000` | 24 KiB | `customer_app` 是单槽。FULLOTA2 能保护下载阶段:文件未下载完整、哈希错误或 FBF 校验 失败时不会设置升级标志;但 updater 开始擦写 `customer_app` 后,没有旧 APP 槽可供回滚。 ## 3. 本次修改 ### 3.1 分区和链接地址 - `CRANELRH_QUEC_FLASH_LAYOUT_SS_04M_OPEN.json`:`customer_fs = 640KiB`; - `app_linkscript.ld`:ROM 起始地址改为 `0x802C2000`,最大 ROM 大小收紧为 `0x73000`; - `customer_fs.bin`:从原 256 KiB LittleFS 中提取全部 14 个音频文件,以 4096 字节块、 160 个块重新生成 640 KiB LittleFS; - 新文件系统镜像 SHA-256: `19bd849dbcc6816f6dfffd1e585645a3474a2265f96a926b5dd073f0affb69f9`。 ### 3.2 运行身份 `ota_baseline.h` 固定布局身份、APP/FS 地址和分区大小。应用启动时打印: ```text [OTA-BASELINE] layout=eg800akcn_11lg-fs640-app2c2000 ... slot=single [OTA-BASELINE] U: total=... free=... fota2_reserve=65536 ``` 该日志用于产线和联调确认设备没有混入旧的 fs256 基线,不改变任何业务状态。 ### 3.3 构建保护 `build_rel.bat` 在构建前运行源码校验,在生成 APP 后运行容量校验。任何一项不满足都会中止: - `customer_fs` 必须为 640 KiB; - APP 地址必须为 `0x002C2000`; - LittleFS 必须为 4096 字节块、160 块且镜像哈希一致; - kernel 必须继续定义 `QUEC_APP_FULL_FOTA`; - FULLOTA2 所需的 SDK API 必须存在; - `app.bin + 8192` 不得超过 460 KiB。 可独立执行: ```powershell ql-cross-tool\win32\owtoolchain\Python\python.exe tools\verify_ota_ready_baseline.py ql-cross-tool\win32\owtoolchain\Python\python.exe tools\verify_ota_ready_baseline.py ` --app-bin ql-application\threadx\build\app.bin ``` ## 4. 构建和生产烧录 在 Windows SDK 构建环境中执行: ```bat build_rel.bat <生产版本号> ``` 量产必须烧录该构建产生的完整 `*-fac.zip`,不能只烧 `customer_app`。完整工厂包需要同时 携带新 `partition.bin`、新 `customer_fs.bin`、11LG kernel/system 和新地址 APP。 量产前冻结并记录: - 源码 Git commit; - 工厂 ZIP 文件名、字节数和 SHA-256; - `customer_fs.bin` SHA-256; - `customer_app.bin` 字节数和 SHA-256; - `partition.bin` 字节数和 SHA-256; - 编译器、Aboot/arelease 和 FBFMake 版本; - 首台、末台抽检设备串口中的 OTA baseline 日志。 不要把 `EG800AKCN_91LC` 的 `cp.bin`、工厂包或射频/GNSS 制品复制给本项目。两个项目同为 ASR1605/CRANELRH,但量产包必须由 `EG800AKCN_11LG` 配置生成。 ## 5. 生产前测试 ### 5.1 静态和产物测试 1. 运行 `verify_ota_ready_baseline.py`,结果必须通过。 2. 解包工厂 ZIP,确认其中 `customer_fs.bin` 为 655360 字节且哈希一致。 3. 使用 Aboot 工具检查 `partition.bin`,确认 APP/FS 地址与本文件一致。 4. 确认 `customer_app.bin + 8192 <= 471040`。 5. 确认 FBF 的目标物理地址是 `0x002C2000`,不能是旧地址 `0x00322000`。 ### 5.2 全擦工厂烧录测试 至少准备 3 台板执行完整擦除和工厂烧录: 1. 下载器无越界、校验或 partition 错误。 2. USB 重新枚举,`AT`/`ATI` 正常。 3. 串口打印正确 layout、地址和 640 KiB 文件系统容量。 4. 14 个预置音频均可读取和播放。 5. IMEI、SIM、驻网、数据连接和 GNSS 正常。 6. MQTT 注册、命令收发、心跳和状态上报正常。 7. 唤醒、对话、打断、录音、播放、音量和休眠正常。 8. NFC、按键、LED、电池检测和低电关机正常。 9. 连续冷启动、软重启和断电启动各不少于 20 次。 10. 连续运行至少 8 小时,观察 watchdog、内存、文件系统和网络重连。 ### 5.3 文件系统压力测试 1. 启动后记录 `ql_fs_size('U')` 和 `ql_fs_free_size('U')`。 2. 写入一个与计划 FBF 等大的测试文件,关闭、重启并逐字节回读。 3. 删除测试文件,确认空间恢复。 4. 在写入 10%、50%、90% 时分别断电,重启后确认业务 APP 仍可运行并可清理残留文件。 5. 验证正常业务文件占用后,仍满足 `FBF size + 64 KiB` 空闲门槛。 ### 5.4 最小 FULLOTA2 冒烟测试 正式量产前至少做一次 A 到 B 的本地暂存升级。B 可以只修改版本号: 1. 将 B 版 `customer_app.fbf` 顺序写入 `U:/FotaFile.bin`; 2. 调用 `ql_fota_fixed_internal_flash_upgrade(1)`; 3. 调用 `ql_fota_set_package_path()`、`ql_fota_init()`、`ql_fota_image_write()`、 `ql_fota_image_flush()` 和 `ql_fota_image_verify()`; 4. 重启,确认 updater 安装 B,且 `U:/FotaFile.bin` 被正确处理; 5. 确认 NVM、设备身份、业务配置和预置音频未损坏; 6. 下载损坏、截断和旧地址 FBF,确认均不能设置有效升级标志。 ## 6. 后续 FULLOTA2 业务实现 后续 APP 版本建议按以下顺序加入,不再改变本基线分区: 1. 下载所有权收敛到单一 OTA task,其他任务只投递数据或事件。 2. 下载前停止对话、录音、播放和非必要 Flash 写入,保持 watchdog 正常喂狗。 3. 删除确认不再使用的旧暂存文件。 4. 检查 `free >= manifest.size + 65536`。 5. 从偏移 0 顺序写入 `U:/FotaFile.bin`;首版不要宣称支持断点续传。 6. 同时计算 SHA-256,并在下载完成后重新打开本地文件复核大小和 SHA-256。 7. 在设置升级标志前持久化 `PENDING_REBOOT` 状态。 8. 只有 `ql_fota_image_verify()` 成功后才重启。 9. 新 APP 启动后核对版本、release sequence 和 layout,再上报健康状态。 10. 失败时 deinit、删除残留文件、恢复业务,并上报明确错误码。 建议使用两个带 CRC 和递增 generation 的状态文件,例如 `U:/ota_state.a`、 `U:/ota_state.b`。这能抵抗状态文件写入中断,但不构成固件 A/B 回滚。 ## 7. 后端联调契约 现有 OTA 命令仅有 URL 和版本不足以可靠激活 FULLOTA2。建议后端增加: ```json { "product": "EG800AKCN_11LG", "layout_id": "eg800akcn_11lg-fs640-app2c2000", "version": "1.9.0", "release_seq": 10900, "min_release_seq": 10800, "url": "https://ota.example/releases/1.9.0/customer_app.fbf", "size": 212992, "sha256": "64-lowercase-hex", "signature": "optional-first-stage" } ``` 设备校验顺序:产品、layout、版本序号、URL host、大小、SHA-256,最后才允许设置升级标志。 设备状态建议至少包含: - `ACCEPTED`:命令和布局校验通过; - `DOWNLOADING`:带下载字节数或百分比; - `VERIFIED`:本地文件 SHA-256 和 FBF 校验通过; - `REBOOTING`:升级标志已设置; - `UPDATED`:目标 APP 已启动并通过健康检查; - `FAILED`:带稳定错误码和失败阶段。 联调测试矩阵必须覆盖:同版本、降级、错误 layout、大小不符、SHA 不符、404、超时、下载 中断、下载阶段断电、verify 前断电、重启后无法确认,以及后台重复下发同一命令。 如果暂时没有动态 OTA 后端,也可以用“不可变 FBF 对象 + 签名 current.json”实现;但灰度、 设备级进度和失败统计需要后台服务。 ## 8. 发布和回退策略 - 新生产设备只允许使用 fs640 工厂包和 `0x002C2000` APP/FBF。 - 禁止向 fs640 设备发布旧地址 `0x00322000` 的 FBF。 - 如生产前验证失败,可回退源码分支和工厂包重新全擦烧录;不得混烧新旧布局制品。 - 量产后若只是业务问题,可通过 APP OTA 修复;若分区或 kernel 基线有问题,需要返厂或有线 完整烧录,不能假定业务 APP OTA 能安全修改分区表。 - 当前为单槽。发布新版本必须先小批量灰度,并保持可用的有线恢复工厂包。