美文网首页
OTA-AB 升级方案(R818)

OTA-AB 升级方案(R818)

作者: 狼少丷 | 来源:发表于2024-06-19 16:49 被阅读0次
    开始前的准备

    支持 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.png
    6.执行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.shresponse_update_info.shrestore_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
      ...
      

    相关文章

      网友评论

          本文标题:OTA-AB 升级方案(R818)

          本文链接:https://www.haomeiwen.com/subject/jgnqcjtx.html