/** * @file network.c * @brief 网络栈初始化与 DHCP 管理 * * 本文件是 TXW82x FPV SDK 的网络子系统初始化模块,负责: * - 初始化 LwIP TCP/IP 协议栈 * - 注册 WiFi / 以太网网络接口 * - 启动 DHCP 服务器(AP 模式下为客户端分配 IP) * - 周期性检查 DHCP 客户端状态并处理超时重试 */ /* ========================= 头文件包含 ========================= */ /* 系统配置与平台基础头文件 */ #include "sys_config.h" // 系统总体配置宏定义(外设使能、功能开关等) #include "basic_include.h" // 基础类型和宏定义(uint8 等) #include "hal/adc.h" // ADC 硬件抽象层(可能用于电源监测等) #include "lib/rpc/cpurpc.h" // CPU 间 RPC 通信库(多核通信) #include "lib/atcmd/libatcmd.h" // AT 指令解析库 #include "lib/common/atcmd.h" // AT 指令公共定义 #include "lib/umac/ieee80211.h" // IEEE 802.11 协议定义(WiFi 上层 MAC) #include "lib/lmac/lmac.h" // 低层 MAC 驱动(WiFi 下层 MAC) #include "lib/bluetooth/uble/ble_demo.h" // BLE 蓝牙低功耗演示接口 /* LwIP 协议栈相关头文件 */ #include "lwip/err.h" // LwIP 错误码定义 #include "lwip/sockets.h" // LwIP POSIX socket 兼容层 #include "lwip/netdb.h" // LwIP 网络主机数据库(DNS 解析等) #include "lwip/sys.h" // LwIP 系统抽象层(OS 适配) #include "lwip/ip_addr.h" // LwIP IP 地址操作工具 #include "lwip/tcpip.h" // LwIP TCP/IP 核心初始化接口 #include "netif/ethernetif.h" // 以太网网络接口适配层 /* 自定义网络工具与服务 */ #include "lib/net/dhcpd/dhcpd.h" // DHCP 服务器实现 #include "lib/net/utils.h" // 网络通用工具函数 #include "lib/net/skmonitor/skmonitor.h" // Socket 监控器(检测 socket 状态/异常) #include "syscfg.h" // 用户可配置参数结构体(sys_cfgs) #include "app/user_app.h" // 用户应用层接口(sys_status 等) /* ========================= 函数实现 ========================= */ /** * @brief 添加一个网络接口并注册到 LwIP 协议栈 * * 根据 devid 获取对应的网络设备,设置静态 IP 或启用 DHCP 客户端, * 然后将该网络接口注册到 LwIP 中,使其可以收发 IP 报文。 * * @param devid 设备 ID,例如 HG_WIFI0_DEVID(WiFi)或 HG_GMAC_DEVID(以太网) * @param name 接口名称字符串,如 "w0"(WiFi)、"e0"(以太网) * @param dhcpc 是否启用 DHCP 客户端:1 = 启用,0 = 使用静态 IP * @param setip 是否使用配置的静态 IP 地址:1 = 使用,0 = 使用默认(0.0.0.0) * * @note 该函数用 __init 修饰,表示仅在系统初始化阶段调用, * 运行完后其代码空间可被回收以节省 RAM。 */ __init static void sys_network_add_netif(uint8 devid, char *name, uint8 dhcpc, uint8 setip) { /* 定义 IP 地址、子网掩码、默认网关三个地址变量 */ ip_addr_t ipaddr, netmask, gw; /* 根据 devid 从设备管理器中获取对应的 netdev 结构体指针 */ struct netdev *ndev = (struct netdev *)dev_get(devid); if (ndev) { /* 从全局配置 sys_cfgs 中读取预设的静态 IP 地址参数 */ ipaddr.addr = sys_cfgs.ipaddr; // 本机 IP 地址 netmask.addr = sys_cfgs.netmask; // 子网掩码 gw.addr = sys_cfgs.gw_ip; // 默认网关地址 /* 打印接口名称和配置的 IP/掩码/网关信息,方便调试 */ os_printf(KERN_INFO"netif %s setttings: "IPSTR"/"IPSTR"/"IPSTR"!\r\n", name, IP2STR_N(ipaddr.addr), IP2STR_N(netmask.addr), IP2STR_N(gw.addr)); /** * 将该网络接口添加到 LwIP 协议栈: * - 若 setip=1,传入 IP/掩码/网关指针,使用静态地址 * - 若 setip=0,三个参数传 NULL,接口地址默认为 0.0.0.0 * (后续通过 DHCP 获取) */ lwip_netif_add(ndev, name, (setip ? &ipaddr : NULL), (setip ? &netmask : NULL), (setip ? &gw : NULL)); if (dhcpc) { /* 标记 DHCP 客户端尚未完成,供 sys_dhcpc_check() 使用 */ sys_status.dhcpc_done = 0; // 0 表示 DHCP 还未获取到地址 /* 启动该接口的 DHCP 客户端,向 DHCP 服务器请求 IP 地址 */ lwip_netif_set_dhcp(ndev, 1); // 参数 1 表示开启 DHCP /* 打印日志提示 DHCP 客户端已启动 */ os_printf(KERN_INFO"netif %s start dhcp client!\r\n", name); } } else { /* 设备 ID 无效或对应设备未注册,打印错误信息 */ os_printf(KERN_ERR"No Netdev: %d\r\n", devid); } } /** * @brief 系统网络初始化总入口 * * 依次完成以下工作: * 1. 初始化 LwIP TCP/IP 协议栈 * 2. 初始化 socket 监控器 * 3. 注册 WiFi 接口 (w0),可选启用 DHCP 客户端 * 4. 若以太网 (GMAC) 使能,注册以太网接口 (e0) * 5. 将 DHCP 客户端结果的初始值设为静态配置(作为 DHCP 失败时的回退) * * @note 由 main.c 在系统启动阶段调用,__init 修饰表示仅执行一次。 */ __init void sys_network_init(void) { /* 初始化 LwIP TCP/IP 协议栈核心,传入 NULL 表示不使用回调通知 */ tcpip_init(NULL, NULL); /* 初始化 socket 监控器,用于检测和管理 socket 连接状态 */ sock_monitor_init(); /* 注册 WiFi 接口 "w0",根据 sys_cfgs.dhcpc_en 决定是否启用 DHCP 客户端, setip=1 表示同时设置静态 IP 作为回退地址 */ sys_network_add_netif(HG_WIFI0_DEVID, "w0", sys_cfgs.dhcpc_en, 1); /* 将 "w0" 设为 LwIP 的默认网络接口(用于路由选择) */ lwip_netif_set_default2("w0"); #if GMAC_EN // 如果以太网 GMAC 功能在编译配置中启用 /* 定义 6 字节数组用于存储 MAC 地址 */ uint8_t mac[6]; /* 从 eFuse 中读取并计算 MAC 地址(每个芯片有唯一值) */ sysctrl_efuse_mac_addr_calc(mac); /** * 将 MAC 地址最后一字节最低两位置 1: * - bit0 = 1 表示本地管理地址(locally administered) * - bit1 = 1 使地址与 WiFi 接口的 MAC 区分开 */ mac[5] |= 3; /* 为以太网设备设置修改后的 MAC 地址 */ netdev_set_macaddr((struct netdev *)dev_get(HG_GMAC_DEVID), mac); /* 注册以太网接口 "e0",DHCP 设置与 WiFi 接口一致 */ sys_network_add_netif(HG_GMAC_DEVID, "e0", sys_cfgs.dhcpc_en, 1); /** * 将 "e0" 设为默认网络接口,覆盖之前 "w0" 的设置。 * 这样做是因为有线连接通常比 WiFi 更稳定可靠, * 当两者同时存在时优先使用以太网。 */ lwip_netif_set_default2("e0"); #endif /** * 将 DHCP 客户端结果的初始值设为静态配置中的 IP 参数。 * 这在 DHCP 尚未完成或失败时,确保 sys_status 中有有效的 IP 信息可用。 */ sys_status.dhcpc_result.ipaddr = sys_cfgs.ipaddr; // 默认 IP = 静态配置 sys_status.dhcpc_result.netmask = sys_cfgs.netmask; // 默认掩码 = 静态配置 sys_status.dhcpc_result.router = sys_cfgs.gw_ip; // 默认网关 = 静态配置 } /** * @brief 启动 DHCP 服务器 * * 在 WiFi AP(接入点)模式下启动 DHCP 服务器,为连接到本设备的 * 客户端(如手机、图传接收端)自动分配 IP 地址。 * * @note 仅当配置中同时满足 dhcpd_en=1 且 wifi_mode=AP 时才启动。 * 服务器运行在 "w0"(WiFi)接口上。 */ //启动DHCP服务器 // Start DHCP server(中文:启动 DHCP 服务器) void sys_dhcpd_start() { /* DHCP 服务器参数结构体 */ struct dhcpd_param param; /* 仅在 DHCP 服务器已启用 且 WiFi 工作在 AP 模式时才执行 */ if (sys_cfgs.dhcpd_en && sys_cfgs.wifi_mode == WIFI_MODE_AP) { /* 清零参数结构体,确保所有字段有确定的初始值 */ os_memset(¶m, 0, sizeof(param)); /* 从全局配置中逐一读取 DHCP 服务器参数 */ param.start_ip = sys_cfgs.dhcpd_startip; // DHCP 分配的起始 IP 地址 param.end_ip = sys_cfgs.dhcpd_endip; // DHCP 分配的结束 IP 地址 param.netmask = sys_cfgs.netmask; // 子网掩码 param.lease_time = sys_cfgs.dhcpd_lease_time; // IP 租约时间(秒) param.dns1 = sys_cfgs.dhcpd_dns1; // 主 DNS 服务器地址 param.dns2 = sys_cfgs.dhcpd_dns2; // 备用 DNS 服务器地址 param.router = sys_cfgs.dhcpd_router; // 下发給客户端的默认网关地址 /* 在 "w0"(WiFi AP)接口上启动 DHCP 服务器,失败则打印错误 */ if (dhcpd_start("w0", ¶m)) { os_printf("dhcpd start error\r\n"); // DHCP 服务器启动失败 } } } /** * @brief 周期性检查 DHCP 客户端状态 * * 该函数应在主循环或定时器中被周期性调用。 * 当 DHCP 客户端已启用但尚未获取到 IP 地址时,每调用约 7 次 * (约 7 个周期)后重新发起 DHCP 请求,防止因网络抖动导致 * DHCP 流程卡死。 * * @note 使用 static 局部变量 __loop 计数,无需外部传入计数器。 */ void sys_dhcpc_check(void) { /* 静态计数器,记录连续检查到 DHCP 未完成的次数 */ static uint8 __loop; /* 仅在 DHCP 客户端已启用 且 尚未成功获取 IP 时进入检查逻辑 */ if (sys_cfgs.dhcpc_en && !sys_status.dhcpc_done) { if (__loop++ > 6) { /** * 连续超过 6 次检查仍未完成 DHCP,认为当前 DHCP 流程可能卡死, * 重新在 "w0" 接口上发起 DHCP 请求。 */ lwip_netif_set_dhcp2("w0", 1); // 重新触发 DHCP 发现流程 /* 重置计数器,开始下一轮超时检测 */ __loop = 0; } } }