随着AI智能体的发展,skill成为智能体必不可少的一部分。之前写代码,讲究是如何让代码模块化才好复用。现在可能就得考虑如何用skill来实现代码复用。上一篇讲了在嵌入式linux下如何处理WIFI连接更省内存实现的思路,这一篇讲一下这种处理方法的skill。
我这里是使用open code来让它根据我提供的代码,来生成一个用于C编程的WIFI连接处理的skill

等了一会,他就给我生成了一个embedded-wifi-manager文件夹,包含SKILL.md文档
--- name: embedded-linux-wifi-manager description: 用于在资源受限的嵌入式 Linux 系统中,通过 C 语言直接操作内核接口(ioctl/socket)管理 Wi-Fi 连接、状态监控及自动恢复的技能。 version: 1.0.0 author: dearlong_00 --- # 🛠️ 工业级嵌入式 Linux WiFi 管理 Skill 你是一个嵌入式 Linux 系统架构师。当用户需要为低功耗、低内存的嵌入式设备(如 IPCamera、IoT 终端)开发 Wi-Fi 功能时,请使用此 Skill。 ## 核心原则 (Core Principles) 1. **零 Shell 依赖 (Zero Shell Overhead):** - **禁止**使用 `system()` 调用 shell 脚本。 - **必须**使用 `vfork()` + `exec` 或直接使用内核 API(ioctl, socket)。 - **必须**包含 `run_cmd`, `run_cmd_direct`, `run_cmd_redirect` 等轻量级执行函数。 2. **内存安全优先 (Memory Safety First):** - 在执行任何网络操作前,必须调用 `check_system_memory_ok()` 检查可用内存。 - 如果内存低于阈值(默认 600KB),跳过操作或进行分级恢复。 3. **分级恢复机制 (Graded Recovery):** - **P0 (轻量级):** 重启 DHCP 客户端 (`udhcpc`)。 - **P1 (中等级):** 重启网络接口 (ioctl SIOCSIFFLAGS)。 - **P2 (重量级):** 重载驱动模块 (rmmod/insmod) 或重启 `wpa_supplicant`。 4. **直接内核交互 (Direct Kernel Interaction):** - 获取信号强度:使用 `ioctl(SIOCGIWSTATS)` 替代 `iwconfig`。 - 获取 IP:使用 `ioctl(SIOCGIFADDR)`。 - Ping 检测:使用 Raw Socket 手动构造 ICMP 包,而非调用系统 `ping` 命令。 ## 关键组件实现规范 ### 1. 进程与命令执行 (Process & Command) 生成轻量级进程执行函数,优先使用 `vfork` 以减少内存拷贝。 ### 2. WiFi 状态机 (State Machine) 实现一个包含以下状态的监控循环: - **DISCONNECTED:** 尝试轻量级重连 (DHCP/Interface)。 - **AUTHENTICATED_NO_IP:** 检测到已认证但无 IP,触发 DHCP 修复。 - **COMPLETED:** 已连接,检查网关和公网连通性。 ### 3. 网络连通性检测 (Connectivity Check) - **网关检测:** 使用 `quick_ping` (Raw Socket) 或 TCP 连接网关 80/53 端口。 - **公网检测:** 尝试连接 `8.8.8.8:53` (DNS 端口,通常不被防火墙拦截)。 ## 代码生成逻辑 (Code Generation Logic) 当用户请求生成 "连接 Wi-Fi" 或 "Wi-Fi 守护进程" 时,请输出包含以下核心逻辑的 C 代码: ### A. 内存保护检查 (Memory Guard) // 生成此函数的逻辑:在 reset_network_full_optimized 或 restart_dhcp 中调用 if (!check_system_memory_ok(MIN_MEM_KB_FOR_OPERATION)) { // 记录日志并跳过重置 return; } ### B. 信号强度获取 (Signal Strength) // 生成此逻辑:替代解析 iwlist 输出 int wifi_get_ssid_signal_strength(int *rssi) { int fd = socket(AF_INET, SOCK_DGRAM, 0); struct iwreq ifr; // ... 初始化 ifr ... if (ioctl(fd, SIOCGIWSTATS, &ifr) == 0) { // 解析 stats.qual.level *rssi = (int8_t)stats.qual.level; // 或转换公式 } } ### C. 分级重置逻辑 (Graded Reset Logic) 请根据失败次数生成对应的恢复策略: 失败计数器 阈值 (示例) 操作 (Action) consecutive_dhcp_failures 3 restart_dhcp_optimized() consecutive_dhcp_failures 10 reset_network_full_optimized() (重载驱动) consecutive_gateway_failures 3 检查网关是否可达,否则重启接口 ### D. 完整的 Connect 流程 生成代码时,必须包含以下步骤: 写配置: 生成 wpa_supplicant.conf (包含 ssid, psk, proto)。 杀进程: 安全终止旧的 wpa_supplicant 和 udhcpc。 启模块: 必要时加载驱动模块 (insmod)。 启服务: 启动 wpa_supplicant (后台模式 -B)。 获取 IP: 启动 udhcpc。 #### E. 线程主循环 (wifi_proc) 生成一个后台线程 wifi_proc,逻辑如下: while(1) { // 1. 内存检查 if (mem < MIN_MEM_KB_CRITICAL) sleep(10); // 2. 状态检查 int status = wifi_check_connect_state(); // 3. NTP 校时 (如果连接成功) if (status) wifi_ntp_sync_if_needed(); // 4. 网络连通性检查 if (!check_gateway_connect()) handle_gateway_loss(); sleep(WIFI_PROC_SLEEP_INTERVAL); // 例如 5秒 } ##约束与常量 (Constraints) 在生成代码时,请使用以下默认常量配置(可根据目标平台微调): 内存阈值: MIN_MEM_KB_FOR_OPERATION = 600 (KB) 重试阈值: DHCP_RETRY_THRESHOLD = 8 重置阈值: FULL_RESET_THRESHOLD = 10 网络接口: HW_NAME = "wlan0" DHCP 客户端: udhcpc ##示例 用户输入: 请为我的嵌入式 Linux 设备生成一个 Wi-Fi 管理模块,要求能自动重连,并且在内存不足时不要崩溃。 你的回应: 你将生成一个包含 run_cmd, wifi_connect_wifi, wifi_proc 等函数的 C 源文件。代码将严格遵循上述“核心原则”,使用 vfork 启动进程,并在每次重置前检查内存。
在workbuddy中,添加这个技能,然后对他说“请使用 `embedded-linux-wifi-manager` Skill,为我生成一个针对 RTL8189FTV 芯片的 Wi-Fi 配置代码”,他就开始工作了

生成的代码如下:
/**
* @file wifi_rtl8189ftv.c
* @brief RTL8189FTV Wi-Fi 管理模块实现
*
* 核心设计原则:
* 1. 零 system() shell 调用 —— 全部使用 vfork()+exec 或 ioctl
* 2. 内存保护优先 —— 每次重置前检查可用内存
* 3. 分级恢复 —— P0(DHCP) → P1(接口) → P2(驱动重载)
* 4. 直接内核交互 —— ioctl(SIOCGIWSTATS) 获取信号强度
*
* 编译示例:
* arm-linux-gnueabihf-gcc -O2 -Wall \
* -I. wifi_rtl8189ftv.c -o wifi_mgr -lpthread
*/
#include "wifi_rtl8189ftv.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <linux/wireless.h>
/* =========================================================
* 内部全局状态
* ========================================================= */
static wifi_config_t g_wifi_cfg; /* 当前连接配置 */
static wifi_stats_t g_wifi_stats; /* 运行统计信息 */
static pthread_t g_wifi_thread; /* 守护线程 */
static volatile int g_wifi_proc_running; /* 线程运行标志 */
static pthread_mutex_t g_stats_mutex = PTHREAD_MUTEX_INITIALIZER;
static time_t g_last_ntp_sync; /* 上次 NTP 同步时间戳 */
/* =========================================================
* 日志宏(可替换为项目日志框架)
* ========================================================= */
#define LOG_TAG "wifi_rtl8189ftv"
#define LOGI(fmt, ...) fprintf(stdout, "[%s][I] " fmt "\n", LOG_TAG, ##__VA_ARGS__)
#define LOGE(fmt, ...) fprintf(stderr, "[%s][E] " fmt "\n", LOG_TAG, ##__VA_ARGS__)
#define LOGW(fmt, ...) fprintf(stdout, "[%s][W] " fmt "\n", LOG_TAG, ##__VA_ARGS__)
/* =========================================================
* Section 1: 进程执行(vfork + exec,零 shell)
* ========================================================= */
/**
* @brief 使用 vfork()+execv 执行命令并等待退出(无超时)
* @return 子进程退出码,失败返回 -1
*/
int run_cmd(const char *path, char *const argv[]) {
pid_t pid = vfork();
if (pid < 0) {
LOGE("vfork failed: %s", strerror(errno));
return -1;
}
if (pid == 0) {
/* 子进程:重定向 stdout/stderr 到 /dev/null 避免日志污染 */
int devnull = open("/dev/null", O_WRONLY);
if (devnull >= 0) {
dup2(devnull, STDOUT_FILENO);
dup2(devnull, STDERR_FILENO);
close(devnull);
}
execv(path, argv);
_exit(127); /* exec 失败 */
}
int status = 0;
waitpid(pid, &status, 0);
return WIFEXITED(status) ? WEXITSTATUS(status) : -1;
}
/**
* @brief 执行命令,带超时(秒)。超时后 SIGKILL 子进程。
* @return 子进程退出码;超时返回 -2;失败返回 -1
*/
int run_cmd_timeout(const char *path, char *const argv[], int timeout_sec) {
pid_t pid = vfork();
if (pid < 0) return -1;
if (pid == 0) {
int devnull = open("/dev/null", O_WRONLY);
if (devnull >= 0) {
dup2(devnull, STDOUT_FILENO);
dup2(devnull, STDERR_FILENO);
close(devnull);
}
execv(path, argv);
_exit(127);
}
time_t deadline = time(NULL) + timeout_sec;
while (1) {
int status = 0;
pid_t ret = waitpid(pid, &status, WNOHANG);
if (ret == pid) {
return WIFEXITED(status) ? WEXITSTATUS(status) : -1;
}
if (time(NULL) >= deadline) {
kill(pid, SIGKILL);
waitpid(pid, NULL, 0);
LOGW("run_cmd_timeout: [%s] timed out after %ds", path, timeout_sec);
return -2;
}
usleep(200000); /* 200ms 轮询 */
}
}
/**
* @brief 执行命令,将 stdout 重定向到文件
* @return 子进程退出码,失败返回 -1
*/
int run_cmd_redirect(const char *path, char *const argv[], const char *out_file) {
pid_t pid = vfork();
if (pid < 0) return -1;
if (pid == 0) {
int fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd >= 0) {
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
}
execv(path, argv);
_exit(127);
}
int status = 0;
waitpid(pid, &status, 0);
return WIFEXITED(status) ? WEXITSTATUS(status) : -1;
}
/**
* @brief 通过 PID 文件终止进程
*/
void kill_process_by_pidfile(const char *pid_file) {
FILE *fp = fopen(pid_file, "r");
if (!fp) return;
int pid = 0;
fscanf(fp, "%d", &pid);
fclose(fp);
if (pid > 1) {
kill(pid, SIGTERM);
usleep(300000);
kill(pid, SIGKILL); /* 确保杀死 */
}
unlink(pid_file);
}
/**
* @brief 通过进程名终止所有同名进程(使用 killall 路径,无 shell)
*/
void kill_process_by_name(const char *name) {
char *argv[] = { (char*)"/usr/bin/killall", "-9", (char*)name, NULL };
run_cmd("/usr/bin/killall", argv);
}
/* =========================================================
* Section 2: 内存保护检查
* ========================================================= */
/**
* @brief 从 /proc/meminfo 读取 MemAvailable(KB)
* @return 可用内存(KB),读取失败返回 -1
*/
int get_free_memory_kb(void) {
FILE *fp = fopen("/proc/meminfo", "r");
if (!fp) return -1;
char line[128];
int available_kb = -1;
while (fgets(line, sizeof(line), fp)) {
if (strncmp(line, "MemAvailable:", 13) == 0) {
sscanf(line + 13, "%d", &available_kb);
break;
}
}
fclose(fp);
/* 部分旧内核无 MemAvailable,回退到 MemFree */
if (available_kb < 0) {
fp = fopen("/proc/meminfo", "r");
if (fp) {
while (fgets(line, sizeof(line), fp)) {
if (strncmp(line, "MemFree:", 8) == 0) {
sscanf(line + 8, "%d", &available_kb);
break;
}
}
fclose(fp);
}
}
return available_kb;
}
/**
* @brief 检查系统内存是否满足操作最低要求
* @param min_kb 最小可用内存阈值(KB)
* @return true=内存充足,false=内存不足(应跳过操作)
*/
bool check_system_memory_ok(int min_kb) {
int free_kb = get_free_memory_kb();
if (free_kb < 0) {
LOGW("check_system_memory_ok: failed to read meminfo, assuming OK");
return true;
}
if (free_kb < min_kb) {
LOGW("check_system_memory_ok: low memory %dKB < %dKB threshold, skip",
free_kb, min_kb);
return false;
}
return true;
}
/* =========================================================
* Section 3: RTL8189FTV 驱动管理
* ========================================================= */
/**
* @brief 检测 RTL8189FTV 驱动是否已加载
* @return true=已加载
*/
bool rtl8189ftv_is_driver_loaded(void) {
/* 通过 /proc/modules 判断 */
FILE *fp = fopen("/proc/modules", "r");
if (!fp) return false;
char line[256];
bool found = false;
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, RTL8189FTV_MODULE_NAME)) {
found = true;
break;
}
}
fclose(fp);
return found;
}
/**
* @brief 加载 RTL8189FTV 驱动(insmod)
* @return 0=成功,-1=失败
*/
int rtl8189ftv_load_driver(void) {
if (rtl8189ftv_is_driver_loaded()) {
LOGI("rtl8189ftv driver already loaded");
return 0;
}
/* 检查 .ko 文件是否存在 */
if (access(RTL8189FTV_KO_PATH, F_OK) != 0) {
LOGE("rtl8189ftv: ko not found at %s", RTL8189FTV_KO_PATH);
return -1;
}
char *argv[] = { (char*)"/sbin/insmod", (char*)RTL8189FTV_KO_PATH, NULL };
int ret = run_cmd_timeout("/sbin/insmod", argv, 10);
if (ret != 0) {
LOGE("rtl8189ftv: insmod failed (ret=%d)", ret);
return -1;
}
/* 等待接口出现,最多 5 秒 */
for (int i = 0; i < 50; i++) {
if (iface_exists(HW_NAME)) {
LOGI("rtl8189ftv: driver loaded, iface %s ready", HW_NAME);
return 0;
}
usleep(100000); /* 100ms */
}
LOGE("rtl8189ftv: driver loaded but iface %s not appeared", HW_NAME);
return -1;
}
/**
* @brief 卸载 RTL8189FTV 驱动(rmmod)
* @return 0=成功,-1=失败
*/
int rtl8189ftv_unload_driver(void) {
if (!rtl8189ftv_is_driver_loaded()) return 0;
char *argv[] = { (char*)"/sbin/rmmod", (char*)RTL8189FTV_MODULE_NAME, NULL };
int ret = run_cmd_timeout("/sbin/rmmod", argv, 10);
if (ret != 0) {
LOGE("rtl8189ftv: rmmod failed (ret=%d)", ret);
return -1;
}
usleep(500000); /* 等待 500ms 让内核释放资源 */
LOGI("rtl8189ftv: driver unloaded");
return 0;
}
/**
* @brief 重载驱动(unload + load)
* @return 0=成功,-1=失败
*/
int rtl8189ftv_reload_driver(void) {
LOGI("rtl8189ftv: reloading driver...");
rtl8189ftv_unload_driver();
sleep(1);
return rtl8189ftv_load_driver();
}
/* =========================================================
* Section 4: 网络接口操作(ioctl)
* ========================================================= */
/**
* @brief 创建 ioctl 用的临时 socket(使用后必须 close)
*/
static int _open_ioctl_sock(void) {
return socket(AF_INET, SOCK_DGRAM, 0);
}
/**
* @brief 检测网络接口是否存在
*/
bool iface_exists(const char *ifname) {
int fd = _open_ioctl_sock();
if (fd < 0) return false;
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
bool exists = (ioctl(fd, SIOCGIFINDEX, &ifr) == 0);
close(fd);
return exists;
}
/**
* @brief 启动网络接口(IFF_UP)
*/
int iface_up(const char *ifname) {
int fd = _open_ioctl_sock();
if (fd < 0) return -1;
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { close(fd); return -1; }
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
int ret = ioctl(fd, SIOCSIFFLAGS, &ifr);
close(fd);
if (ret < 0) LOGE("iface_up(%s) failed: %s", ifname, strerror(errno));
return ret;
}
/**
* @brief 关闭网络接口(清除 IFF_UP)
*/
int iface_down(const char *ifname) {
int fd = _open_ioctl_sock();
if (fd < 0) return -1;
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { close(fd); return -1; }
ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
int ret = ioctl(fd, SIOCSIFFLAGS, &ifr);
close(fd);
return ret;
}
/* =========================================================
* Section 5: Wi-Fi 状态检测(WEXT ioctl)
* ========================================================= */
/**
* @brief 通过 ioctl(SIOCGIWSTATS) 获取信号强度(dBm)
* RTL8189FTV 驱动完整支持 WEXT,此方法可靠
* @param rssi_dbm 输出参数,信号强度(负数 dBm)
* @return 0=成功,-1=失败(接口不存在或未关联)
*/
int wifi_get_rssi(int *rssi_dbm) {
int fd = _open_ioctl_sock();
if (fd < 0) return -1;
struct iwreq iwr;
struct iw_statistics stats;
memset(&iwr, 0, sizeof(iwr));
memset(&stats, 0, sizeof(stats));
strncpy(iwr.ifr_name, HW_NAME, IFNAMSIZ - 1);
iwr.u.data.pointer = &stats;
iwr.u.data.length = sizeof(stats);
iwr.u.data.flags = 1; /* 清除统计标志 */
if (ioctl(fd, SIOCGIWSTATS, &iwr) < 0) {
close(fd);
return -1;
}
close(fd);
/*
* RTL8189FTV 驱动报告的 qual.level 为无符号 dBm+256 偏移
* 转换规则:level >= 64 时,rssi = level - 256
* level < 64 时,rssi = level (罕见)
*/
uint8_t raw_level = (uint8_t)stats.qual.level;
*rssi_dbm = (raw_level >= 64) ? (int)raw_level - 256 : (int)raw_level;
return 0;
}
/**
* @brief 通过 ioctl(SIOCGIFADDR) 获取当前 IP 地址
*/
bool wifi_get_ip(char *ip_buf, int buf_len) {
int fd = _open_ioctl_sock();
if (fd < 0) return false;
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, HW_NAME, IFNAMSIZ - 1);
if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
close(fd);
return false;
}
close(fd);
struct sockaddr_in *addr = (struct sockaddr_in *)&ifr.ifr_addr;
const char *ip_str = inet_ntoa(addr->sin_addr);
if (!ip_str || strcmp(ip_str, "0.0.0.0") == 0) return false;
strncpy(ip_buf, ip_str, buf_len - 1);
ip_buf[buf_len - 1] = '\0';
return true;
}
/**
* @brief 从 /proc/net/route 获取默认网关 IP
*/
bool wifi_get_gateway(char *gw_buf, int buf_len) {
FILE *fp = fopen("/proc/net/route", "r");
if (!fp) return false;
char line[256], iface[16];
uint32_t dest, gw, mask;
bool found = false;
fgets(line, sizeof(line), fp); /* 跳过表头 */
while (fgets(line, sizeof(line), fp)) {
if (sscanf(line, "%15s %x %x %*s %*s %*s %*s %x",
iface, &dest, &gw, &mask) == 4) {
if (dest == 0 && strcmp(iface, HW_NAME) == 0) {
struct in_addr addr;
addr.s_addr = gw;
strncpy(gw_buf, inet_ntoa(addr), buf_len - 1);
gw_buf[buf_len - 1] = '\0';
found = true;
break;
}
}
}
fclose(fp);
return found;
}
/**
* @brief 检查 wpa_supplicant 是否已完成认证(COMPLETED 状态)
* 通过读取 /proc/net/wireless 判断是否有活跃连接
* (更可靠的方式是 wpa_cli status,此处用轻量 proc 方式)
*/
bool wifi_is_wpa_completed(void) {
/* 方法:检查 /proc/net/wireless 中 wlan0 的 link quality > 0 */
FILE *fp = fopen("/proc/net/wireless", "r");
if (!fp) return false;
char line[256];
fgets(line, sizeof(line), fp); /* 跳过 2 行表头 */
fgets(line, sizeof(line), fp);
bool connected = false;
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, HW_NAME)) {
/* 格式:iface: status link level noise ... */
int status = 0, link = 0;
char iface[16];
sscanf(line, "%15[^:]: %d %d", iface, &status, &link);
connected = (link > 0);
break;
}
}
fclose(fp);
return connected;
}
/* =========================================================
* Section 6: 网络连通性检测(Raw Socket ICMP)
* ========================================================= */
/**
* @brief ICMP 校验和计算
*/
static uint16_t _icmp_checksum(const void *data, size_t len) {
const uint16_t *p = (const uint16_t *)data;
uint32_t sum = 0;
while (len > 1) { sum += *p++; len -= 2; }
if (len) sum += *(const uint8_t *)p;
while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16);
return (uint16_t)~sum;
}
/**
* @brief 快速 ICMP Ping(Raw Socket,不调用系统 ping 命令)
* @param ip_str 目标 IP(字符串)
* @param timeout_ms 超时时间(毫秒)
* @return true=可达,false=不可达
*/
static bool _quick_ping(const char *ip_str, int timeout_ms) {
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sock < 0) {
/* Raw socket 需要 root 权限;无权限时回退到 TCP 探测 */
goto fallback_tcp;
}
/* 设置接收超时 */
struct timeval tv = { timeout_ms / 1000, (timeout_ms % 1000) * 1000 };
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
struct sockaddr_in dest;
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
inet_aton(ip_str, &dest.sin_addr);
/* 构造 ICMP Echo Request */
struct {
struct icmphdr hdr;
char payload[16];
} pkt;
memset(&pkt, 0, sizeof(pkt));
pkt.hdr.type = ICMP_ECHO;
pkt.hdr.code = 0;
pkt.hdr.un.echo.id = (uint16_t)getpid();
pkt.hdr.un.echo.sequence = 1;
memcpy(pkt.payload, "wifi_probe_ping", 15);
pkt.hdr.checksum = _icmp_checksum(&pkt, sizeof(pkt));
if (sendto(sock, &pkt, sizeof(pkt), 0,
(struct sockaddr *)&dest, sizeof(dest)) <= 0) {
close(sock);
goto fallback_tcp;
}
char recv_buf[128];
struct sockaddr_in from;
socklen_t from_len = sizeof(from);
ssize_t n = recvfrom(sock, recv_buf, sizeof(recv_buf), 0,
(struct sockaddr *)&from, &from_len);
close(sock);
return (n > 0);
fallback_tcp:
/* Fallback:TCP 连接目标 53 端口(DNS),探测连通性 */
{
int tcp_sock = socket(AF_INET, SOCK_STREAM, 0);
if (tcp_sock < 0) return false;
struct timeval tv2 = { timeout_ms / 1000, (timeout_ms % 1000) * 1000 };
setsockopt(tcp_sock, SOL_SOCKET, SO_SNDTIMEO, &tv2, sizeof(tv2));
setsockopt(tcp_sock, SOL_SOCKET, SO_RCVTIMEO, &tv2, sizeof(tv2));
/* 设置非阻塞以便超时控制 */
int flags = fcntl(tcp_sock, F_GETFL, 0);
fcntl(tcp_sock, F_SETFL, flags | O_NONBLOCK);
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(PING_TARGET_PORT);
inet_aton(ip_str, &sa.sin_addr);
connect(tcp_sock, (struct sockaddr *)&sa, sizeof(sa));
fd_set wset;
FD_ZERO(&wset);
FD_SET(tcp_sock, &wset);
struct timeval tv3 = { timeout_ms / 1000, (timeout_ms % 1000) * 1000 };
bool reachable = (select(tcp_sock + 1, NULL, &wset, NULL, &tv3) > 0);
close(tcp_sock);
return reachable;
}
}
/**
* @brief 检测默认网关是否可达
*/
bool check_gateway_reachable(const char *gw_ip) {
if (!gw_ip || gw_ip[0] == '\0') return false;
return _quick_ping(gw_ip, PING_TIMEOUT_MS);
}
/**
* @brief 检测公网连通性(8.8.8.8:53)
*/
bool check_internet_reachable(void) {
return _quick_ping(PING_TARGET_PUBLIC, PING_TIMEOUT_MS);
}
/* =========================================================
* Section 7: wpa_supplicant 管理
* ========================================================= */
/**
* @brief 写入 wpa_supplicant.conf 配置文件
* 支持 WPA/WPA2/WPA3 和开放网络
*/
int wpa_write_config(const wifi_config_t *cfg) {
FILE *fp = fopen(WPA_CONF_PATH, "w");
if (!fp) {
LOGE("wpa_write_config: cannot open %s", WPA_CONF_PATH);
return -1;
}
fprintf(fp, "ctrl_interface=%s\n", WPA_CTRL_PATH);
fprintf(fp, "ctrl_interface_group=0\n");
fprintf(fp, "update_config=1\n\n");
fprintf(fp, "network={\n");
fprintf(fp, " ssid=\"%s\"\n", cfg->ssid);
if (cfg->bssid[0] != '\0') {
fprintf(fp, " bssid=%s\n", cfg->bssid);
}
if (cfg->password[0] == '\0') {
/* 开放网络 */
fprintf(fp, " key_mgmt=NONE\n");
} else {
fprintf(fp, " psk=\"%s\"\n", cfg->password);
switch (cfg->proto) {
case 1:
fprintf(fp, " proto=WPA\n");
fprintf(fp, " key_mgmt=WPA-PSK\n");
fprintf(fp, " pairwise=CCMP TKIP\n");
fprintf(fp, " group=CCMP TKIP\n");
break;
case 3: /* WPA3-SAE */
fprintf(fp, " proto=RSN\n");
fprintf(fp, " key_mgmt=SAE\n");
fprintf(fp, " pairwise=CCMP\n");
fprintf(fp, " group=CCMP\n");
break;
case 2:
default: /* WPA2(推荐 RTL8189FTV 默认使用) */
fprintf(fp, " proto=RSN\n");
fprintf(fp, " key_mgmt=WPA-PSK\n");
fprintf(fp, " pairwise=CCMP\n");
fprintf(fp, " group=CCMP TKIP\n");
break;
}
}
fprintf(fp, " scan_ssid=1\n"); /* 支持隐藏 SSID */
fprintf(fp, "}\n");
fclose(fp);
LOGI("wpa_write_config: ssid=[%s] proto=%d", cfg->ssid, cfg->proto);
return 0;
}
/**
* @brief 启动 wpa_supplicant(后台模式 -B)
* @return 0=成功,-1=失败
*/
int wpa_start(void) {
/* 确保控制目录存在 */
mkdir(WPA_CTRL_PATH, 0700);
char *argv[] = {
(char*)WPA_SUPPLICANT_BIN,
(char*)"-B", /* 后台运行 */
(char*)"-D", (char*)"nl80211,wext", /* RTL8189FTV 优先 nl80211 */
(char*)"-i", (char*)HW_NAME,
(char*)"-c", (char*)WPA_CONF_PATH,
(char*)"-P", (char*)WPA_PID_FILE,
NULL
};
int ret = run_cmd_timeout(WPA_SUPPLICANT_BIN, argv, 10);
if (ret != 0) {
LOGE("wpa_start: failed (ret=%d)", ret);
return -1;
}
LOGI("wpa_start: wpa_supplicant launched");
return 0;
}
/**
* @brief 停止 wpa_supplicant
*/
void wpa_stop(void) {
kill_process_by_pidfile(WPA_PID_FILE);
kill_process_by_name("wpa_supplicant");
unlink(WPA_CONF_PATH);
}
/**
* @brief 等待 wpa_supplicant 完成认证(轮询 /proc/net/wireless)
* @param timeout_sec 超时秒数
* @return 0=已连接,-1=超时
*/
int wpa_wait_connected(int timeout_sec) {
time_t deadline = time(NULL) + timeout_sec;
while (time(NULL) < deadline) {
if (wifi_is_wpa_completed()) {
LOGI("wpa_wait_connected: associated");
return 0;
}
sleep(1);
}
LOGE("wpa_wait_connected: timeout after %ds", timeout_sec);
return -1;
}
/* =========================================================
* Section 8: DHCP 管理
* ========================================================= */
/**
* @brief 启动 udhcpc 获取 IP
* @return 0=成功,-1=失败
*/
int dhcp_start(void) {
char *argv[] = {
(char*)UDHCPC_BIN,
(char*)"-i", (char*)HW_NAME,
(char*)"-p", (char*)UDHCPC_PID_FILE,
(char*)"-t", (char*)"5", /* 重试 5 次 */
(char*)"-T", (char*)"3", /* 每次 3 秒超时 */
(char*)"-n", /* 获取失败则退出(非守护模式) */
NULL
};
int ret = run_cmd_timeout(UDHCPC_BIN, argv, DHCP_TIMEOUT_SEC);
if (ret != 0) {
LOGE("dhcp_start: udhcpc failed (ret=%d)", ret);
return -1;
}
LOGI("dhcp_start: DHCP obtained");
return 0;
}
/**
* @brief 停止 udhcpc 并释放 IP
*/
void dhcp_stop(void) {
kill_process_by_pidfile(UDHCPC_PID_FILE);
kill_process_by_name("udhcpc");
}
/**
* @brief 向 udhcpc 发送 SIGUSR1 触发 DHCP renew
*/
int dhcp_renew(void) {
FILE *fp = fopen(UDHCPC_PID_FILE, "r");
if (!fp) {
/* PID 文件不存在,重新启动 DHCP */
return dhcp_start();
}
int pid = 0;
fscanf(fp, "%d", &pid);
fclose(fp);
if (pid > 1) {
kill(pid, SIGUSR1);
LOGI("dhcp_renew: sent SIGUSR1 to udhcpc pid=%d", pid);
return 0;
}
return dhcp_start();
}
/* =========================================================
* Section 9: 高层 Wi-Fi 连接接口
* ========================================================= */
/**
* @brief 完整的 Wi-Fi 连接流程
* 步骤: 写配置 → 杀旧进程 → 加载驱动 → 接口 UP →
* 启 wpa → 等待认证 → 启 DHCP → 检查 IP
* @return 0=成功,-1=失败
*/
int wifi_connect(const wifi_config_t *cfg) {
if (!cfg || cfg->ssid[0] == '\0') return -1;
/* 内存检查 */
if (!check_system_memory_ok(MIN_MEM_KB_FOR_OPERATION)) {
LOGE("wifi_connect: insufficient memory, abort");
return -1;
}
memcpy(&g_wifi_cfg, cfg, sizeof(g_wifi_cfg));
pthread_mutex_lock(&g_stats_mutex);
g_wifi_stats.state = WIFI_STATE_CONNECTING;
pthread_mutex_unlock(&g_stats_mutex);
LOGI("wifi_connect: connecting to [%s]", cfg->ssid);
/* Step 1: 写 wpa_supplicant.conf */
if (wpa_write_config(cfg) != 0) return -1;
/* Step 2: 杀掉旧的 wpa_supplicant 和 udhcpc */
dhcp_stop();
wpa_stop();
usleep(500000);
/* Step 3: 确保驱动已加载(RTL8189FTV 特有) */
if (rtl8189ftv_load_driver() != 0) {
LOGE("wifi_connect: driver load failed");
return -1;
}
/* Step 4: 接口 UP */
if (iface_up(HW_NAME) != 0) {
LOGE("wifi_connect: iface_up(%s) failed", HW_NAME);
return -1;
}
usleep(300000);
/* Step 5: 启动 wpa_supplicant */
if (wpa_start() != 0) return -1;
/* Step 6: 等待认证完成 */
if (wpa_wait_connected(WIFI_CONNECT_TIMEOUT_SEC) != 0) {
LOGE("wifi_connect: authentication timeout");
pthread_mutex_lock(&g_stats_mutex);
g_wifi_stats.state = WIFI_STATE_DISCONNECTED;
pthread_mutex_unlock(&g_stats_mutex);
return -1;
}
pthread_mutex_lock(&g_stats_mutex);
g_wifi_stats.state = WIFI_STATE_AUTHENTICATED_NO_IP;
pthread_mutex_unlock(&g_stats_mutex);
/* Step 7: 启动 DHCP 获取 IP */
if (dhcp_start() != 0) {
LOGE("wifi_connect: DHCP failed");
pthread_mutex_lock(&g_stats_mutex);
g_wifi_stats.consecutive_dhcp_failures++;
g_wifi_stats.state = WIFI_STATE_AUTHENTICATED_NO_IP;
pthread_mutex_unlock(&g_stats_mutex);
return -1;
}
/* Step 8: 验证 IP 已获取 */
char ip[16] = {0};
if (!wifi_get_ip(ip, sizeof(ip))) {
LOGE("wifi_connect: got no IP after DHCP");
return -1;
}
pthread_mutex_lock(&g_stats_mutex);
strncpy(g_wifi_stats.ip, ip, sizeof(g_wifi_stats.ip) - 1);
g_wifi_stats.state = WIFI_STATE_CONNECTED;
g_wifi_stats.consecutive_dhcp_failures = 0;
g_wifi_stats.total_reconnects++;
pthread_mutex_unlock(&g_stats_mutex);
LOGI("wifi_connect: connected! IP=%s", ip);
return 0;
}
/**
* @brief 断开 Wi-Fi 连接并清理资源
*/
void wifi_disconnect(void) {
dhcp_stop();
wpa_stop();
iface_down(HW_NAME);
pthread_mutex_lock(&g_stats_mutex);
memset(g_wifi_stats.ip, 0, sizeof(g_wifi_stats.ip));
g_wifi_stats.state = WIFI_STATE_DISCONNECTED;
pthread_mutex_unlock(&g_stats_mutex);
LOGI("wifi_disconnect: done");
}
wifi_state_t wifi_get_state(void) {
pthread_mutex_lock(&g_stats_mutex);
wifi_state_t s = g_wifi_stats.state;
pthread_mutex_unlock(&g_stats_mutex);
return s;
}
void wifi_get_stats(wifi_stats_t *stats) {
pthread_mutex_lock(&g_stats_mutex);
memcpy(stats, &g_wifi_stats, sizeof(wifi_stats_t));
pthread_mutex_unlock(&g_stats_mutex);
/* 实时更新信号强度 */
wifi_get_rssi(&stats->rssi);
}
/* =========================================================
* Section 10: 分级恢复
* ========================================================= */
/**
* @brief P0 恢复:仅重启 DHCP(最轻量,不中断连接)
*/
void wifi_recovery_p0_dhcp(void) {
if (!check_system_memory_ok(MIN_MEM_KB_FOR_OPERATION)) {
LOGW("P0 recovery: skipped due to low memory");
return;
}
LOGI("Wi-Fi recovery P0: restart DHCP");
dhcp_stop();
usleep(200000);
int ret = dhcp_start();
pthread_mutex_lock(&g_stats_mutex);
if (ret == 0) {
g_wifi_stats.consecutive_dhcp_failures = 0;
char ip[16] = {0};
wifi_get_ip(ip, sizeof(ip));
strncpy(g_wifi_stats.ip, ip, sizeof(g_wifi_stats.ip) - 1);
} else {
g_wifi_stats.consecutive_dhcp_failures++;
}
pthread_mutex_unlock(&g_stats_mutex);
}
/**
* @brief P1 恢复:重启网络接口(iface down + up),不重载驱动
*/
void wifi_recovery_p1_interface(void) {
if (!check_system_memory_ok(MIN_MEM_KB_FOR_OPERATION)) {
LOGW("P1 recovery: skipped due to low memory");
return;
}
LOGI("Wi-Fi recovery P1: restart interface %s", HW_NAME);
dhcp_stop();
wpa_stop();
iface_down(HW_NAME);
sleep(1);
iface_up(HW_NAME);
usleep(500000);
wpa_start();
wpa_wait_connected(WIFI_CONNECT_TIMEOUT_SEC);
dhcp_start();
}
/**
* @brief P2 恢复:重载驱动模块 + 完整重连(最重量级)
*/
void wifi_recovery_p2_full_reset(void) {
if (!check_system_memory_ok(MIN_MEM_KB_FOR_OPERATION)) {
LOGW("P2 recovery: skipped due to low memory");
return;
}
LOGI("Wi-Fi recovery P2: reload RTL8189FTV driver + full reconnect");
dhcp_stop();
wpa_stop();
iface_down(HW_NAME);
sleep(1);
rtl8189ftv_reload_driver();
sleep(1);
wifi_connect(&g_wifi_cfg);
pthread_mutex_lock(&g_stats_mutex);
g_wifi_stats.consecutive_dhcp_failures = 0;
g_wifi_stats.consecutive_gw_failures = 0;
pthread_mutex_unlock(&g_stats_mutex);
}
/* =========================================================
* Section 11: NTP 时间同步(简化实现,用 ntpdate)
* ========================================================= */
#define NTP_SYNC_INTERVAL_SEC 3600 /* 每小时同步一次 */
#define NTP_SERVER "pool.ntp.org"
void wifi_ntp_sync_if_needed(void) {
time_t now = time(NULL);
if (now - g_last_ntp_sync < NTP_SYNC_INTERVAL_SEC) return;
char *argv[] = { (char*)"/usr/sbin/ntpdate", (char*)"-u",
(char*)NTP_SERVER, NULL };
int ret = run_cmd_timeout("/usr/sbin/ntpdate", argv, 10);
if (ret == 0) {
g_last_ntp_sync = now;
LOGI("NTP sync OK");
} else {
LOGW("NTP sync failed (ret=%d)", ret);
}
}
/* =========================================================
* Section 12: Wi-Fi 守护线程(wifi_proc)
* ========================================================= */
/**
* @brief Wi-Fi 状态机主循环(后台 pthread)
*
* 逻辑流程:
* 1. 内存检查(极低内存则休眠等待)
* 2. 检查 wpa 认证状态
* 3. 检查 IP
* 4. 检查网关连通性
* 5. 检查公网连通性
* 6. 按失败计数触发分级恢复
* 7. NTP 同步
*/
void *wifi_proc(void *arg) {
(void)arg;
LOGI("wifi_proc: thread started");
while (g_wifi_proc_running) {
/* ---- 1. 临界内存检查 ---- */
int mem_kb = get_free_memory_kb();
if (mem_kb >= 0 && mem_kb < MIN_MEM_KB_CRITICAL) {
LOGW("wifi_proc: critical low memory %dKB, sleeping 10s", mem_kb);
sleep(10);
continue;
}
/* ---- 2. WPA 认证状态检查 ---- */
bool wpa_ok = wifi_is_wpa_completed();
if (!wpa_ok) {
LOGI("wifi_proc: WPA not connected, triggering reconnect");
pthread_mutex_lock(&g_stats_mutex);
g_wifi_stats.state = WIFI_STATE_DISCONNECTED;
int dhcp_fails = g_wifi_stats.consecutive_dhcp_failures;
pthread_mutex_unlock(&g_stats_mutex);
if (dhcp_fails >= FULL_RESET_THRESHOLD) {
wifi_recovery_p2_full_reset();
} else {
wifi_connect(&g_wifi_cfg);
}
sleep(WIFI_PROC_SLEEP_INTERVAL);
continue;
}
/* ---- 3. IP 检查 ---- */
char ip[16] = {0};
if (!wifi_get_ip(ip, sizeof(ip))) {
LOGI("wifi_proc: authenticated but no IP");
pthread_mutex_lock(&g_stats_mutex);
g_wifi_stats.state = WIFI_STATE_AUTHENTICATED_NO_IP;
g_wifi_stats.consecutive_dhcp_failures++;
int fails = g_wifi_stats.consecutive_dhcp_failures;
pthread_mutex_unlock(&g_stats_mutex);
if (fails >= FULL_RESET_THRESHOLD) {
wifi_recovery_p2_full_reset();
} else if (fails >= DHCP_RETRY_THRESHOLD) {
wifi_recovery_p1_interface();
} else {
wifi_recovery_p0_dhcp();
}
sleep(WIFI_PROC_SLEEP_INTERVAL);
continue;
}
/* 更新 IP */
pthread_mutex_lock(&g_stats_mutex);
strncpy(g_wifi_stats.ip, ip, sizeof(g_wifi_stats.ip) - 1);
g_wifi_stats.consecutive_dhcp_failures = 0;
pthread_mutex_unlock(&g_stats_mutex);
/* ---- 4. 网关连通性检查 ---- */
char gw[16] = {0};
bool gw_ok = false;
if (wifi_get_gateway(gw, sizeof(gw))) {
gw_ok = check_gateway_reachable(gw);
pthread_mutex_lock(&g_stats_mutex);
strncpy(g_wifi_stats.gateway, gw, sizeof(g_wifi_stats.gateway) - 1);
pthread_mutex_unlock(&g_stats_mutex);
}
if (!gw_ok) {
pthread_mutex_lock(&g_stats_mutex);
g_wifi_stats.consecutive_gw_failures++;
int gw_fails = g_wifi_stats.consecutive_gw_failures;
pthread_mutex_unlock(&g_stats_mutex);
LOGW("wifi_proc: gateway unreachable (count=%d)", gw_fails);
if (gw_fails >= GW_FAIL_THRESHOLD) {
wifi_recovery_p1_interface();
}
sleep(WIFI_PROC_SLEEP_INTERVAL);
continue;
}
/* 网关正常,重置计数 */
pthread_mutex_lock(&g_stats_mutex);
g_wifi_stats.consecutive_gw_failures = 0;
pthread_mutex_unlock(&g_stats_mutex);
/* ---- 5. 公网连通性检查 ---- */
bool internet_ok = check_internet_reachable();
pthread_mutex_lock(&g_stats_mutex);
g_wifi_stats.state = internet_ok ? WIFI_STATE_CONNECTED
: WIFI_STATE_NO_INTERNET;
pthread_mutex_unlock(&g_stats_mutex);
if (!internet_ok) {
LOGW("wifi_proc: internet unreachable (LAN only)");
}
/* ---- 6. 更新信号强度 ---- */
int rssi = 0;
if (wifi_get_rssi(&rssi) == 0) {
pthread_mutex_lock(&g_stats_mutex);
g_wifi_stats.rssi = rssi;
pthread_mutex_unlock(&g_stats_mutex);
}
/* ---- 7. NTP 校时 ---- */
if (internet_ok) {
wifi_ntp_sync_if_needed();
}
sleep(WIFI_PROC_SLEEP_INTERVAL);
}
LOGI("wifi_proc: thread exiting");
return NULL;
}
/**
* @brief 启动 Wi-Fi 守护线程
* @param cfg Wi-Fi 连接配置
* @return 0=成功,-1=失败
*/
int wifi_proc_start(const wifi_config_t *cfg) {
if (cfg) memcpy(&g_wifi_cfg, cfg, sizeof(g_wifi_cfg));
/* 先做一次初始连接 */
if (wifi_connect(&g_wifi_cfg) != 0) {
LOGW("wifi_proc_start: initial connect failed, will retry in thread");
}
g_wifi_proc_running = 1;
int ret = pthread_create(&g_wifi_thread, NULL, wifi_proc, NULL);
if (ret != 0) {
LOGE("wifi_proc_start: pthread_create failed: %s", strerror(ret));
g_wifi_proc_running = 0;
return -1;
}
pthread_detach(g_wifi_thread);
LOGI("wifi_proc_start: daemon thread started");
return 0;
}
/**
* @brief 停止 Wi-Fi 守护线程
*/
void wifi_proc_stop(void) {
g_wifi_proc_running = 0;
/* 等待线程自然退出(最多 2×INTERVAL) */
sleep(WIFI_PROC_SLEEP_INTERVAL * 2 + 2);
LOGI("wifi_proc_stop: done");
}
代码基本上满足了当初的设计思路。这里未做测试。
浙公网安备 33010602011771号