开始前的准备
支持 OTA 升级命令 :
# 在 Tina5.0 以前的版本,按照以下方法配置:
Make menuconfig ‑‑>
Allwinner ‑‑‑>
<*>ota‑burnboot
# 在 Tina5.0 中,配置路径有更改,按照以下配置:
Make menuconfig ‑‑>
Allwinner ‑‑‑>
System ‑‑‑>
<*>ota‑burnboot
#在 Linux‑5.15 内核中, nand 驱动没有 ioctl,在没有 ioctl 的情况下,目前 nand 通过/dev/ rawnand_cdev 节点更新 boot0、 uboot
相关文件路径:
package/allwinner/swupdate
target/allwinner/r818-evb1/defconfig_ota
package/busybox-init-base-files/busybox-init-base-files/etc/init.d/load_script.conf
+ swupdate_autorun
一、准备升级
device/config/chips/r818/configs/evb1/linux/sys_partition.fex
# 修改分区表 并增大分局的size
# 在分区表中,将原有的 boot 分区和 rootfs 分区,分区名改为 bootA 和 rootfsA。
# 将这两个分区配置拷贝一份,即新增两个分区,并把名字改为 bootB 和 rootfsB。
[partition]
name = bootA
size = 30720
downloadfile = "boot.fex"
user_type = 0x8000
[partition]
name = rootfsA
size = 4194304
downloadfile = "rootfs.fex"
user_type = 0x8000
[partition]
name = bootB
size = 30720
downloadfile = "boot.fex"
user_type = 0x8000
[partition]
name = rootfsB
size = 4194304
downloadfile = "rootfs.fex"
user_type = 0x8000
1.准备 sw‑description
target/allwinner/generic/swupdate/sw-description-ab
software =
{
version = "1.0.0";
description = "Firmware update for Tina Project";
stable = {
/* now in systemA, we need to upgrade systemB(bootB, rootfsB) */
now_A_next_B = {
/* 这部分是描述,当前处于A系统,需要更新B系统,该执行的动作。执行完后下次启动为B系统 */
images: (/* 处理各个image */
{
filename = "kernel";
device = "/dev/by-name/bootB";
/* 当使用ubinand方案时,需要将上面的device = "/dev/by-name/bootB";注释掉,修改为下面的语句 */
/*volume = "bootB";*/
/* 流式升级,即从网络升级时边下载边写入, 而不是先完整下载到本地再写入,避免占用额外的RAM或ROM */
installed-directly = true;
},
{
filename = "rootfs";
device = "/dev/by-name/rootfsB";
/* 当使用ubinand方案时,需要将上面的device = "/dev/by-name/rootfsB";注释掉,修改为当前语句 */
/*volume = "rootfsB";*/
installed-directly = true;
},
{
filename = "uboot";
/* type为awuboot,则swupdate会调用对应的handler做处理 */
type = "awuboot";
},
{
filename = "boot0";
/* type为awuboot,则swupdate会调用对应的handler做处理 */
type = "awboot0";
}
);
bootenv: ( /* 处理bootenv,会修改uboot的env分区 */
{
/* 设置env:swu_mode=upgrade_kernel, 这是为了记录OTA进度, 对于AB系统来说,此时已经升级完成,置空 */
name = "swu_mode";
value = "";
},
{
/* 设置env:boot_partition=bootB, 这是为了切换系统,下次uboot就会启动B系统(kernel位于bootB分区) */
name = "boot_partition";
value = "bootB";
},
{
/* 设置env:root_partition=rootfsB, 这是为了切换系统,下次uboot就会通过cmdline指示挂载B系统的rootfs */
name = "root_partition";
value = "rootfsB";
},
{
/* 兼容另外的切换方式,可以先不管 */
name = "systemAB_next";
value = "B";
},
{
/* 设置env:swu_next=reboot, 这是为了跟外部脚本配合,指示外部脚本做reboot动作 */
name = "swu_next";
value = "reboot";
},
{
/* 表示这个OTA包的版本号, OTA时会写入env分区,用于在下次OTA时读出作为-N参数的值 */
name = "swu_version";
value = "1.0.0";
}
);
};
/* now in systemB, we need to upgrade systemA(bootA, rootfsA) */
now_B_next_A = {
images: (
{
filename = "kernel";
device = "/dev/by-name/bootA";
installed-directly = true;
},
{
filename = "rootfs";
device = "/dev/by-name/rootfsA";
installed-directly = true;
},
{
filename = "uboot";
type = "awuboot";
},
{
filename = "boot0";
type = "awboot0";
}
);
bootenv: (
{
name = "swu_mode";
value = "";
},
{
name = "boot_partition";
value = "bootA";
},
{
name = "root_partition";
value = "rootfsA";
},
{
name = "systemAB_next";
value = "A";
},
{
name = "swu_next";
value = "reboot";
},
{
name = "swu_version";
value = "1.0.0";
}
);
};
};
/* 当没有匹配上面的tag,进入对应的处理流程时,则运行到此处。我们默认清除掉一些状态 */
/* when not call with -e xxx,xxx just clean */
bootenv: (
{
name = "swu_param";
value = "";
},
{
name = "swu_software";
value = "";
},
{
name = "swu_mode";
value = "";
},
{
name = "swu_version";
value = "";
}
);
}
2.准备 sw‑subimgs.cfg
target/allwinner/generic/swupdate/sw-subimgs-ab.cfg
swota_file_list=(
#取得sw‑description‑ab,重命名成sw‑description,放到OTA包中。
#注意第一行必须为sw‑description
target/allwinner/generic/swupdate/sw-description-ab:sw-description
#取得uboot.img,重命名为uboot,放到OTA包中。以下雷同
#uboot.img和boot0.img是执行swupdate_pack_swu时自动拷贝得到的,需配置sys_config.fex中的storage_type
# 注意:
out/${TARGET_BOARD}/uboot.img:uboot
#注: boot0没有修改的话,以下这行可去除,其他雷同,可按需升级
out/${TARGET_BOARD}/boot0.img:boot0
out/${TARGET_BOARD}/boot.img:kernel
out/${TARGET_BOARD}/rootfs.img:rootfs
)
# build环境下的上一级目录的env.cfg和sys_partition.fex按照以上两步一起修改
# 修改路径:target/allwinner/generic/swupdate/sw-subimgs-ab.cfg
# 文件系统格式修改为:squashfs,该过程以实际为准
# ${......}/rootfs.squashfs:rootfs
3.修改env_xx.cfg文件
device/config/chips/r818/configs/evb1/linux/env-4.9.cfg
bootdelay=0
bootcmd=run setargs_nand boot_normal
earlyprintk=sunxi-uart,0x05000000
initcall_debug=0
console=ttyS0,115200
nand_root=/dev/nandd
mmc_root=/dev/mmcblk0p7
- boot_partition=boot
- root_partition=rootfs
+ boot_partition=bootA
+ root_partition=rootfsA
init=/sbin/init
rdinit=/rdinit
loglevel=8
cma=32M
...
4.判断当前系统(AB)
# 用fw_printenv查看当前是那个系统
root@TinaLinux:/# fw_printenv
bootdelay=0
bootcmd=run setargs_nand boot_normal
earlyprintk=sunxi-uart,0x05000000
initcall_debug=0
console=ttyS0,115200
nand_root=/dev/nandd
mmc_root=/dev/mmcblk0p7
boot_partition=bootA #当前为A系统
root_partition=rootfsA
init=/sbin/init
rdinit=/rdinit
loglevel=8
cma=32M
...
5.编译 OTA 包所需的子镜像
# 正常编译
make -j<N>
# 编译uboot,如果需要升级boot0/uboot, 则必须要执行该步骤,否则可以忽略。
muboot
mboot0
# 编译出的 boot0/uboot 还不能直接用于 OTA,请继续编译和打包固件. -s为安全方案
pack/pack -s
# 打包生成OTA包,因为我们使用的是 sw‑subimgs‑ab.cfg 或 sw‑subimgs‑ab‑ubi.cfg,所以调用时带参数‑ab 或 ‑ab‑ubi.
swupdate_pack_swu ‑ab 或 swupdate_pack_swu ‑ab‑ubi
# 最终会生成.swu的升级包 同时会生成一个MD5的校验文件
在编译升级包时,可能会报下图的错误:解决也很简单,它已经提示了!需要修改sys_config中的存储类型。
本例中使用storage_type = 1 image-20240618081929349.png image.png6.执行OTA
U盘挂载:在设备树中找到usbc0看看引脚是否正确 并切换为devices模式 并且拉高
(menuconfig 中的配置查看Linux_usb_开发指南)
在开发板中看看otg_role是什么模式 ,路径:/sys/devices/platform/soc/usb
如果是usb_device模式, 切换为usb_host模式
cat usb_host
二、开始升级
一个是使用原生的升级程序,另一个是使用辅助的升级脚本。这里建议使用辅助脚本来升级。因为原生的升级程序不会在自启动的时候帮我们准备好swupdate 所需的‑e 参数 。
1.设置版本号:
# 则在 SDK 中,需在 env‑x.x.cfg 中添加一行:
swu_version=1.0.0
# 在 sw-description-ab 中,设置 OTA 包版本号。
# target/allwinner/generic/swupdate/sw-description-ab
# 更新设备端版本号
software =
{
#表示这个OTA包的版本号,给swupdate读取检查的。原生规定的。
version = "1.0.0";
...
bootenv: (
...
{
#表示这个OTA包的版本号, OTA时会写入env分区,用于在下次OTA时读出作为‑N参数的值。 Tina自定义的。
name = "swu_version";
value = "1.0.0";
}
...
);
...
}
总结版本号:需要同步修改以上3个位置的version,及env‑x.x.cfg 和 sw-description的3个位置。其中在bootenv内的版本号,建议AB两版都修改。
2.USB升级:
监听USB设备挂载,并扫描是否有可用的升级包,如有并自动升级。
1.创建udev
规则文件, 这里名为:99-usb-listen.rules
- 本例位置:/lib/udev/rules.d/99-usb-listen.rules
# 规则比较简单,按照自己的需求自行完善。比如规则改成KERNEL=="sd[a-z]"
ACTION=="add", KERNEL=="sd[a-z]", SUBSYSTEMS=="usb", RUN+="/usr/sbin/update_byusb.sh mount %k"
ACTION=="remove", KERNEL=="sd[a-z]", SUBSYSTEMS=="usb", RUN+="/usr/sbin/update_byusb.sh unmount %k"
- 或者在mdev.conf中添加:
package/busybox-init-base-files/files/etc/mdev.conf
sd[a-z]([0-9]*) 0:0 0600 */usr/bin/hotplug.sh
mmcblk([0-9]+) 0:0 0600 */usr/bin/hotplug.sh
mmcblk([0-9]+)p1 0:0 0600 */usr/bin/hotplug.sh
- 重新加载:udev
# 一般都不需要自己加载的,仅做提示
udevadm control --reload-rules
udevadm trigger
2.对应创建被调用的脚本文件:update_byusb.sh
,位置对应即可。
- U盘升级辅助脚本:
#!/bin/sh
######################################################################
#
# 功能介绍: 检查U盘状态变化,并根据U盘内的swu文件进行升级
#
# 脚本使用方法:
# 1. 将update_byusb.sh脚本拷贝到/home/linux/r818_tina/package/autoshll-files目录下
# 2. 将U盘插入U盘槽,U盘内放置swu文件,U盘状态变化后,脚本会自动进行升级
# 3. 匹配U盘中的版本文件,本例中扫描文件名包含boot_version的文件中读取版本号并对比当前系统的版本号
# 4. boot_version中的版本号及为本次升级的目标版本,必须和ota升级包版本一致
#
#######################################################################
action=$1
device=$2
# U盘挂载点
mount_point="/mnt/exUDISK/"
# 版本文件名
version_file_name="boot_version"
logger() {
echo "[AutoSHLL] $1" > /dev/console 2>&1
}
split_version() {
echo $1 | tr '.' ' '
}
equalize_version_length() {
v1=$1
v2=$2
while [ $(echo $v1 | tr -cd '.' | wc -c) -lt $(echo $v2 | tr -cd '.' | wc -c) ]; do
v1="$v1.0"
done
while [ $(echo $v2 | tr -cd '.' | wc -c) -lt $(echo $v1 | tr -cd '.' | wc -c) ]; do
v2="$v2.0"
done
echo "$v1 $v2"
}
compare_versions() {
v1=$1
v2=$2
while [ -n "$v1" ] && [ -n "$v2" ]; do
v1_part=$(echo $v1 | awk '{print $1}')
v2_part=$(echo $v2 | awk '{print $1}')
if [ "$v1_part" -gt "$v2_part" ]; then
echo "greater"
return
elif [ "$v1_part" -lt "$v2_part" ]; then
echo "less"
return
fi
v1=$(echo $v1 | awk '{$1=""; print $0}' | sed 's/^ *//')
v2=$(echo $v2 | awk '{$1=""; print $0}' | sed 's/^ *//')
done
if [ -n "$v1" ]; then
echo "greater"
elif [ -n "$v2" ]; then
echo "less"
else
echo "equal"
fi
}
logger "检查到U盘状态变化......"
if [ "$#" -ne 2 ]; then
logger "Usage: $0 [mount|unmount] device_name"
exit 1
fi
if [ $action = "mount" ]; then
logger "Mounting $device..."
if [ ! -d "$mount_point" ]; then
logger "Creating mount point $mount_point..."
mkdir -p $mount_point
else
logger "Mount point $mount_point already exists."
fi
logger "Mounted at $mount_point"
counter=0
while [ $counter -lt 5 ]; do
update_file=$(find $mount_point -type f -name "*.swu" | head -n 1)
logger "Found update file: $update_file"
if [ -n "$update_file" ]; then
break
fi
counter=$((counter + 1))
sleep 3
done
if [ -z "$update_file" ]; then
logger "No update file found in $mount_point"
exit 1
fi
version_file=$(find $mount_point -type f -name "*$version_file_name*" | head -n 1)
if [ -z "$version_file" ]; then
logger "No version file found in $mount_point"
exit 1
fi
stored_version=$(cat "$version_file")
swu_version=$(fw_printenv -n swu_version 2> /dev/null)
set -- $(equalize_version_length "$swu_version" "$stored_version")
version1=$1
version2=$2
v1_parts=$(split_version "$version1")
v2_parts=$(split_version "$version2")
result=$(compare_versions "$v1_parts" "$v2_parts")
if [ "$result" = "less" ]; then
echo " $version1 < $version2"
logger "Prepare to upgrade....."
else
echo " $version1 >= $version2"
exit 1
fi
boot_partition=$(fw_printenv -n boot_partition 2> /dev/null)
logger "The current system is: $boot_partition"
if [ -n "$boot_partition" ]; then
if [ "$boot_partition" = "bootA" ]; then
logger "run: swupdate_cmd A2B"
swupdate_cmd.sh -i "$update_file" -e stable,now_A_next_B
elif [ "$boot_partition" = "bootB" ]; then
logger "run: swupdate_cmd B2A"
swupdate_cmd.sh -i "$update_file" -e stable,now_B_next_A
fi
fi
elif [ $action = "unmount" ]; then
logger "Unmounting $device..."
umount $mount_point
logger "Unmounted $device"
else
logger "Unknown action: $action"
exit 1
fi
- 全志提供的辅助脚本swupdate_cmd.sh,这里新增了-R 的校验
#!/bin/sh
#Copyright (c) 2018-2020 Allwinner Technology Co. Ltd.
logger() {
echo "$1" > /dev/console 2>&1
}
# ============================================================================
# GLOBAL FUNCTIONS
# ============================================================================
swupdate_cmd() {
while true; do
swu_param=$(fw_printenv -n swu_param 2> /dev/null)
swu_software=$(fw_printenv -n swu_software 2> /dev/null)
swu_mode=$(fw_printenv -n swu_mode 2> /dev/null)
swu_version=$(fw_printenv -n swu_version 2> /dev/null)
echo "swu_param: ##$swu_param##"
echo "swu_software: ##$swu_software##"
echo "swu_mode: ##$swu_mode##"
check_version_para=""
check_version_para_r=""
[ x"$swu_version" != x"" ] && {
echo "now version is $swu_version"
check_version_para="-N $swu_version"
check_version_para_r="-R $swu_version"
}
[ "$swu_mode" = "" ] && {
echo "no swupdate_cmd to run, wait for next swupdate"
return
}
echo "###now do swupdate###"
echo "###log in $swupdate_log_file###"
logger "## swupdate -v $check_version_para $check_version_para_r $swu_param -e $swu_software,$swu_mode ##"
swupdate -v $check_version_para $check_version_para_r $swu_param -e "$swu_software,$swu_mode" >> "$swupdate_log_file" 2>&1
swu_next=$(fw_printenv -n swu_next 2> /dev/null)
echo "swu_next: ##$swu_next##"
if [ x"$swu_next" = "xreboot" ]; then
fw_setenv swu_next
reboot -f
fi
sleep 1
done
}
# ============================================================================
# GLOBAL VARIABLES
# ============================================================================
swupdate_log_file=/mnt/UDISK/swupdate.log
# ============================================================================
# MAIN
# ============================================================================
mkdir -p /var/lock
[ $# -ne 0 ] && {
logger "config new swupdate"
rm -f $swupdate_log_file
swu_input=$*
echo "swu_input: ##$swu_input##"
swu_param=$(echo " $swu_input" | sed -E 's/ -e +[^ ]*//')
# echo "swu_param: ##$swu_param##"
echo "swu_param $swu_param" > /tmp/swupdate_param_file
swu_param_e=$(echo " $swu_input" | awk -F ' -e ' '{print $2}')
swu_param_e=$(echo "$swu_param_e" | awk -F ' ' '{print $1}')
swu_software=$(echo "$swu_param_e" | awk -F ',' '{print $1}')
# echo "swu_software: ##$swu_software##"
echo "swu_software $swu_software" >> /tmp/swupdate_param_file
swu_mode=$(echo "$swu_param_e" | awk -F ',' '{print $2}')
# echo "swu_mode: ##$swu_mode##"
echo "swu_mode $swu_mode" >> /tmp/swupdate_param_file
fw_setenv -s /tmp/swupdate_param_file
sync
logger "## set swupdate_param done ##"
}
swupdate_cmd
3.网路升级:
# 需依赖外部程序,提供自动联网支持。 OTA 本身不处理联网。
swupdate_cmd.sh -d -u"$upgrade_link" -e stable,now_A_next_B
swupdate_cmd.sh ‑d ‑uhttp://192.168.35.112/tina‑cowbell‑perf1.swu ‑e stable,now_A_next_B
# 也可以单独下载升级包在调用
swupdate_cmd.sh -i "$update_file" -e stable,now_A_next_B
注意:启动后会自动联网,连网后等待 OTA 后台脚本尝试更新。中途掉电重启后,正常会在启动后几十秒内,成功联网并开始继续更新。
4.恢复出厂设置:
格式化rootfs_data 或UDISK 分区,默认OTA不会清空这两个分区 。
# package/base-files/files/lib/preinit/79_format_partition
# 79_format_partition 实现了clean_parts函数 , 使用方法如下
fw_setenv parts_clean rootfs_data:UDISK
reboot -f
三、实用命令整理:
1:查看版本
# 开始前要确定当前系统是A还是B,执行下面命令
fw_printenv
2:升级命令
# 原生的升级程序: 执行之后提示升级成功后,手动重启即可。
# 当前处于A系统:
# /mnt/UDISK/<board>.swu 这里是你升级包的实际挂载路径
swupdate ‑i /mnt/UDISK/<board>.swu ‑e stable,now_A_next_B
# 当前处于B系统:
swupdate ‑i /mnt/UDISK/<board>.swu ‑e stable,now_B_next_A
# 辅助脚本,此脚本升级之后会自动重启并在升级包目录生成升级日志
# 当前处于A系统:
swupdate_cmd.sh -i /mnt/UDISK/<board>.swu -e stable,now_A_next_B
# 当前处于B系统:
swupdate_cmd.sh -i /mnt/UDISK/<board>.swu -e stable,now_B_next_A
3:手动切换系统
# 切换A系统
fw_setenv boot_partition bootA
fw_setenv root_partition rootfsA
reboot -f
# 切换B系统
fw_setenv boot_partition bootB
fw_setenv root_partition rootfsB
reboot -f
# 切换可以混合:bootB、文件系统A
fw_setenv boot_partition bootB
fw_setenv root_partition rootfsA
reboot -f
提醒:
相关文件需要安装到镜像中,这部分自行处理,此处仅供参考:
-
99-usb-listen.rules
到/lib/udev/rules.d目录下 -
update_byusb.sh
、response_update_info.sh
、restore_factory.sh
到/usr/sbin目录下# makefile 实例 ... define Package/$(PKG_NAME)/install ... $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_DIR) $(1)/lib/udev/rules.d ... $(INSTALL_BIN) ./update_byusb.sh $(1)/usr/sbin/update_byusb.sh $(INSTALL_BIN) ./response_update_info.sh $(1)/usr/sbin/response_update_info.sh $(INSTALL_BIN) ./restore_factory.sh $(1)/usr/sbin/restore_factory.sh $(INSTALL_DATA) ./99-usb-listen.rules $(1)/usr/sbin/rules.d/99-usb-listen.rules endef ...
网友评论