网络协议栈
AC792N SDK(WL83 平台)的网络协议栈以 lwIP 2.2.0 为核心,向上提供 Socket / DNS / netif 抽象,向下对接 Wi-Fi 驱动与 DHCP 状态机;应用层围绕它构建了配网(网络配置)、OTA 升级、PJSIP 音视频等能力。本页面向该协议栈的整体架构、关键集成点与配置流程。
Purpose and Scope
本文档说明 AC792N SDK 网络协议栈的职责边界与实现机制,包括:
- lwIP 协议栈在 WL83 平台的集成方式(netif 抽象、DHCP 状态机、Socket API);
- 应用层对协议栈的封装:
config_network(配网框架)、pjsip_lwip(网络就绪探测与地址查询)、net_server(网络服务); - 从 Wi-Fi 关联成功到 DHCP 获取 IP、再到应用联网的完整控制流;
- 关键 API、配置开关与失败模式。
以下内容属于相邻能力,不在本页展开,请参见对应页面:
- Wi-Fi 驱动与连接管理(
wifi_connect、wifi_get_mode_cur_info、wifi_sta_connect)属于 Wi-Fi 子系统; - 基于网络的 OTA 升级(
net_update.c、net_single_backup_update.c)属于固件升级能力; - BLE 配网协议(
le_net_cfg.c)与声学配网(voiceprint_cfg)属于配网协议细节; - USB 网络设备(
usbnet.c)属于 USB 子系统。
Overview
AC792N SDK 的联网路径分为三层:
- 底层协议栈:lwIP 2.2.0(链接脚本中可见
lib_lwip_2_2_0_version版本标记),负责 IP 协议族、TCP/UDP、Socket、DNS 与 netif 管理。 - 平台适配层:SDK 提供
lwip_get_netif_info(WIFI_NETIF, ...)等接口,把 lwIP 的struct netif收敛为应用友好的struct netif_info(含ip字段),并把 Wi-Fi 的 STA 连接状态与 DHCP 结果绑定(WIFI_STA_NETWORK_STACK_DHCP_SUCC)。 - 应用服务层:
config_network提供多通道配网(Airkiss、WSC、BLE、二维码、声学);pjsip_lwip在 SIP 业务启动前探测网络是否真正可用;net_server提供 TCP/UDP 服务端承载。
设计意图:把"Wi-Fi 关联"与"IP 层就绪"解耦。关联成功不等于可以收发数据,只有 DHCP 成功(WIFI_STA_NETWORK_STACK_DHCP_SUCC)且 netif_info->ip != 0 时应用才认为网络可用。这一约定被 SIP、配网、OTA 等所有网络应用复用,避免各模块重复实现就绪判断。
Architecture
flowchart TD
subgraph sg_App["应用层 (App)"]
Cfg["config_network<br/>配网框架"]
Pj["pjsip_lwip<br/>SIP 网络助手"]
Ns["net_server<br/>网络服务"]
Up["net_update<br/>网络 OTA"]
end
subgraph sg_Adapt["平台适配层"]
Netif["lwip_get_netif_info<br/>WIFI_NETIF"]
WifiMode["wifi_get_mode_cur_info"]
StaState["wifi_get_sta_connect_state"]
Evt["event/net_event.h<br/>网络事件"]
end
subgraph sg_Lwip["lwIP 2.2.0 协议栈"]
Sock["lwip/sockets.h"]
Netdb["lwip/netdb.h"]
Dhcp["DHCP 客户端"]
NetifCore["netif 管理"]
end
subgraph sg_Wifi["Wi-Fi 子系统"]
Wifi["wifi_connect / wsc / smp"]
end
Cfg --> Wifi
Cfg --> Evt
Pj --> Netif
Pj --> WifiMode
Pj --> StaState
Ns --> Sock
Up --> Sock
Netif --> NetifCore
Netif --> Dhcp
Sock --> NetifCore
Netdb --> NetifCore
Wifi --> StaState
Wifi --> Dhcp
架构说明:
- lwIP 核心(
lwip/sockets.h、lwip/netdb.h)是协议栈主体,提供 BSD 风格 Socket 与 DNS 解析;config_network.c直接包含这些头文件使用协议栈能力。 - 平台适配层把 lwIP 的 netif 状态、Wi-Fi 模式、STA 连接状态三者聚合,供上层查询;
pjsip_lwip.c正是通过这三者判断"网络就绪"。 - 应用层各模块通过 Socket 或适配接口消费协议栈,互不直接依赖,便于裁剪(如关闭 PJSIP 时
pjsip_lwip.c整体不编译)。
配网框架:config_network
config_network 是整个 SDK 的"入网入口",负责把各种配网通道(Airkiss、WSC、BLE、二维码、声学、第三方协议)汇聚成统一的"拿到 SSID/密码 → 连接 Wi-Fi"流程。
对外接口
config_network.h 定义了六个公共函数,是整个配网能力的契约:
#ifndef __CONFIG_NETWORK_H__
#define __CONFIG_NETWORK_H__
extern void config_network_start(void);
extern void config_network_stop(void);
extern void wifi_smp_set_ssid_pwd(void);
extern void config_network_connect(void);
extern void config_network_broadcast(void);
extern u8 is_in_config_network_state(void);
#endif // __CONFIG_NETWORK_H__
Source: config_network.h
config_network_start:进入配网状态,按编译开关启动对应配网通道;config_network_stop:退出配网状态,反初始化各通道;is_in_config_network_state:查询当前是否处于配网状态(返回全局config_network_flag);config_network_connect/config_network_broadcast:配网成功后连接路由器 / 对外广播状态。
配网状态机与通道选择
flowchart TD
Start([config_network_start]) --> Flag["config_network_flag = 1"]
Flag --> Sel{"编译开关选择"}
Sel -->|"CONFIG_AIRKISS_NET_CFG"| Airkiss["wifi_enter_smp_cfg_mode<br/>SMP 配网"]
Sel -->|"CONFIG_WSC_NET_CFG"| Wsc["wifi_wsc_start<br/>WSC 配网"]
Sel -->|"CONFIG_QR_CODE_NET_CFG"| Qr["qr_code_net_cfg_init"]
Sel -->|"BLE + NET_CFG_EN"| Ble["le_net_cfg_all_init"]
Sel -->|"CONFIG_ACOUSTIC_COMMUNICATION_ENABLE"| Voice["voiceprint_cfg_start"]
Airkiss --> Done["等待配网结果"]
Wsc --> Done
Qr --> Done
Ble --> Done
Voice --> Done
Done -->|"获取 SSID/密码"| Connect["wifi_sta_connect(ssid, pwd)"]
Connect --> Stop([config_network_stop])
config_network_start 的实现要点(config_network.c):
void config_network_start(void)
{
config_network_flag = 1;
#ifdef CONFIG_AIRKISS_NET_CFG
memset(&airkiss_result, 0, sizeof(airkiss_result));
wifi_set_smp_cfg_timeout(100);
if (!wifi_is_on()) {
wifi_on();
}
wifi_enter_smp_cfg_mode();
#else
#ifdef CONFIG_WSC_NET_CFG
memset(&wsc_result.ssid, 0, sizeof(wsc_result.ssid));
memset(&wsc_result.pwd, 0, sizeof(wsc_result.pwd));
if (!wifi_is_on()) {
wifi_on();
}
wifi_sta_connect(wsc_result.cfg_ssid, "", 0);
wifi_wsc_start(wsc_result.cfg_ssid, wsc_result.ap_pincode,
wsc_result.ap_pincode[0] ? STA_REGISTRAR_PIN_MODE : STA_ENROLLEE_PIN_MODE);
#endif
#endif
#ifdef CONFIG_QR_CODE_NET_CFG
memset(&qr_code_net_result, 0, sizeof(qr_code_net_result));
qr_code_net_cfg_init();
#endif
#if TCFG_USER_BLE_ENABLE && TCFG_BLE_NET_CFG_EN
memset(&bt_net_result, 0, sizeof(bt_net_result));
ble_config_complete_flag = 0;
#if defined THIRD_PARTY_PROTOCOLS_SEL && (THIRD_PARTY_PROTOCOLS_SEL & NET_CFG_EN)
le_net_cfg_all_init();
#endif
#endif
#ifdef CONFIG_ACOUSTIC_COMMUNICATION_ENABLE
memset(&voiceprint_result, 0, sizeof(voiceprint_result));
voiceprint_cfg_start();
#endif
}
Source: config_network.c
设计意图:
- 编译期裁剪:配网通道全部用宏控制(
CONFIG_AIRKISS_NET_CFG、CONFIG_WSC_NET_CFG、CONFIG_QR_CODE_NET_CFG、CONFIG_ACOUSTIC_COMMUNICATION_ENABLE及TCFG_BLE_NET_CFG_EN),未开启的通道不占任何 RAM/ROM。 - 结果缓冲区静态化:每种通道的结果(SSID/密码)存放在独立 static 结构(如
airkiss_result、wsc_result、bt_net_result),避免动态内存分配在配网低内存场景下的不确定性。 - Wi-Fi 开机保证:进入配网前统一检查
wifi_is_on(),未开机先wifi_on(),保证协议栈的 netif 已注册、DHCP 客户端已就绪。 - WSC 双模式:根据 AP PIN 是否为空自动选择
STA_REGISTRAR_PIN_MODE(路由器作为 Registrar)或STA_ENROLLEE_PIN_MODE(设备作为 Enrollee),一个函数覆盖两种拓扑。
config_network_stop 则做对称清理:复位 config_network_flag,停止声学配网、反初始化二维码与 BLE 通道;BLE 通道还通过 ble_config_complete_flag 判断是否真正完成了配网,避免"未完成就退出"时遗留未清理的协议栈。
网络就绪判定:pjsip_lwip 的实践
pjsip_lwip.c 是"如何正确使用协议栈"的典型范例:它不管理 Wi-Fi 关联和 DHCP 策略,只做就绪探测与地址查询,并把结果反馈给 SIP 业务。其核心函数 query_sta_network_ready 将三个来源聚合为一个布尔结论:
static int query_sta_network_ready(struct netif_info *netif_info,
struct wifi_mode_info *mode_info,
enum wifi_sta_connect_state *sta_state)
{
struct netif_info local_netif_info;
struct wifi_mode_info local_mode_info;
enum wifi_sta_connect_state local_sta_state;
if (!netif_info) {
netif_info = &local_netif_info;
}
if (!mode_info) {
mode_info = &local_mode_info;
}
if (!sta_state) {
sta_state = &local_sta_state;
}
pj_bzero(netif_info, sizeof(*netif_info));
lwip_get_netif_info(WIFI_NETIF, netif_info);
mode_info->mode = NONE_MODE;
mode_info->ssid = NULL;
mode_info->pwd = NULL;
wifi_get_mode_cur_info(mode_info);
*sta_state = wifi_get_sta_connect_state();
return (mode_info->mode == STA_MODE &&
netif_info->ip != 0 &&
*sta_state == WIFI_STA_NETWORK_STACK_DHCP_SUCC);
}
Source: pjsip_lwip.c
判定条件的含义:
| 条件 | 来源 | 含义 |
|---|---|---|
mode_info->mode == STA_MODE | wifi_get_mode_cur_info | 当前处于 STA 模式(而非 AP/组合模式) |
netif_info->ip != 0 | lwip_get_netif_info(WIFI_NETIF, ...) | lwIP netif 已获得非零 IPv4 地址 |
sta_state == WIFI_STA_NETWORK_STACK_DHCP_SUCC | wifi_get_sta_connect_state | Wi-Fi 状态机已推进到"协议栈 DHCP 成功" |
只有三者同时满足,SIP 才认为网络可用;get_local_ip_string 随后用 inet_ntoa_r 把 netif_info->ip(网络字节序整数)格式化为点分十进制字符串:
pj_status_t get_local_ip_string(char *buf, unsigned buf_size)
{
struct netif_info netif_info;
struct wifi_mode_info mode_info;
enum wifi_sta_connect_state sta_state;
PJ_ASSERT_RETURN(buf && buf_size >= 16, PJ_EINVAL);
if (!query_sta_network_ready(&netif_info, &mode_info, &sta_state)) {
log_debug("get_local_ip_string: network not ready (mode=%d, sta_state=%d, ip=0x%08x)",
mode_info.mode, sta_state, (unsigned int)netif_info.ip);
return PJ_ENOTFOUND;
}
if (!inet_ntoa_r(netif_info.ip, buf, (int)buf_size)) {
return PJ_EUNKNOWN;
}
return PJ_SUCCESS;
}
Source: pjsip_lwip.c
该文件还包含 log_host_resolution,通过 pj_getaddrinfo(pj_AF_INET(), ...) 做 DNS 解析诊断——这是对 lwip/netdb.h DNS 能力的间接使用。边界约定(文件头注释明确):不管理 Wi-Fi 关联与 DHCP 策略、不创建 SIP 传输、不解析 SIP 报文,只做网络环境助手,因此可独立裁剪。
核心控制流:从配网到应用联网
以下序列图把"设备上电 → 配网 → DHCP → 应用联网"的完整路径串起来,标注了实际参与的模块与状态:
sequenceDiagram
participant App as 应用/配网入口
participant Cfg as config_network
participant Wifi as Wi-Fi 子系统
participant Lwip as lwIP 2.2.0
participant Svc as 网络服务 (net_server / SIP)
App->>Cfg: config_network_start()
Cfg->>Cfg: config_network_flag = 1
Cfg->>Wifi: wifi_is_on() / wifi_on()
Cfg->>Wifi: 启动配网通道 (SMP/WSC/BLE/QR/声学)
Wifi-->>Cfg: 配网结果 (SSID/密码)
Cfg->>Wifi: wifi_sta_connect(ssid, pwd)
Wifi->>Wifi: 关联 + 4-way handshake
Wifi->>Lwip: netif up,启动 DHCP
Lwip->>Lwip: DHCP 交互 (DISCOVER/OFFER/REQUEST/ACK)
Lwip-->>Wifi: WIFI_STA_NETWORK_STACK_DHCP_SUCC
Wifi-->>Svc: 网络事件 (net_event)
Svc->>Lwip: lwip_get_netif_info(WIFI_NETIF)
Lwip-->>Svc: netif_info.ip != 0
Svc->>Lwip: socket()/connect() 或 DNS 解析
Lwip-->>Svc: 数据收发
Svc->>Cfg: config_network_stop() (退出配网态)
关键点:
- 配网阶段不占用协议栈业务:
config_network_start只启动通道与 Wi-Fi,真正的 IP 层动作由 Wi-Fi 关联成功后自动触发。 - 状态推进由 Wi-Fi 状态机驱动:
WIFI_STA_NETWORK_STACK_DHCP_SUCC表示"协议栈 DHCP 成功",它是应用层唯一可信的"IP 就绪"信号。 - 就绪判定是轮询式:
pjsip_lwip的query_sta_network_ready每次调用实时读取三个状态源,不缓存,避免陈旧状态导致 SIP 提前拉起。 - 网络事件旁路通知:
event/net_event.h(struct net_event)用于模块间解耦,例如腾讯连连配网回调中构造net_event上抛,配网结果不依赖轮询。
配置选项
| 宏 / 配置项 | 类型 | 默认 | 说明 |
|---|---|---|---|
CONFIG_NET_ENABLE | 编译宏 | 平台相关 | 总开关,关闭后 config_network.c 整体不参与编译 |
CONFIG_AIRKISS_NET_CFG | 编译宏 | 关闭 | 启用 Airkiss/SMP 配网通道(wifi_enter_smp_cfg_mode) |
CONFIG_WSC_NET_CFG | 编译宏 | 关闭 | 启用 WSC(Wi-Fi Simple Config)配网通道 |
CONFIG_QR_CODE_NET_CFG | 编译宏 | 关闭 | 启用二维码配网通道(qr_code_net_cfg_init) |
CONFIG_ACOUSTIC_COMMUNICATION_ENABLE | 编译宏 | 关闭 | 启用声学通信配网(voiceprint_cfg_start) |
TCFG_USER_BLE_ENABLE | 编译宏 | 关闭 | BLE 总开关,与配网组合使用 |
TCFG_BLE_NET_CFG_EN | 编译宏 | 由 THIRD_PARTY_PROTOCOLS_SEL 推导 | 是否允许通过 BLE 配网;MIJIA_EN/NET_CFG_EN/DUEROS_EN 任一开启时置 1 |
THIRD_PARTY_PROTOCOLS_SEL | 位域宏 | 0 | 第三方协议选择:MIJIA_EN(米家)、NET_CFG_EN(乐鑫 net_cfg)、DUEROS_EN(小度) |
TCFG_BT_NET_CFG_TENCENT_EN | 编译宏 | 关闭 | 腾讯连连 BLE 配网(tencent_net_config 回调) |
TCFG_PJSIP_ENABLE | 编译宏 | 关闭 | 启用 PJSIP 及 pjsip_lwip.c 网络助手 |
TCFG_POWER_ON_ENABLE_BLE | 编译宏 | 关闭 | 上电即开 BLE;影响 config_network_stop 是否反初始化 BLE 配网 |
说明:以上宏在 app_config.h 及具体板级配置中定义,config_network.c 通过 #ifdef/#if 做编译期裁剪;THIRD_PARTY_PROTOCOLS_SEL 同时决定 BLE 配网初始化走 le_net_cfg_all_init 还是 le_net_cfg_dueros_all_init。
API 参考
config_network_start(void)
进入配网状态。置位 config_network_flag,按编译开关初始化各配网通道,必要时打开 Wi-Fi。
- 返回:void
- 副作用:可能调用
wifi_on();启动 SMP/WSC/BLE/QR/声学通道。
config_network_stop(void)
退出配网状态。复位 config_network_flag,反初始化各通道;BLE 通道仅在 ble_config_complete_flag == 0 且未 TCFG_POWER_ON_ENABLE_BLE 时退出。
- 返回:void
is_in_config_network_state(void) : u8
查询是否处于配网状态。
- 返回:
config_network_flag的值(1 = 配网中,0 = 非配网态)。
wifi_smp_set_ssid_pwd(void)
由 SMP 配网通道在拿到 SSID/密码后调用,把结果写入 Wi-Fi 并触发连接。声明于 config_network.h,实现在 Wi-Fi 子系统。
- 返回:void
config_network_connect(void) / config_network_broadcast(void)
配网完成后的收尾动作:连接目标 AP / 向配网端广播结果。具体实现依赖板级与配网通道。
- 返回:void
query_sta_network_ready(netif_info, mode_info, sta_state) : int(静态,pjsip_lwip)
聚合 STA 模式、netif IP、DHCP 状态三个信号判断网络就绪。参数可传 NULL,内部使用局部变量兜底。
- 返回:1 = 就绪,0 = 未就绪。
get_local_ip_string(buf, buf_size) : pj_status_t
获取本机 IPv4 点分十进制字符串。
- 参数:
buf输出缓冲区(需 ≥16 字节);buf_size缓冲区长度。 - 返回:
PJ_SUCCESS成功;PJ_EINVAL参数非法;PJ_ENOTFOUND网络未就绪;PJ_EUNKNOWN格式化失败。
config_wsc_ssid_and_pincode(ssid, pincode)(CONFIG_WSC_NET_CFG 下)
预置 WSC 配网的 AP SSID 与 PIN 码,内部做长度校验后拷贝到 wsc_result。
- 返回:void
失败模式、边界情况与并发
网络未就绪时的行为
get_local_ip_string在未就绪时返回PJ_ENOTFOUND并打印诊断日志(mode / sta_state / ip 十六进制),调用方(SIP)据此延迟拉起而不是崩溃。这是"协议栈就绪"与"业务可用"之间的显式门禁。- DHCP 未成功时
netif_info->ip == 0,所有依赖 IP 地址的模块(配网广播、OTA 上报)都必须自行重试或等待WIFI_STA_NETWORK_STACK_DHCP_SUCC事件。
配网通道的互斥与清理
- 配网通道通过
#ifdef互斥编译,同一构建只存在一个"主通道"(Airkiss 优先于 WSC),避免多通道同时进入 SMP 模式。 config_network_stop的清理必须与config_network_start严格配对;BLE 通道用ble_config_complete_flag区分"已配网成功"与"中途退出",防止未完成配网时残留 BLE 协议栈。
缓冲区边界
- 所有配网结果结构体对 SSID/密码使用固定数组(
ssid[33]、pwd[65]),tencent_net_config、config_wsc_ssid_and_pincode等入口在strncpy/strcpy前做长度比较,越界输入被丢弃。 get_local_ip_string要求buf_size >= 16(IPv4 点分十进制最大长度),并在inet_ntoa_r中显式传入缓冲区长度。
并发与状态竞争
config_network_flag与ble_config_complete_flag为全局标志,配网流程与 UI/业务线程之间需保证调用顺序(start 先于 stop),SDK 内部未加锁,依赖单线程事件循环的串行性。query_sta_network_ready每次实时采样三个状态源,天然规避"缓存过期"竞争;代价是调用方需按自己的节奏轮询。- 第三方协议(米家/乐创/小度)通过
THIRD_PARTY_PROTOCOLS_SEL位域选择,同一时刻只初始化一种协议族,避免 BLE GATT 服务冲突。
性能与运维
- 内存敏感设计:配网结果使用静态缓冲区而非堆分配,SDK 在配网阶段常主动关闭 Wi-Fi(见
config_network.c中被注释掉的wifi_off()调用,注释注明"为了节省内存"),说明协议栈内存占用是 WL83 平台的硬约束。 - 诊断入口:
debug_user.c通过lwip_dhcp_bound()判断 DHCP 是否完成,配合pjsip_lwip的log_host_resolution(pj_getaddrinfoDNS 诊断)可在串口日志中定位"未关联 / 未拿到 IP / DNS 失败"三类问题。 - 裁剪原则:协议栈能力按需编译——
TCFG_PJSIP_ENABLE关闭时pjsip_lwip.c整体不参与编译;CONFIG_NET_ENABLE关闭时config_network.c不编译。链接脚本(sdk_ld_sdram.c、sdk_ld_sfc.c)中的lib_lwip_2_2_0_version标记用于版本一致性校验,升级 lwIP 版本时需同步检查。
扩展点
- 新增配网通道:在
config_network.c中仿照 Airkiss/WSC 模式,新增#ifdef CONFIG_XXX_NET_CFG分支 + static 结果结构体 + start/stop 对称初始化,并在config_network.h暴露通道 API;编译开关加在板级app_config.h。 - 第三方协议接入:通过
THIRD_PARTY_PROTOCOLS_SEL增加位定义,在config_network_start/stop中增加对应的le_net_cfg_xxx_init/exit分支。 - 自定义就绪判定:
pjsip_lwip的query_sta_network_ready是只读探针,其他业务(OTA、音视频播放)可复制该模式,自行组合lwip_get_netif_info+wifi_get_sta_connect_state构造自己的就绪门禁。 - 网络服务层:
server/net_server.h提供 TCP/UDP 服务端封装,业务注册回调即可在协议栈之上提供自定义协议;event/net_event.h的网络事件用于跨模块通知(如配网完成、IP 变更)。