设计说明
OTA 相关文件说明
OTA 的源码目录位于 Luban-Lite 中:
packages/artinchip/ota/
├── absystem.c # 文件系统挂载源文件
├── absystem.h
├── burn.c # FLASH 固化接口源文件
├── burn.h
├── ota.c # OTA 接口源文件
├── ota.h
└── test_ota.c # 测试 TF 卡 OTA 源文件
1
2
3
4
5
6
7
2
3
4
5
6
7
OTA 升级过程中的使用到的相关文件位于各个板级目录中:
target/xx-chip/xx-board/pack
├── bootloader.bin
├── ddr_init.json
├── env.txt # 用于环境变量存放
├── image_cfg.json # 用作系统分区
└── ota-subimgs.cfg # 用于打包生成 OTA 升级包
1
2
3
4
5
2
3
4
5
A/B 系统方案分区信息
注意
1.ENV 分区,用来保存 env 环境变量,增加了一个备用 ENV 分区,确保在一份数据破坏后,另外一份数据还能继续使用。
1.ENV 分区,用来保存 env 环境变量,增加了一个备用 ENV 分区,确保在一份数据破坏后,另外一份数据还能继续使用。
2.OS, RODATA, DATA 分区各有一个备份分区,它们一起称为 A/B 系统。
3.用户可根据实际 OTA 需求剪裁分区,例如只需升级 OS,则可以删除 RODATA 与 DATA 的备份分区。
剪裁分区
上述提及的剪裁分区,可通过修改image_cfg.json
文件进行分区调整。
"spi-nand": { // Device, The name should be the same with string in image:info:media:type
"size": "128m", // Size of SPI NAND
"partitions": {
"spl": { "size": "1m" },
"env": { "size": "256k" },
"env_r": { "size": "256k" },
"os": { "size": "4m" },
"os_r": { "size": "4m" },
"rodata": { "size": "12m" },
"rodata_r": { "size": "12m" },
"data": {
"size": "40m",
"nftl": { // Volume in NFTL device
"data": { "size": "-" },
},
},
"data_r": {
"size": "40m",
"nftl": { // Volume in NFTL device
"data": { "size": "-" },
},
},
},
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
注意
1.修改分区的时候要注意,各分区的大小总和不能超过总的 FLASH 大小。
1.修改分区的时候要注意,各分区的大小总和不能超过总的 FLASH 大小。
2.上述文件中带 _r
的分区为备份分区,即 B 分区,若不需要升级,可将对应 _r
分区删除。
3.同时 ota-subimgs.cfg 中对应文件不应该被打包,需整行删除。
剪裁 OTA 分区对照表
名称 | image_cfg.json 分区名称 | ota-subimgs.cfg 打包文件名称 |
---|---|---|
系统 | os_r | [file] 中dxx_os.itb:os; 项 |
只读文件系统 | rodata_r | [file] 中rodata.fatfs:rodata; 项 |
系统 | os_r | [file] 中dxx_os.itb:os; 项 |
A/B 系统升级过程
注意
1.升级前,A 系统为启动系统,B 系统为备用系统;
1.升级前,A 系统为启动系统,B 系统为备用系统;
2.启动 OTA 升级,给 B 系统升级程序;
3.升级完成后重启,从 B 系统启动,A 系统为备用系统。
环境变量指导 A/B 系统升级和启动
OTA 升级相关环境变量
相关文件路径如下:
target/xx-chip/xx-board/pack/env.txt
1
查看环境变量文件:
osAB_next=A
osAB_now=A
rodataAB_next=A
rodataAB_now=A
dataAB_next=A
dataAB_now=A
upgrade_available=0
bootlimit=5
bootcount=0
rodata_partname=blk_rodata
rodata_partname_r=blk_rodata_r
data_partname=blk_data
data_partname_r=blk_data_r
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
环境变量使用说明
环境变量名称 | 定义 | 注释说明 |
---|---|---|
osAB_now | 当前启动分区 | osAB_now 为“A”表示 OS 本次从 A 启动, 为“B”表示 OS 本次从 B 启动,无需手动修改 |
osAB_next | 下次启动分区 | osAB_next 为“A”表示 OS 下次从 A 启动,为“B”表示 OS 下次从 B 启动,可以手动修改 |
rodataAB_now | 当前挂载的只读分区 | rodataAB_now 为“A”表示 rodata 本次从 A 挂载, 为“B”表示 rodata 本次从 B 挂载,无需手动修改 |
rodataAB_next | 下次挂载的只读分区 | rodataAB_next 为“A”表示 rodata下次从 A 挂载,为“B”表示 rodata 下次从 B 挂载,可以手动修改 |
dataAB_now | 当前挂载的读写分区 | dataAB_now 为“A”表示 data 本次从 A 挂载, 为“B”表示 data 本次从 B 挂载,无需手动修改 |
dataAB_next | 下次挂载的读写分区 | dataAB_next 为“A”表示 data 下次从 A 挂载,为“B”表示 data 下次从 B 挂载,可以手动修改 |
upgrade_available | OTA升级完成 | OTA升级完成,指导 boot 程序更新启动分区和版本回退 |
bootcount | 失败启动次数 | 启动分区启动os程序失败的次数 |
bootlimit | 失败启动次数限制 | 启动分区启动失败次数超过bootlimit会触发版本回退 |
rodata_partname | rodata挂载设备名称 | rodata实际挂载的设备名称,如果系统设备名称改变,此处应该同步修改 |
rodata_partname_r | 备份rodata挂载设备名称 | rodata_r实际挂载的设备名称,如果系统设备名称改变,此处应该同步修改 |
data_partname | data挂载设备名称 | rodata实际挂载的设备名称,如果系统设备名称改变,此处应该同步修改 |
data_partname_r | 备份data挂载设备名称 | rodata_r实际挂载的设备名称,如果系统设备名称改变,此处应该同步修改 |
环境变量在 OTA 升级过程中的作用
注解
rodata 和 data 分区的升级原理与 OS 相同。
rodata 和 data 分区的升级原理与 OS 相同。
OTA 升级包制作过程
OTA升级包配置文件:ota-subimgs.cfg
查看配置文件:
[image]
size = "";
version = "1.0.0";
[file]
ota_info.bin:file;
d21x_os.itb:os;
rodata.fatfs:rodata;
data.fatfs:blk_data;
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
ota-subimgs.cfg 配置文件说明
环境变量名称 | 定义 | 注释说明 |
---|---|---|
size | 整个ota.cpio文件的大小 | size 为 py 脚本自动计算,在 C 文件中有解析此字段,供用户用做下载进度等使用。 |
version | 新更新的系统版本号 | version 为 py 脚本自动填充,从 image_cfg.json 中解析,用户应修改 image_cfg.json 中的版本号。 |
ota_info.bin | ota-subimgs.cfg的二进制文件 | py 脚本会将本文件编译为二进制流文件,供 C 文件中解析使用。file 属性可以忽略。 |
d21x_os.itb | OS 的实际比特流文件 | 此文件编译会自动生成,此处存在代表将会被打包进 cpio 包。 os 为即将升级到的设备。 |
rodata.fatfs | 只读分区的实际比特流文件 | 此文件编译会自动生成,此处存在代表将会被打包进 cpio 包。 rodata 为即将升级到的设备。 |
data.fatfs | 读写分区的实际比特流文件 | 此文件编译会自动生成,此处存在代表将会被打包进 cpio 包。 blk_data 为即将升级到的设备。 |
注意
1.FLASH 为 NAND 时,只读文件系统与读写文件系统升级的设备不同,读写文件系统依赖 NFTL 支持,烧写 FLASH 需使用块设备接口,而只读文件系统烧写 FLASH使用 MTD 设备接口即可。
1.FLASH 为 NAND 时,只读文件系统与读写文件系统升级的设备不同,读写文件系统依赖 NFTL 支持,烧写 FLASH 需使用块设备接口,而只读文件系统烧写 FLASH使用 MTD 设备接口即可。
2.FLASH 为 NOR 时,由于空间限制,目前读写文件系统不使用 OTA 进行升级。
3.FLASH 为 eMMC 时,主分区应为 mmc0p*
, 备份分区在 image_cfg.json
分配时应该在主分区下面,在 OTA 代码中,默认将 mmc0p*+1
作为备份分区。
OTA 升级包生成工具: mkcpio.py
相关文件路径如下:
tools/scripts/mkcpio.py
1
执行编译命令,会自动执行 mkcpio.py 命令生成 ota.cpio。mkcpio.py 具体调用了cpio, cpio.exe命令进行打包。
OTA 包数据组成格式
其中头部信息如下:
struct filehdr {
unsigned int format;
unsigned int size;
unsigned int size_align;
unsigned int begin_offset;
unsigned int cpio_header_len;
unsigned int namesize;
unsigned int filename_size;
unsigned int filename_align;
unsigned int burn_len;
unsigned int chksum;
unsigned int sum;
char filename[MAX_IMAGE_FNAME];
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
OTA 分区说明
NAND 分区说明
设备名称 | 定义 | 注释说明 |
---|---|---|
os | 系统主分区 MTD 设备 | 用于 A 系统烧录 OS |
os_r | 系统备份分区 MTD 设备 | 用于 B 系统烧录 OS |
blk_rodata | 只读文件系统主分区块设备 | 用于 A 系统挂载只读文件系统 |
blk_rodata_r | 只读文件系统备份分区块设备 | 用于 B 系统挂载只读文件系统 |
blk_data | 读写文件系统主分区块设备 | 用于 A 系统挂载与烧录读写文件系统 |
blk_data_r | 读写文件系统备份分区块设备 | 用于 B 系统挂载与烧录读写文件系统 |
rodata | 只读文件系统主分区 MTD 设备 | 用于 A 系统烧录只读文件系统 |
rodata_r | 只读文件系统备份分区 MTD 设备 | 用于 B 系统烧录只读文件系统 |
NOR 分区说明
设备名称 | 定义 | 注释说明 |
---|---|---|
os | 系统主分区 MTD 设备 | 用于 A 系统烧录 OS |
os_r | 系统备份分区 MTD 设备 | 用于 B 系统烧录 OS |
blk_rodata | 只读文件系统主分区块设备 | 用于 A 系统挂载只读文件系统 |
blk_rodata_r | 只读文件系统备份分区块设备 | 用于 B 系统挂载只读文件系统 |
rodata | 只读文件系统主分区 MTD 设备 | 用于 A 系统烧录只读文件系统 |
rodata_r | 只读文件系统备份分区 MTD 设备 | 用于 B 系统烧录只读文件系统 |
eMMC 分区说明
设备名称 | 定义 | 注释说明 |
---|---|---|
mmc0p3 | 系统主分区块设备 | 用于 A 系统烧录 OS |
mmc0p4 | 系统备份分区块设备 | 用于 B 系统烧录 OS |
mmc0p5 | 只读文件系统主分区块设备 | 用于 A 系统挂载与烧录只读文件系统 |
mmc0p6 | 只读文件系统备份分区块设备 | 用于 B 系统挂载与烧录只读文件系统 |
mmc0p7 | 读写文件系统主分区块设备 | 用于 A 系统挂载与烧录读写文件系统 |
mmc0p8 | 读写文件系统备份分区块设备 | 用于 B 系统挂载与烧录读写文件系统 |
网络 OTA 流程
OS 阶段 app OTA 执行流程
http_ota //packages/third-party/ota_downloader/src/http_ota.c
|-> http_ota_fw_download(argv[1]);
|-> webclient_session_create(GET_HEADER_BUFSZ);
|-> webclient_shard_head_function(session, uri, &file_size);
|-> webclient_register_shard_position_function(session, http_ota_shard_download_swu_handle);
|-> webclient_shard_position_function(session, uri, file_offset, file_size, HTTP_OTA_BUFF_LEN);
|-> http_ota_shard_download_swu_handle //采用分片方式下载数据
|-> ota_buf_push(&shdr, buffer, length); //将下载的数据放到缓存区,供解析cpio头部信息
|-> find_cpio_data(&fhdr, shdr.buf, shdr.buflen); //解析cpio头部信息
|-> parse_cpio_file_info(&bhdr, &fhdr, &finfo); //解析cpio文件大小和版本号,供客户自定义使用,并且存储烧录的分区设备名字
|-> swu_buf_pop(&bhdr, &shdr, &fhdr); //将cpio头部信息后面的数据放到另外的缓存区,供升级使用
|-> ota_buf_push(&bhdr, buffer, length); //将下载的数据放到缓存区,供升级使用
|-> find_device(partname) //获取分区信息
|-> aic_ota_erase_part(dl_part, 0, dl_part->len) //擦除分区
|-> download_buf_pop(&bhdr, &fhdr) //将缓存区里面的数据取出来,升级到FLASH上
|-> aic_ota_part_write //写入数据到FLASH上
|-> cpio_file_checksum((unsigned char *)bhdr->buf, burn_len); //升级完成以后,校验和信息
|-> aic_upgrade_end(); //升级结束,更新环境变量信息
|-> rt_hw_cpu_reset(); //重新启动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Boot 阶段新系统程序加载启动流程
do_nor_boot //application/baremetal/bootloader/cmd/nor_boot.c
|-> aic_ota_check(); //application/baremetal/bootloader/lib/absystem/absystem.c
|-> aic_ota_version_fallback(); // OTA 升级失败,版本回退到之前的版本
|-> aic_get_os_to_startup(target); //获取新系统信息
|-> find_boot_part(target); // 获取新系统 OS 分区结构体
|-> spl_load_simple_fit(&info, &entry_point); // 加载 os 分区程序到缓存上
1
2
3
4
5
6
2
3
4
5
6
OS 阶段 app 挂载新文件系统
absystem.c //packages/artinchip/ota/absystem.c
|-> INIT_ENV_EXPORT(aic_absystem_mount_fs_prio0);
|-> INIT_LATE_APP_EXPORT(aic_absystem_mount_fs_prio1);
1
2
3
2
3
OTA 编程指南
OTA 编程流程图¶
OTA 编程参考代码
//部分源码
int test_ota()
{
FILE *file;
int size;
int ret;
//update file ota.cpio is placed in the sdcard
file = fopen("/sdcard/ota.cpio", "rb");
if (file == NULL) {
printf("Failed to open the file.\n");
return -1;
}
//1.Buffer allocation required by OTA
ret = ota_init();
if (ret != RT_EOK) {
printf("ota initialization failed.");
goto __exit;
}
//2.Read BUFFER_SIZE each time and update it into flash
while (!feof(file)) {
size = fread(buffer, 1, BUFFER_SIZE, file);
if (size > 0) {
if(ota_shard_download_fun(buffer, size) < 0) {
printf ("ota download failed.");
goto __exit;
}
}
}
//3.Update the environment variables
ret = aic_upgrade_end();
if (ret) {
printf("Aic upgrade end");
}
//4. Reset the device, Start new firmware
extern void rt_hw_cpu_reset(void);
rt_hw_cpu_reset();
__exit:
fclose(file);
ota_deinit();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50