ajsb85/sprdflash-rs

GitHub: ajsb85/sprdflash-rs

纯 Rust 编写的跨平台 SPRD/UNISOC .pac 固件烧录工具,专为生产线多夹具并行高吞吐量产场景设计。

Stars: 3 | Forks: 0

# SPRD Flash Tool [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/ajsb85/sprdflash-rs/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Rust 2024](https://img.shields.io/badge/rust-2024_edition-orange.svg)](https://doc.rust-lang.org/edition-guide/rust-2024/index.html) [![MSRV 1.85](https://img.shields.io/badge/MSRV-1.85-blue.svg)](https://blog.rust-lang.org/) [![Platforms](https://img.shields.io/badge/platforms-Windows_%7C_Linux-informational.svg)](#build--test) [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://www.conventionalcommits.org) [![unsafe: forbidden in core](https://img.shields.io/badge/unsafe-forbidden_in_core-success.svg)](crates/sprdflash-core) 纯 Rust 编写的跨平台(Windows 11 + Linux/WSL)SPRD/UNISOC `.pac` 固件烧录工具 ——适用于 RDA8910 / UIS8910(Air724UG 及相关设备)。专为**生产线**构建: 快速、稳健且具备高度可观测性,足以在多个夹具上每天执行数千次烧录。 它是经过硬件验证的 Python 版 [`sprdflash`](https://github.com/ajsb85/sprdflash) 的移植版,原项目对完整的下载协议(PDL 第一阶段 → BSL/FDL2 → 分区 → 跨 SDK 格式)进行了逆向工程,并从厂商工具中逐字节还原。 本项目将该协议保留在 **sans-I/O 核心库** 中,从而保持确定性并完全可进行单元测试, 并在其上层叠加了快速、可恢复的传输层和工位编排器。 ## 为什么使用 Rust,以及速度的来源 瓶颈在于传输线,而不是 CPU。主要的优化手段(按效果从大到小排列)如下: 1. **将 `CHANGE_BAUD` 设置为 FDL 的最大值。** 参考烧录工具在 115200 波特率下 运行整个 6 MB 的传输(约 42 秒)。FDL2 支持更高的速率;将其提升至 921600–3 M 可实现 **8–25 倍的线路提速** —— 这是单项最大的优化。 2. **每个夹具分配一个工位。** 串行 I/O 是阻塞式且对延迟敏感的,因此 每个夹具都有自己独立的 OS 线程;吞吐量随夹具数量呈线性扩展。 3. **更大的 MIDST 数据块 + 在批量数据阶段使用 `DISABLE_TRANSCODE` (0x21)** —— 减少了往返次数,且没有 HDLC 转义扩展。 4. **仅对 PAC 进行一次 `mmap`**,通过 `Arc` 共享零拷贝的有效载荷切片给所有工位。 ## 稳健性(工业级) - **Sans-I/O 核心库** (`sprdflash-core`):声明了 `#![forbid(unsafe_code)]`,测试时无需 硬件 —— 协议是根据抓取的厂商数据流进行验证的。 - 分阶段的 **超时 + 有限次重试 + 自动设备恢复**(在 Windows 上 通过 `pnputil` 重新枚举;在 Linux/WSL 上通过 `usbip` 重新连接 / `uhubctl` 电源循环)。 - **烧录前进行 PAC CRC 校验** —— 绝不烧录损坏的镜像文件。 - 提供**针对单个设备的 JSON-lines 记录** 以供 MES 使用,每个工位配备 `tracing` 跨度, 以及 Prometheus 指标(良率、吞吐量、各阶段耗时)。 - **烧录后启动验证**(在 AT 端口执行 ATI/IMEI 校验),仅追加的审计日志。 - 提交了 `Cargo.lock`,在 release 构建中启用了 `panic = "abort"` + `overflow-checks = on`。 ## 工作区 | crate | 状态 | 角色 | |----------------------|--------|---------------------------------------------------------------| | `sprdflash-core` | ✅ 完成 | sans-I/O 协议:PAC 解析、PDL + BSL 组帧、校验和、计划 | | `sprdflash-cli` | ✅ `info`, `list-ports`, `flash` | `sprdflash` 可执行程序 | | `sprdflash-transport`| ✅ 完成 | `serialport` 传输、端口发现、信标窗口连接、恢复 | | `sprdflash-flash` | ✅ 完成 | 设备驱动:PDL→BSL→分区→格式化→重置,`CHANGE_BAUD` | | `sprdflash-line` | ✅ 完成 | 并行工位、启动验证 (ATI/IMEI)、JSON-lines 记录、指标 | 核心库已针对真实的 V4035 PAC 和参考 Python 实现进行了逐字节验证。完整的驱动程序已 **在真实的 Air724UG (RDA8910) 上完成了硬件验证**,包括同 SDK 的重新烧录以及跨 SDK 的 `--format` 切换(LuatOS V4035 ⇄ CSDK V302340),在软重启后设备即可直接启动,且 IMEI 和 NV 数据保持完好。 ### 实测速度(单设备,6 MB) | 工具 / 配置 | 耗时 | 备注 | |------------------------------|--------|---------------------------------------------| | Python `sprdflash` (528 B) | ~42 s | 参考 | | `sprdflash-rs` (2048 B) | ~33 s | **快 22%**;默认配置 | 该设备受限于**闪存写入速度**(~186 KiB/s):大于 ~2 KB 的 MIDST 数据块会导致 FDL2 接收缓冲区溢出,而且在 2 KB 时每帧的开销已经很小了 —— 因此单设备的耗时已 接近其下限。**流水线吞吐量的提升来自于并行运行每个夹具的一个工位**(即 `sprdflash-line` crate),而不是靠压榨单个设备的速度。 ## 构建与测试 ``` cargo test # 21 tests, all against captured ground truth cargo build --release # 单个设备,自动 mode-switch,启动进入新固件 sprdflash flash --enter-download firmware.pac # cross-SDK 更改:格式化 FS + 刷新 NV(保留 IMEI) sprdflash flash --enter-download --format firmware.pac sprdflash info firmware.pac sprdflash list-ports ``` 目标平台:`x86_64-pc-windows-msvc` 和 `x86_64-unknown-linux-gnu` (WSL Ubuntu)。 ## 通过 usbipd 从 WSL 进行烧录 在原生 Linux 主机上,下载小工具显示为普通的 `/dev/ttyACM0`,烧录过程与在 Windows 上完全相同。要想从 **WSL** 中访问连接在 Windows 上的设备,请使用 [usbipd-win](https://github.com/dorssel/usbipd-win) 进行转发: ``` usbipd bind --busid # once, elevated usbipd attach --wsl --busid # module -> /dev/ttyUSB*, download -> /dev/ttyACM0 ``` ``` # 在 WSL 中,以 root 身份(或将你的用户添加到 dialout group) sprdflash flash --port /dev/ttyACM0 --chunk 512 --format firmware.pac ``` **在使用 usbipd 时请使用 `--chunk 512`。** 默认的 2 KB MIDST 帧非常适合 直连环境,但 usbipd 背后通用的 `cdc_acm` 驱动程序在处理多 MB 传输末尾的 大帧数据时会停滞;使用 512 B 可以保证端到端的稳定传输(已验证:完整的 6 MB 烧录 + 格式化 + 启动,IMEI 完好无损)。原生的 Linux 生产线主机没有 usbipd 隧道,可以很好地运行默认的 2 KB 配置。 ## 运行流水线 每个 `--station` 都在其独立的线程上运行;它们并行执行烧录和启动验证, 为每个设备流式传输一条 JSON-lines 记录,并报告良率和吞吐量。各工位之间 相互独立,因此一个损坏的设备绝不会阻塞整条流水线。卡死的下载代理会被 自动恢复(在 Windows 上通过 `pnputil` 重新枚举;在 Linux 上则通过夹具的 电源硬重启),并限制重试次数。 ``` > sprdflash line firmware.pac \ --station fixture-1:COM12 --station fixture-2:COM22 --station fixture-3:COM32 \ --format --records /var/log/flash/units.jsonl ── results ── [PASS] fixture-1 41.0s LuatOS-Air_V4035_... IMEI 8634880509... [PASS] fixture-2 41.3s LuatOS-Air_V4035_... IMEI 8634880511... [PASS] fixture-3 40.8s LuatOS-Air_V4035_... IMEI 8634880514... 3/3 passed in 41.3s → ~261 good units/hour at this concurrency ``` 每个设备对应 MES / 审计日志中的一条 JSON 记录: ``` {"ts_ms":1700000000000,"station":"fixture-1","port":"COM12","product":"UIX8910_MODEM","result":"pass","attempts":1,"bytes":6071296,"flash_seconds":33.2,"total_seconds":41.0,"firmware":"LuatOS-Air_V4035_...","imei":"863488050987562","error":null} ``` 单个设备受限于闪存写入速度,耗时约 33 秒,因此吞吐量随夹具数量增加而提升 (每个约 110个/小时) → **在规模适中的工位集群中即可实现每天数千台的产量**。 ## 协议参考 完整的逆向工程说明请参阅 Python 项目的 README 和 `native.py`。以下是 编码在 `sprdflash-core` 中的关键事实: - **PDL** (`0525:a4a7`,第一阶段):使用 `ae` 组帧,头部和有效载荷是 **分离写入**的;加载 `HOST_FDL`/`PDL1`,然后移交给 BSL。 - **BSL**:使用 HDLC `0x7e` 组帧,`type|size` 为大端序,在 RDA8910/UIS8910 上使用 **Spreadtrum sum** 校验和(而非 CRC)。 - **跨 SDK 的 `--format`**:擦除 `FMT_FSSYS "SYSF"` + `FLASH 0`(根据 地址去重),写入带有更新后 **CRC-16-ARC** 的 **NV 模板**以及经过 **sum32** 校验的 12 字节 `START_DATA`,然后执行 `PREPACK`。IMEI/RF 校准数据存在于一个 独立的 `factorynv` 区域中,格式化过程绝不会触及该区域。
标签:Rust, SPRD/UNISOC, 串口通信, 可视化界面, 固件烧录, 嵌入式系统, 网络流量审计, 自定义请求头, 通知系统