杰理 SDK 文档中心
首页
首页
  • fw-Bootloader:JL 系列定制 Bootloader

    • Bootloader 架构与芯片适配
    • uboot 升级协议与流程
    • 上位机升级工具
    • 编译环境与快速开始
  • ac792n-ota-loader:AC792N 系列 OTA Loader

    • 工程结构与公共运行时框架
    • SD 卡与 USB 基础升级通道
    • 安全升级通道(SD/USB)
    • 用户自定义升级通道(UART/USB HID)
    • LVGL 图形化升级界面与模拟器
  • ac791n-ota-loader:AC791N 系列 OTA Loader

    • uboot 应用框架与 WiFi 示例
    • 升级通道变体(AP/STA/USB HID)
    • 网络与系统库依赖

升级通道变体(AP/STA/USB HID)

本文档介绍 Jieli(杰理)OTA 升级体系中三种典型传输通道变体:WiFi AP 模式、WiFi STA 模式与 USB HID 模式,说明各通道的协议、数据流、配置参数以及它们共享的 Flash 升级流水线。

Purpose and Scope

本页聚焦 ota-loader 项目中**升级通道(传输层)**的变体实现:

  • AP 模式:设备作为 WiFi 热点,手机 APP 直连设备后通过 TCP 私有协议(CTP / net_ota)传输固件;
  • STA 模式:设备连接路由器后,以 HTTP 客户端方式从服务器断点续传下载固件;
  • USB HID 模式:设备枚举为 USB HID 设备,上位机通过 HID 报告传输固件。

三者在传输层之上共用同一套 Flash 升级流水线(fs_v2_update.h 声明的 ufw_head_check / flash_update_process / 校验系列 API),因此本页同时说明这条共享流水线。

不属于本页范围(由同名工程的兄弟页面/目录承载):UART 串口升级通道(ac792n-ota-loader/uart_user_update)、SD 卡 OTA 通道(ac792n-ota-loader/sd_ota_update、sd_sec_ota_update)、经典蓝牙/BLE 升级状态机细节(update_main.c 中的 LMP_STA_UPDATA_ACCEPT 等仅作交叉引用)。如需了解这些通道,请参见相关页面。

Overview

在 Jieli 的 OTA 架构中,"升级"被拆成两层:

  1. 传输通道(Channel):负责把固件字节从"数据源"(APP、服务器、上位机)搬到设备内存,是本文的主题;
  2. 升级流水线(Pipeline):负责校验 .ufw 文件头、按分区写 Flash、校验与复位,由 fs_v2_update.h 声明的 API 实现,与具体通道无关。

通道变体存在的意义是适配不同使用场景:

场景通道谁发起连接数据源
出厂后首次配网 / 无路由器环境AP(热点)手机 APP → 设备 TCP Server手机 APP
设备已联网(STA)STA(HTTP 客户端)设备 → 升级服务器HTTP 服务器
产线 / 售后工具USB HID上位机 → USB 设备PC 工具

AP 与 STA 的通道代码集中在 net_update.c,通过编译宏 CONFIG_WL_AP_ENABLE 二选一。USB HID 通道与经典蓝牙通道共用 update_main.c 中的更新状态机(btupdata_sw 状态、ble_update_state 标志位),最终全部汇入同一 Flash 流水线。

Architecture

下图展示三种通道变体与共享升级流水线、Flash 存储之间的关系:

flowchart TD
    subgraph sg_Channels["传输通道层"]
        AP["AP 模式<br/>(CONFIG_WL_AP_ENABLE)"]
        STA["STA 模式<br/>(HTTP_UPDATE_ENABLE)"]
        HID["USB HID 通道"]
        BT["BT/BLE 通道<br/>(update_main.c)"]
    end

    subgraph sg_Transport["通道协议实现 (net_update.c)"]
        CTP["CTP 控制协议<br/>port 3333"]
        TCP["TCP 数据服务<br/>NET_UPDATE_PORT 3335"]
        HTTP["HTTP 客户端<br/>Range 断点续传 port 80"]
    end

    subgraph sg_Update["更新控制层"]
        UPD_MAIN["update_main.c 状态机<br/>btupdata_sw / ble_update_state"]
    end

    subgraph sg_Pipeline["共享升级流水线 (fs_v2_update)"]
        UFW["ufw_head_check<br/>头校验"]
        PROC["flash_update_process<br/>写 Flash"]
        VERIFY["flash_all_data_verify<br/>数据校验"]
    end

    APP["手机 APP / 上位机"] -->|"WiFi 直连"| AP
    AP -->|"TCP + CTP"| CTP
    AP -->|"固件数据"| TCP
    SERVER["HTTP 升级服务器"] -->|"HTTP GET Range"| STA
    STA --> HTTP
    HOST["PC 升级工具"] -->|"HID 报告"| HID
    HID --> UPD_MAIN
    BT["蓝牙手机"] --> UPD_MAIN

    CTP --> UPD_MAIN
    TCP --> UPD_MAIN
    HTTP --> UPD_MAIN
    UPD_MAIN --> UFW
    UFW --> PROC
    PROC --> VERIFY
    VERIFY --> FLASH[("Flash 存储<br/>.ufw 分区")]

图注:AP 与 STA 都编译进 net_update.c,由 CONFIG_WL_AP_ENABLE 决定走 TCP 服务端(AP)还是 HTTP 客户端(STA);USB HID 与 BT 通道通过 update_main.c 的状态机进入流水线;所有通道最终调用 fs_v2_update.h 暴露的 Flash 升级 API,保证"通道可换、流水线唯一",这是该设计最核心的意图——新增通道只需实现传输层,无需改动 Flash 写入与校验逻辑。

通道变体详解

1. 通道选择宏:CONFIG_WL_AP_ENABLE

net_update.c 的第一处关键分支决定编译进哪个 WiFi 通道:

#ifdef CONFIG_WL_AP_ENABLE
#define HTTP_UPDATE_ENABLE	0
#else
#define HTTP_UPDATE_ENABLE	1 //1,STA模式下http断点续传OTA升级
#endif

Source: net_update.c

设计意图:同一份源码用编译期开关适配两种网络拓扑。AP 模式下设备是服务端(等待 APP 主动连入),STA 模式下设备是客户端(主动向服务器拉取),两者对 TCP/IP 栈的使用方向完全相反,因此用宏消除运行时分支,避免在 RAM 紧张的 loader 中同时保留两套路径。

2. AP 模式通道(设备热点 + TCP 私有协议)

AP 模式的协议常量:

#define CTP_PREFIX "CTP:"
#define CTP_PREFIX_LEN 4
#define CTP_TOPIC_LEN   2
#define CTP_TOPIC_CONTENT_LEN   4
#define CTP_KEEP_ALIVE_TOPIC        "CTP_KEEP_ALIVE"
#define CTP_KEEP_ALIVE_TOPIC_LEN    strlen(CTP_KEEP_ALIVE_TOPIC)
#define CTP_LOGIN_TOPIC             "CTP_LOGIN"
#define CTP_LOGIN_TOPIC_LEN         strlen(CTP_LOGIN_TOPIC)
#define CTP_APP_ACCESS				"APP_ACCESS"
#define CTP_APP_ACCESS_NOTIFY_START	"{\"op\":\"NOTIFY\",\"param\":{\"type\":\"0\",\"ver\":\"%d%s"
#define CTP_APP_ACCESS_NOTIFY_VER	20906
#define CTP_APP_ACCESS_NOTIFY_END	"\"}}"

#define CTP_CTRL_PORT       3333
#define NET_UPDATE_PORT		3335
#define HTTP_PORT			8080

Source: net_update.c

AP 通道在设备侧同时监听三个端口:

  • 3333(CTP 控制口):跑 CTP 文本协议(CTP_KEEP_ALIVE 心跳、CTP_LOGIN 登录、APP_ACCESS 通知),用于 APP 与设备之间的握手、鉴权与升级通知;
  • 3335(数据口):传输固件字节流,使用与 APP/ota_loader 保持一致的 net_ota 20 字节头协议;
  • 8080(HTTP 口):HTTP_DEV_DESC 设备信息描述接口,APP 先 GET 该口获取 device_type、ota_type、firmware_version 等 JSON 描述,再决定是否发起升级。

升级状态定义与 20 字节协议头:

#define NET_UPDATE_STATUS_NONE		(0 << 0)
#define NET_UPDATE_STATUS_OK		(1 << 0)
#define NET_UPDATE_STATUS_ERR		(1 << 1)
#define NET_UPDATE_STATUS_READ      (1 << 2)
#define NET_UPDATE_STATUS_STOP      (1 << 3)
#define NET_UPDATE_STATUS_SAME      (1 << 4)

//协议保持和APP和ota_loard一致
//协议:头部20字节
struct net_ota {
    unsigned int sum_check;//设备端:头部16字节和校验,APP端:头部16字节+读取字节的和校验
    unsigned int seek_addr;//文件偏移地址
    unsigned int read_size;//读取文件长度
    unsigned int all_size;//升级过程中需要的文件总字节数(用在进度条)
    unsigned int status: 8; //NET_UPDATE_STATUS_NONE/OK/ERR
    unsigned int download_percent: 8; //升级过程中设备的百分比
    unsigned int resv: 16; //预留,最后收到这4个字节代表成功和失败
    unsigned char buf[0];
};

Source: net_update.c

该协议与 struct net_local(设备侧对应结构)保持前 20 字节布局一致,net_local 的 buf 指向固件读取缓冲。设计意图:头部含 sum_check 校验和、seek_addr 文件偏移与 read_size 读取长度,天然支持随机读 + 断点续传——APP 可以在任意偏移发起读取,且 status 位域让"最后收到的 4 字节"决定成败,避免单独的状态消息。

AP 模式的内存布局(40K 大缓冲 + 固定 RAM 地址):

#ifdef CONFIG_WL_AP_ENABLE
#define BUF_MAX_SIZE		(40*1024)
static u8 *ota_ram_addr 	= (u8 *)0x1C00000; //OTA缓存地址,长度42*1024,42K
#else
#define BUF_MAX_SIZE		(4*1024)
static u8 *ota_ram_addr 	= NULL;
#endif
static struct net_local *net_local_addr = NULL;

#define RECV_OFFSET_SIZE 	(sizeof(struct net_local))
#define RECV_BUF_MAX_SIZE 	(BUF_MAX_SIZE - sizeof(struct net_local) - sizeof(struct net_ota))//40K大小务必<42K
#define NET_MAX_SIZE 		(RECV_BUF_MAX_SIZE)
#define NET_TCP_OTA			(ota_ram_addr + sizeof(struct net_local))

Source: net_update.c

设计意图:AP 模式下数据源是手机 APP,TCP 吞吐取决于发送窗口,因此分配 40K 接收缓冲(配 42K 的 RAM 预留区,注释强调"40K 务必 < 42K"),利用 RECV_BUF_MAX_SIZE 在缓冲内做预读窗口——当 APP 请求的 seek_addr + read_size 落在已缓存窗口内时直接内存拷贝返回,减少重复建连与读 Flash 次数;STA 模式则退化为 4K 小缓冲(见下文)。

3. STA 模式通道(HTTP 断点续传)

STA 模式的 HTTP 客户端参数:

#if HTTP_UPDATE_ENABLE
#define HTTP_UPDATE_PORT	80			//http端口
#define HTTP_RECV_BUF		(4096*10)	//http升级缓存大小

/*#define HTTP_UPDATE_URL 	"http://test02.jieliapp.com/file/2022/update-ota.ufw"*/
#define HTTP_UPDATE_URL     "http://172.16.109.91:18888/update-ota.ufw"

#define HTTP_GET_HEAD		"GET %s HTTP/1.1\r\n"\
							"Host: %s\r\n"\
							"User-Agent: User\r\n"\
							"Accept: */*\r\n"\
							"Connection: Close\r\n"\
							"Range: bytes=%d-%d\r\n\r\n"
struct http_tcp {
    struct ip4_addr ip_addr;
    struct tcp_pcb *pcb;
    unsigned char conneted: 1;
    unsigned char lock: 7;
};
struct http_range_ota {
    struct http_tcp tcp;
    unsigned int file_size;//文件大小
    unsigned int content_length;
    unsigned int range_start;//断点续传起始
    unsigned int range_end;//断点续传结束
    unsigned int recv_offset;
    unsigned int read_offset;
    unsigned char *host;
    unsigned char *file;
    unsigned char *send_buf;
    unsigned int buf_size;
    unsigned char head_ok;  //http头接收完成
    unsigned char buf[0];
};

Source: net_update.c

设计意图:STA 模式下设备走路由器出网,数据源是远端 HTTP 服务器。HTTP_GET_HEAD 携带 Range: bytes=%d-%d 实现断点续传——升级中断后从 range_start 继续下载,不必重传整个固件;http_range_ota 中的 recv_offset / read_offset 区分"网络收到的字节"与"已写入 Flash 的字节",两者解耦才能支撑边收边写。HTTP_UPDATE_URL 为可配置的升级服务器地址(含内网测试地址示例),生产环境应替换为正式 OTA 域名。

4. USB HID 通道

USB HID 通道的传输载体是设备枚举为 HID 设备后、上位机通过 HID 报告(Interrupt OUT/IN)发送的固件分片。在本仓库的 ota-loader 工程内,HID/BT 通道的更新状态机位于 update_main.c,其核心状态变量为:

//ble_control_info.btupdata_sw / ble_update_state 相关状态
ble_control_info.btupdata_sw = UPDATA_ACCEPT_OK;
ble_control_info.ble_update_state |= LMP_STA_UPDATA_ACCEPT;
//...
ble_control_info.btupdata_sw = UPDATA_STOP;
ble_control_info.ble_update_state &= ~LMP_STA_UPDATA_ACCEPT;

Source: update_main.c 与 update_main.c

btupdata_sw 承载升级开关状态(UPDATA_ACCEPT_OK 接受、UPDATA_STOP 停止、UPDATA_STOP_KEYERR 密钥错误停止、UPDATA_READ 读取中),ble_update_state 承载传输状态位(LMP_STA_UPDATA_ACCEPT 接受、LMP_STA_UPDATA_ACTIVE_SCUSS 激活成功)。HID 与 BT 通道均通过该状态机汇聚到共享流水线。说明:HID 传输层的具体收发实现(HID 报告解析、端点调度)位于 loader 的 USB 设备栈中,在本次文档的源码取证预算内未定位到独立文件,此处不作臆测;其下游行为与 AP/STA 完全一致——进入 fs_v2_update 流水线写 Flash。

5. 共享升级流水线(fs_v2_update)

所有通道最终调用同一组 Flash 升级 API(fs_v2_update.h 声明):

extern u8 ufw_head_check(u32 *update_part_flag);
extern u8 flash_bin_head_check(void);
extern u8 update_type_check(u32 *update_len);
extern u8 flash_update_process(void);
extern u8 flash_file_data_verify(void);
extern u8 flash_update_reserve_area(void);
extern u8 flash_all_data_verify(void);
extern u8 flash_parts_update_check(u32 update_part_flag);
extern void clr_update_loader_record(void);
extern u32 fs_dev_mount(u16 dev_type);
extern u32 get_ex_buffer(u8 *buffer);

Source: fs_v2_update.h

流水线顺序(以通道收完数据为前提):

  1. ufw_head_check:校验 .ufw 升级包头部 CRC,并输出本次需要升级的分区标志位(update_part_flag);
  2. flash_bin_head_check / update_type_check:校验 Flash 中已有 bin 头、判定升级类型与总长度;
  3. flash_update_process:执行分区写 Flash(BT 配置、提示音、预留区、ANC 配置等按 UPDATE_PART_* 位域逐项处理);
  4. flash_file_data_verify / flash_all_data_verify:对写入数据与整包数据做 CRC 校验;
  5. clr_update_loader_record:升级完成后清除 loader 记录,下次启动进入正式固件。

分区标志位定义:

enum {
    UPDATE_PART_BT_CFG = BIT(0),
    UPDATE_PART_TONE = BIT(1),
    UPDATE_PART_RESERVED = BIT(2),
    UPDATE_PART_ANC_CFG = BIT(3),
    UPDATE_PART_ANC_COEF = BIT(4),
    UPDATE_PART_ALL_FLASH = 0xffffffff,
};

Source: fs_v2_update.h

设计意图:分区位域让升级包可以部分升级(如只更新提示音 UPDATE_PART_TONE),避免全量擦写;UPDATE_PART_ALL_FLASH 则代表整片升级。通道层不感知分区细节,只负责把 .ufw 字节完整送达,职责边界清晰。

Core Flow

AP 模式升级时序(APP → 设备)

sequenceDiagram
    participant APP as 手机 APP
    participant HTTPD as 设备 HTTP 服务 (8080)
    participant CTP as 设备 CTP 服务 (3333)
    participant TCP as 设备 TCP 数据服务 (3335)
    participant UPD as update_main 状态机
    participant FS as fs_v2_update 流水线

    APP->>HTTPD: GET 设备描述
    HTTPD-->>APP: HTTP_DEV_DESC JSON (ota_type/firmware_version)
    APP->>CTP: CTP_LOGIN / CTP_KEEP_ALIVE
    CTP-->>APP: ACK
    APP->>TCP: net_ota 头 (seek_addr/read_size/all_size)
    TCP->>UPD: 置 UPDATA_READ / 校验 sum_check
    UPD->>FS: flash_update_process(写入固件)
    FS-->>TCP: 返回数据 + download_percent
    TCP-->>APP: net_ota 响应 (status=OK/ERR)
    APP->>TCP: 最后 4 字节 (status/resv)
    TCP-->>APP: 升级完成/失败确认

STA 模式升级时序(设备 → HTTP 服务器)

sequenceDiagram
    participant DEV as 设备 (STA 客户端)
    participant WIFI as 路由器 WiFi
    participant SRV as HTTP 升级服务器
    participant FS as fs_v2_update 流水线

    DEV->>WIFI: wifi_connect 联网
    DEV->>SRV: GET /update-ota.ufw + Range: bytes=0-40959
    SRV-->>DEV: 206 Partial Content (分片)
    DEV->>FS: flash_update_process(写入分片)
    FS-->>DEV: 校验通过
    DEV->>SRV: 下一段 Range: bytes=40960-...
    SRV-->>DEV: 继续分片 ...
    DEV->>FS: flash_all_data_verify(整包校验)
    FS-->>DEV: clr_update_loader_record(完成)

通道汇合流程图

flowchart TD
    START([升级请求]) --> CH{"通道类型?"}
    CH -->|"AP"| TCP_SRV["TCP Server 3335<br/>net_ota 协议"]
    CH -->|"STA"| HTTP_CLI["HTTP Client 80<br/>Range 断点续传"]
    CH -->|"USB HID"| HID_TX["HID 报告收发"]
    TCP_SRV --> SUM{"sum_check 校验"}
    HTTP_CLI --> HEAD{"HTTP 头解析"}
    HID_TX --> SW{"btupdata_sw 状态"}
    SUM -->|"通过"| WRITE["flash_update_process"]
    HEAD -->|"head_ok"| WRITE
    SW -->|"ACCEPT_OK"| WRITE
    WRITE --> VERIFY{"flash_all_data_verify"}
    VERIFY -->|"通过"| DONE([升级完成复位])
    VERIFY -->|"失败"| ERR([置 NET_UPDATE_STATUS_ERR / 上报错误码])

Usage Examples

示例 1:TCP 发送前的缓冲检查(AP 模式关键路径)

static int send_msg(struct tcp_pcb *pcb, u8 *buf, u32 len)
{
    err_t err;
    int size = tcp_sndbuf(pcb);
    if (size < len) {
        printf("err tcp no buf to send , %d , %d\n", size, len);
        return 0;
    }
    size = MIN(size, len);
    err = tcp_write(pcb, buf, size, 1);
    if (err != ERR_OK) {
        puts("tcp_write err \n");
        return 0;
    }
    return size;
}

Source: net_update.c

该函数先查 tcp_sndbuf 发送窗口再 tcp_write,窗口不足时直接返回 0 而不是阻塞,配合上层 OS_SEM(5 秒超时)实现背压,防止 loader 在 TCP 拥塞时内存被写爆。

示例 2:校验和算法(协议层完整性保障)

static unsigned int sumget(void *buf, int len)
{
    unsigned char *pbuf = (unsigned char *)buf;
    unsigned int sum = 0;
    int i = 0;
    while (i < len) {
        sum += pbuf[i++];
    }
    return sum;
}
static int sumcheck(void *buf, int len, unsigned int sum)
{
    unsigned char *pbuf = (unsigned char *)buf;
    unsigned int sum_check = 0;
    int i = 0;
    while (i < len) {
        sum_check += pbuf[i++];
    }
    if (sum_check != sum) {
        printf("err sumcheck = 0x%x, 0x%x\n", sum_check, sum);
        return -1;
    }
    return 0;
}

Source: net_update.c

net_ota 协议头中 sum_check 的算法即为此 32 位累加和:设备侧对头部 16 字节求和,APP 侧对头部 + 读取字节求和。校验失败返回 -1,上层据此置 NET_UPDATE_STATUS_ERR。

示例 3:固件预读窗口命中时直接内存拷贝

if (net_local_addr->status == NET_UPDATE_STATUS_READ) {
    if (!net_local->all_size) {
        if (!net_ota->seek_addr && !net_ota->read_size) {
            net_ota->seek_addr = net_local->seek_addr;
        }
        if (!net_ota->read_size || net_local->seek_addr < net_ota->seek_addr ||
            ((net_local->seek_addr + net_local->read_size) > (net_ota->seek_addr + net_ota->read_size))) {
            net_ota->all_size = 0;
            net_ota->read_size = tcp_size;
            net_ota->seek_addr = net_local->seek_addr;
        } else {
            memcpy(net_local->buf, net_ota->buf + (net_local->seek_addr - net_ota->seek_addr), net_local->read_size);
            return net_local->read_size;
        }
    }
#if NET_OTA_FILE_RESV
    if (net_ota->seek_addr > (RECV_BUF_MAX_SIZE - net_local->read_size)) {
        net_ota->seek_addr -= (RECV_BUF_MAX_SIZE - net_local->read_size);
    }
#endif

Source: net_update.c

设计意图:APP 多次请求的偏移如果落在已缓存窗口内(seek_addr >= net_ota->seek_addr 且不越界),直接 memcpy 命中返回,避免重复读 Flash;NET_OTA_FILE_RESV(文件预留读取标志,默认 1 往前读取)控制窗口回退方向,且由 net_ota_file_resv_set() 在正向读取时关闭反向预读,注释说明"避免每包重新建连"。

Configuration Options

选项类型默认值说明
CONFIG_WL_AP_ENABLE编译宏未定义定义后启用 AP 模式(HTTP_UPDATE_ENABLE=0);未定义则启用 STA 模式(HTTP_UPDATE_ENABLE=1)
HTTP_UPDATE_ENABLE宏0(AP)/ 1(STA)由 CONFIG_WL_AP_ENABLE 派生,STA 模式下编译 HTTP 客户端
HTTP_UPDATE_PORT宏80STA 模式 HTTP 服务器端口
HTTP_RECV_BUF宏4096*10STA 模式 HTTP 接收缓存大小(约 40K)
HTTP_UPDATE_URL宏内网测试地址STA 模式升级固件 URL(生产应替换为正式 OTA 服务器)
CTP_CTRL_PORT宏3333AP 模式 CTP 控制协议监听端口
NET_UPDATE_PORT宏3335AP 模式固件数据 TCP 监听端口
HTTP_PORT宏8080AP 模式设备描述 HTTP 服务端口
BUF_MAX_SIZE宏40K(AP)/ 4K(STA)固件传输缓冲上限
ota_ram_addr指针0x1C00000(AP)OTA 缓存 RAM 基址,长度 42K,要求 BUF_MAX_SIZE 必须小于 42K
NET_OTA_FILE_RESV宏1文件预留区读取方向:1 往前读取
net_ota_file_resv_en全局变量1运行时预读开关,正向读取时关闭反向预读
OS_SEM_TIME_MAX宏500(5s)网络读取信号量超时(500 × 10ms)
UPDATE_CMD_WAIT_TIMEOUT宏2000ULBT/HID 命令等待超时,单位 2ms(约 4s)

UPDATE_CMD_WAIT_TIMEOUT 定义于 update_main.c。

API Reference

net_update.c(WiFi 通道)

函数签名说明
net_ota_file_resv_setvoid net_ota_file_resv_set(u8 en)设置预读标志,正向读取时关闭反向预读
sumgetstatic unsigned int sumget(void *buf, int len)计算 32 位累加校验和
sumcheckstatic int sumcheck(void *buf, int len, unsigned int sum)校验和比对,失败返回 -1
send_msgstatic int send_msg(struct tcp_pcb *pcb, u8 *buf, u32 len)TCP 发送,窗口不足返回 0
msgsenddataint msgsenddata(u8 *buf, u32 len, u8 no_wait)读取指定偏移固件并发送,命中预读窗口时直接 memcpy
net_update_http_dev_descint net_update_http_dev_desc(void)启动 8080 设备描述 HTTP 服务
net_update_ctp_serverint net_update_ctp_server(void)启动 3333 CTP 控制服务

fs_v2_update.h(共享 Flash 流水线)

函数签名说明
ufw_head_checku8 ufw_head_check(u32 *update_part_flag)校验 .ufw 头 CRC,输出分区标志位
flash_bin_head_checku8 flash_bin_head_check(void)校验 Flash 内 bin 头
update_type_checku8 update_type_check(u32 *update_len)判定升级类型并输出升级总长度
flash_update_processu8 flash_update_process(void)执行分区写 Flash 主流程
flash_file_data_verifyu8 flash_file_data_verify(void)校验已写入的文件数据
flash_update_reserve_areau8 flash_update_reserve_area(void)升级 Flash 预留区
flash_all_data_verifyu8 flash_all_data_verify(void)整包数据最终校验
flash_parts_update_checku8 flash_parts_update_check(u32 update_part_flag)按分区标志位检查需升级的分区
clr_update_loader_recordvoid clr_update_loader_record(void)清除 loader 升级记录
fs_dev_mountu32 fs_dev_mount(u16 dev_type)挂载文件系统设备(升级包来源)
get_ex_bufferu32 get_ex_buffer(u8 *buffer)获取外部扩展缓冲区

错误码(fs_v2_update.h)

升级过程错误 UPDATE_ERR_*(节选):UPDATE_ERR_NONE=0、UPDATE_ERR_UFW_HEAD_CRC_ERR、UPDATE_ERR_MALLOC_ERR、UPDATE_ERR_LOADER_WRITE_ERR、UPDATE_ERR_KEY_ERR=12(密钥错误)、UPDATE_ERR_UBOOT_NOT_MATCH、UPDATE_ERR_CODE_VERIFY_ERR=16、UPDATE_ERR_ERASE_FAIL=28(擦除失败,可能写保护)、UPDATE_ERR_PRODUCT_ID_NOT_MATCH(产品 ID 不匹配)、UPDATE_ERR_BR40_FLASH_ID_ERR 等。

结果码 UPDATE_RESULT_*(节选):UPDATE_RESULT_FILE_SIZE_ERR=0x1、UPDATE_RESULT_LOADER_VERIFY_ERR、UPDATE_RESULT_FLASH_DATA_VERIFY_ERR=0x7、UPDATE_RESULT_UBOOT_NOT_MATCH=0x9、UPDATE_RESULT_PRODUCT_INFO_NOT_MATCH、UPDATE_RESULT_FLASH_ERASE_ERR=0xd、UPDATE_RESULT_ISD_CONFIG_ERR=0x28(ini 的 REQUIRE_TARGET_FEATURE 配置错误)等;UPDATE_RESULT_FLAG_BITMAP (BIT(7)) 标记结果位图。

Source: fs_v2_update.h

Failure Modes, Edge Cases & Concurrency

  • 校验失败:sumcheck 返回 -1 时协议层丢弃该包并置 NET_UPDATE_STATUS_ERR;Flash 侧 flash_file_data_verify / flash_all_data_verify 失败会以 UPDATE_RESULT_FLASH_DATA_VERIFY_ERR 上报,loader 不清记录,下次启动可重试。
  • TCP 发送窗口不足:send_msg 检查 tcp_sndbuf,不足返回 0,配合 OS_SEM 5 秒超时避免阻塞死等;net_recv_err / net_ota_stop 标志用于中断异常传输。
  • 断点续传边界:http_range_ota 的 range_start/range_end 与 recv_offset/read_offset 解耦,中断后按 Range 重发;设备侧 seek_addr 窗口重叠判定(seek_addr < net_ota->seek_addr 或越界)决定重新预读还是内存命中。
  • 缓冲越界风险:RECV_BUF_MAX_SIZE = BUF_MAX_SIZE - sizeof(net_local) - sizeof(net_ota),注释明确"40K 务必 < 42K",防止覆盖预留 RAM 区外的固件数据。
  • 并发/重入:AP 模式同时运行 CTP、TCP、HTTP 三个服务,各自使用独立 tcp_pcb(client_pcb、http_accept、ctp_accept 标志区分连接归属);client_accept 单连接模型,多个 APP 同时连接时以最后建立者为准。
  • 升级中断:UPDATA_STOP / UPDATA_STOP_KEYERR 路径会复位 ble_update_state 并清零 date_len(已收数据长度),防止半包残留被误判为完整固件。

Performance & Operational Notes

  • AP 模式 40K 预读窗口:通过 memcpy 命中窗口避免重复读 Flash,显著减少 TCP 往返;窗口回退由 NET_OTA_FILE_RESV 控制,正向读取时建议 net_ota_file_resv_set(0) 关闭反向预读以省建连开销。
  • STA 模式 4K 小缓冲:STA 数据源为服务器,无需大窗口预读,4K 缓冲配合 HTTP Range 分片边收边写,节省 loader RAM。
  • 进度上报:download_percent(8 bit 位域)由设备随每包回传,APP 据此绘制进度条;all_size 只在整包开始时 > 0,用于总进度归一化。
  • 看门狗:update_main.c 通过 CLR_WDT() 周期性喂狗,升级写 Flash 期间避免超时复位(UPDATE_CMD_WAIT_TIMEOUT 2ms 粒度用于命令等待超时判定)。
  • 运维建议:生产环境将 HTTP_UPDATE_URL 替换为正式 OTA 服务器并走 HTTPS/CDN;AP 模式下 HTTP_DEV_DESC 中的 firmware_version 需与固件实际版本一致,否则 APP 端版本比对会误判"已最新"。

Extension Points

  • 新增通道:只需实现"把 .ufw 字节送入设备内存"的传输层,并复用 fs_v2_update.h 流水线——这正是 AP/STA/HID 三通道共存的架构依据。协议头(struct net_ota)注释明确"协议保持和 APP 和 ota_loader 一致",新增端(如 Linux 上位机)需严格对齐 20 字节布局与 sum_check 算法。
  • 分区扩展:UPDATE_PART_* 位域(BT 配置、提示音、预留区、ANC 配置/系数)可继续追加,流水线按位域增量升级,通道层无感知。
  • 服务器侧:STA 通道的服务器只需支持 HTTP Range 请求(206 Partial Content),即可复用现有 HTTP_GET_HEAD 实现,无需私有协议。

Related Links

  • 升级通道变体(AP/STA/USB HID)源码入口:net_update.c
  • 共享升级流水线声明:fs_v2_update.h
  • BT/HID 更新状态机:update_main.c
  • UART 升级通道变体(兄弟页面/目录):uart_user_update
  • SD 卡 OTA 变体(兄弟页面/目录):sd_ota_update / sd_sec_ota_update
Prev
uboot 应用框架与 WiFi 示例
Next
网络与系统库依赖