Contents
实现
SDK 已经实现 EZ 配网的底层逻辑,定义了一套无线适配接口,应用只需要完成接口适配即可。
EZ 配网的交互流程示意图:
接下来,我们一步步来完成 EZ 配网的适配工作。
本示例仅供参考,要求开发者理解其实现逻辑。本示例代码适配的是 Realtek RTL8197F 平台,不同芯片或不同系统无线相关的操作可能都存在差异。为了方便演示,本示例代码不考虑性能,直接使用 Shell 命令操作。
-
在 SDK 初始化时指定无线配网方式为仅支持 EZ 配网。
// ...int main(int argc, char **argv){// ...#if defined(GW_SUPPORT_WIRED_WIFI) && (GW_SUPPORT_WIRED_WIFI==1)TUYA_CALL_ERR_RETURN(tuya_iot_wired_wf_sdk_init(IOT_GW_NET_WIRED_WIFI, GWCM_OLD, WF_START_SMART_ONLY, PID, USER_SW_VER, NULL, 0));#elif defined(WIFI_GW) && (WIFI_GW==1)TUYA_CALL_ERR_RETURN(tuya_iot_wf_sdk_init(GWCM_OLD, WF_START_SMART_ONLY, PID, USER_SW_VER, NULL, 0));#else// 有线 SDK,不支持无线配网return OPRT_COM_ERROR;#endif// ...} -
适配获取 MAC 地址接口,实现获取无线接口 MAC 地址。
OPERATE_RET tuya_adapter_wifi_get_mac(CONST WF_IF_E wf, NW_MAC_S *mac){CHAR_T buf[256] = {0};CHAR_T *pstart = NULL;FILE *fp = NULL;fp = popen("ifconfig " WLAN_DEV, "r");if (fp == NULL) {return OPRT_COM_ERROR;}while (fgets(buf, SIZEOF(buf), fp) != NULL) {pstart = strstr(buf, "HWaddr ");if (pstart != NULL) {INT_T x1, x2, x3, x4, x5, x6;sscanf(pstart + strlen("HWaddr "), "%x:%x:%x:%x:%x:%x", &x1, &x2, &x3, &x4, &x5, &x6);mac->mac[0] = x1 & 0xFF;mac->mac[1] = x2 & 0xFF;mac->mac[2] = x3 & 0xFF;mac->mac[3] = x4 & 0xFF;mac->mac[4] = x5 & 0xFF;mac->mac[5] = x6 & 0xFF;break;}}pclose(fp);PR_DEBUG("MAC Addr: %02X-%02X-%02X-%02X-%02X-%02X", mac->mac[0], mac->mac[1], mac->mac[2], mac->mac[3], mac->mac[4], mac->mac[5]);return OPRT_OK;} -
适配设置无线模式接口,根据参数把无线设置到对应的模式。
OPERATE_RET tuya_adapter_wifi_set_work_mode(CONST WF_WK_MD_E mode){/*** Realtek RTL8197F SNIFFER 模式不需要设置,这里直接保存到全局变量*/g_cur_mode = mode;switch (mode) {case WWM_STATION:{system("iwpriv " WLAN_DEV " set_mib opmode=0x8");break;}case WWM_SOFTAP:{system("iwpriv " WLAN_DEV " set_mib opmode=0x10");break;}default:{break;}}return OPRT_OK;} -
适配扫描 AP 接口,搜索周围环境的 AP,把搜索到的 AP 信道、BSSID、SSID 等信息提供给 SDK,SDK 仅在这些 AP 列表中依次选择信道。当 AP 列表为空时,SDK 会轮询所有信道。
OPERATE_RET tuya_adapter_wifi_all_ap_scan(AP_IF_S **aps, UINT_T *num){INT_T idx = -1;FILE *fp = NULL;CHAR_T buf[128] = {0};CHAR_T *pstart = NULL;STATIC AP_IF_S s_aps[MAX_SCAN_NUM];memset(s_aps, 0, SIZEOF(s_aps));*aps = s_aps;*num = 0;system("iwpriv wlan0 at_ss && sleep 2");fp = fopen("/proc/wlan0/SS_Result", "r");if (fp == NULL) {return OPRT_OK;}while (1) {if (fgets(buf, SIZEOF(buf), fp) == NULL) {break;}pstart = strstr(buf, "HwAddr");if (pstart == NULL) {continue;}idx++;/* BSSID */INT_T x1, x2, x3, x4, x5, x6;sscanf(pstart + strlen("HwAddr: "), "%02x%02x%02x%02x%02x%02x", &x1, &x2, &x3, &x4, &x5, &x6);s_aps[idx].bssid[0] = x1 & 0xFF;s_aps[idx].bssid[1] = x2 & 0xFF;s_aps[idx].bssid[2] = x3 & 0xFF;s_aps[idx].bssid[3] = x4 & 0xFF;s_aps[idx].bssid[4] = x5 & 0xFF;s_aps[idx].bssid[5] = x6 & 0xFF;/* Channel */memset(buf, 0, SIZEOF(buf));if (fgets(buf, SIZEOF(buf), fp) == NULL) {break;}pstart = strstr(buf, "Channel");if (pstart == NULL) {break;}sscanf(pstart + strlen("Channel: "), "%d", &(s_aps[idx].channel));/* SSID */memset(buf, 0, SIZEOF(buf));if (fgets(buf, SIZEOF(buf), fp) == NULL) {break;}pstart = strstr(buf, "SSID");if (pstart == NULL) {break;}sscanf(pstart + strlen("SSID: "), "%s", s_aps[idx].ssid);s_aps[idx].s_len = strlen(s_aps[idx].ssid);}*num = idx + 1;return OPRT_OK;} -
适配设置 SNIFFER 接口,开启或关闭 SNIFFER 功能。
/*** a) en == TRUE: 开启 SNIFFER 功能,则创建线程抓取无线包,然后把无线包给 SDK 解析。为了提升效率,在把无线包给 SDK 解析* 之前,应用可以先做过滤,帧类型为 Data 的数据才给 SDK 处理,丢弃帧类型为 Management 和 Control 的数据。* b) en == FALSE: 关闭 SNIFFER 功能,则销毁线程,为了能保证触发连接路由器接口,可以强制把无线切换成 AP 模式。*/OPERATE_RET tuya_adapter_wifi_sniffer_set(CONST BOOL_T en, CONST SNIFFER_CALLBACK cb){PR_DEBUG("WiFi Set Sniffer, en: %d", en);if (en == g_sniffer_en_flag) {PR_WARN("sniffer status not changed, en: %d, en_flag: %d", en, g_sniffer_en_flag);return OPRT_OK;}g_sniffer_en_flag = en;if (en) {PR_DEBUG("enable sniffer");g_sniffer_cb = cb;tuya_adapter_wifi_set_work_mode(WWM_SNIFFER);if (0 != pthread_create(&g_sniffer_tid, NULL, sniffer_run, NULL)) {PR_ERR("pthread_create error");return OPRT_COM_ERROR;}} else {PR_DEBUG("disable sniffer");pthread_join(g_sniffer_tid, NULL);tuya_hal_wifi_set_work_mode(WWM_SOFTAP);}return OPRT_OK;}STATIC OPERATE_RET wlan_80211_packet_parse(BYTE_T *buf, UINT_T len){WLAN_80211_HEAD_S *wbuf = (WLAN_80211_HEAD_S *)buf;/* version must be 0x00 */if ((wbuf->frmae_ctl1 & 0x03) != 0x00) {return OPRT_COM_ERROR;}switch ((wbuf->frmae_ctl1) & 0x0c) {case 0x08: {/* Only Receive Data Frame */break;}default: {return OPRT_COM_ERROR;}}/* From DS = 1 */if ((wbuf->frmae_ctl2 & 0x3) == 0x2) {/*** Filter out data by destination address.* 1. For Broadcast, FF:FF:FF:FF:FF:FF is valid* 2. For Multicast, prefix with 01:00:5E is valid*/if (!memcmp(wbuf->addr1, "\xff\xff\xff\xff\xff\xff", 6) || \!memcmp(wbuf->addr1, "\x01\x00\x5e", 3)) {return OPRT_OK;}}return OPRT_COM_ERROR;}STATIC VOID *sniffer_run(VOID *arg){INT_T sock = 0;struct ifreq ifr;size_t recv_len = 0;BYTE_T recv_buf[1024] = {0};PR_DEBUG("sniffer task started");sock = socket(PF_PACKET, SOCK_RAW, htons(0x03)); //ETH_P_ALLif (sock < 0) {PR_ERR("socket error");return (VOID *)0;}memset(&ifr, 0x00, SIZEOF(ifr));strncpy(ifr.ifr_name, WLAN_DEV , SIZEOF(ifr.ifr_name) - 1);setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (VOID *)&ifr, SIZEOF(ifr));while (g_sniffer_en_flag && (g_sniffer_cb != NULL)) {recv_len = recvfrom(sock, recv_buf, SIZEOF(recv_buf), 0, NULL, NULL);if (recv_len <= 0) {continue;}struct rtl_wifi_header *rh = (struct rtl_wifi_header *)recv_buf;if ((rh->pkt_len > 0) && (rh->data[0] != 0xff)) {if (wlan_80211_packet_parse(rh->data, rh->pkt_len) == 0) {g_sniffer_cb(rh->data, rh->pkt_len, 99);}}}g_sniffer_cb = NULL;close(sock);return (void *)0;} -
适配设置信道接口,实现设置信道功能。设置信道其实是为了让无线接口在该信道上接收无线包,可能有些无线驱动在监听模式抓包不需要固定信道,可以忽略不处理。
OPERATE_RET tuya_adapter_wifi_set_cur_channel(CONST UCHAR_T channel){/*** Realtek RTL8197F 接收无线包不需要固定信道,这里直接保存到全局变量*/g_cur_channel = channel;return OPRT_OK;} -
适配获取信道接口,实现获取信道功能。SDK 接收到前导码后,需要锁定信道接收剩余的数据,使用该接口获取当前的信道。可能有些无线驱动在监听模式抓包不需要固定信道,可以忽略不处理。
OPERATE_RET tuya_adapter_wifi_get_cur_channel(UCHAR_T *chan){/*** Realtek RTL8197F 不需要固定信道,这里直接从全局变量取值*/*chan = g_cur_channel;return OPRT_OK;} -
适配连接路由器接口,实现连接路由器功能。SDK 已经成功获取到路由器的 SSID 和密码,使用该接口连接路由器。
OPERATE_RET tuya_adapter_wifi_station_connect(CONST SCHAR_T *ssid, CONST SCHAR_T *passwd){CHAR_T cmd_buf[128] = {0};/*** 关闭 udhcpd 服务*/system("killall udhcpd");system("ifconfig " WLAN_DEV " down");system("iwpriv " WLAN_DEV " set_mib opmode=0x8");system("iwpriv " WLAN_DEV " set_mib band=11");system("iwpriv " WLAN_DEV " set_mib deny_legacy=0");system("iwpriv " WLAN_DEV " set_mib use40M=1");system("iwpriv " WLAN_DEV " set_mib 802_1x=0");snprintf(cmd_buf, SIZEOF(cmd_buf), "iwpriv %s set_mib ssid=\"%s\"", WLAN_DEV, ssid);system(cmd_buf);if (!passwd || (strlen(passwd) == 0)) {system("iwpriv " WLAN_DEV " set_mib authtype=0");system("iwpriv " WLAN_DEV " set_mib encmode=0");} else {system("iwpriv " WLAN_DEV " set_mib authtype=2");system("iwpriv " WLAN_DEV " set_mib psk_enable=3");system("iwpriv " WLAN_DEV " set_mib encmode=2");system("iwpriv " WLAN_DEV " set_mib wpa2_cipher=10");system("iwpriv " WLAN_DEV " set_mib wpa_cipher=10");snprintf(cmd_buf, SIZEOF(cmd_buf), "iwpriv %s set_mib passphrase=\"%s\"", WLAN_DEV, passwd);system(cmd_buf);}system("ifconfig " WLAN_DEV " up");system("ps | grep 'udhcpc -i wlan0' | grep -v grep | awk '{print $1}' | xargs kill -9");system("ps | grep 'udhcpc -b -i wlan0' | grep -v grep | awk '{print $1}' | xargs kill -9");system("busybox udhcpc -b -i wlan0 -A 3 -T 3 -s /tmp/tuya/8197wlan0_udhcpc.script&");return OPRT_OK;} -
适配获取无线工作模式接口,实现获取无线接口当前的模式功能。
OPERATE_RET tuya_adapter_wifi_get_work_mode(WF_WK_MD_E *mode){CHAR_T buf[256] = {0};CHAR_T *pstart = NULL;CHAR_T tmp[16] = {0};FILE *fp = NULL;/*** Realtek RTL8197F SNIFFER 模式不需要设置,直接从全局变量取值*/if (g_cur_mode == WWM_SNIFFER) {*mode = WWM_SNIFFER;return OPRT_OK;}fp = popen("iwconfig " WLAN_DEV, "r");if (fp == NULL) {return OPRT_COM_ERROR;}while (fgets(buf, SIZEOF(buf), fp) != NULL) {pstart = strstr(buf, "Mode:");if (pstart != NULL) {sscanf(pstart + strlen("Mode:"), "%s ", tmp);break;}}pclose(fp);if (!strncasecmp(tmp, "Managed", strlen("Managed"))) {*mode = WWM_STATION;} else if (!strncasecmp(tmp, "Master", strlen("Master"))) {*mode = WWM_SOFTAP;} else {*mode = WWM_UNKNOWN;}return OPRT_OK;} -
适配获取无线 IP 地址接口,实现获取无线接口的 IP 地址功能,用于局域网通讯。
OPERATE_RET tuya_adapter_wifi_get_ip(CONST WF_IF_E wf, NW_IP_S *ip) { CHAR_T buf[256] = {0}; CHAR_T *pstart = NULL; FILE *fp = NULL;
fp = popen("ifconfig " WLAN_DEV, "r");if (fp == NULL) {return OPRT_COM_ERROR;}while (fgets(buf, SIZEOF(buf), fp) != NULL) {pstart = strstr(buf, "inet ");if (pstart != NULL) {break;}}pclose(fp);pstart = strstr(buf, "inet addr:");if (pstart == NULL) {return OPRT_COM_ERROR;}sscanf(pstart + strlen("inet addr:"), "%s", ip->ip);if (wf == WF_STATION) {/*** 避免获取到的是默认 IP 地址*/if (!strncmp(ip->ip, AP_DEFAULT_IP, SIZEOF(ip->ip))) {PR_TRACE("%s is default ip of AP", ip->ip);return OPRT_COM_ERROR;}}PR_TRACE("IP Addr: %s", ip->ip);return OPRT_OK;}
-
适配获取无线状态接口,实现获取 Station 的连接状态功能。连接路由器后,SDK 会定时获取无线的连接状态,用来判断网络的状态,以
WSS_GOT_IP状态为判断标准。OPERATE_RET tuya_adapter_wifi_station_get_status(WF_STATION_STAT_E *stat) { NW_IP_S ip = {0};
if (stat == NULL) {PR_ERR("invalid param");return OPRT_INVALID_PARM;}if (OPRT_OK == tuya_adapter_wifi_get_ip(WF_STATION, &ip)) {*stat = WSS_GOT_IP;} else {*stat = WSS_CONN_FAIL;}return OPRT_OK;}
注意事项
在无线配网的适配中有些需要注意的地方,以下描述了无线配网适配的常见开发问题,开发应用时要重点关注。
接口实现
所有的适配接口都不允许阻塞,处理任务的时间也不允许过长,否则会出现配网超时等问题。耗时大于 10 秒的任务,建议使用异步方式来处理。
接口返回值
除了获取 IP 地址接口,其他无线配网相关的适配接口返回值为非 0,无线配网都会被终止,所以适配接口尽量保证返回值为 0。
无线状态
SDK 获取到 SSID 和密码后,先判断设备是否已经连接到上级路由器,未连接上级路由器则调用 tuya_adapter_wifi_station_connect 接口通知应用连接上级路由器。判断是否已经连接到上级路由器的机制是,调用 tuya_adapter_wifi_station_get_status 接口获取无线状态,状态为 WSS_GOT_IP 表示已连接到上级路由器。
在切换到 Station 模式之前,无线接口可能有默认 IP 地址,往往会导致在获取无线状态时无判断无线接口已经获取到了 IP 地址,所以返回 WSS_GOT_IP 状态,而实际这是无线接口的默认 IP 地址,而不是路由器分配了 IP 地址,会出现配网超时等现象。
另外,SDK 会定时调用 tuya_adapter_wifi_station_get_status 接口查询无线状态,要求该接口必须快速响应,尽量不超过 1 秒。
附录
main.c
#include <unistd.h>
#include "uni_log.h"#include "base_os_adapter.h"#include "tuya_iot_base_api.h"#include "tuya_iot_com_api.h"
#include "tuya_iot_sdk_api.h"#include "tuya_iot_sdk_defs.h"
#if defined(TY_BT_MOD) && (TY_BT_MOD == 1)#include "tuya_os_adapt_bt.h"#endif
#define PID "fljmamufiym5fktz" // 替换成自己的产品 ID#define UUID "tuya461dbc63aeeb991f" // 替换成自己的 UUID#define AUTHKEY "c8X4PR4wx1gMFaQlaZu5dfgVvVRwB8Ug" // 替换成自己的 AUTHKEY
STATIC VOID __gw_reset_cb(GW_RESET_TYPE_E type){ PR_DEBUG("gw reset callback, type: %d", type);
if (GW_RESET_DATA_FACTORY != type) { exit(0); }
return;}
STATIC VOID __gw_upgrade_cb(CONST FW_UG_S *fw){ PR_DEBUG("gw upgrade callback");
if (fw == NULL) { PR_ERR("invalid param"); return; }
PR_DEBUG(" tp: %d", fw->tp); PR_DEBUG(" fw_url: %s", fw->fw_url); PR_DEBUG(" sw_ver: %s", fw->sw_ver); PR_DEBUG(" fw_hmac: %s", fw->fw_hmac); PR_DEBUG(" file_size: %u", fw->file_size);
return;}
STATIC VOID __gw_active_stat_cb(GW_STATUS_E status){ PR_DEBUG("gw active stat callback, status: %d", status);
return;}
STATIC VOID __gw_reboot_cb(VOID){ PR_DEBUG("gw reboot callback");
exit(0);
return;}
STATIC VOID __nw_stat_cb(IN CONST SDK_NW_STAT_T stat){ PR_DEBUG("network stat: %d", stat);
return;}
STATIC VOID __wired_stat_cb(IN CONST SDK_WIRED_NW_STAT_T stat){ PR_DEBUG("wired stat: %d", stat);
return;}
int main(int argc, char **argv){ OPERATE_RET rt = OPRT_OK; GW_PROD_INFO_S prod_info = {0};
/* gw base callback */ TY_GW_INFRA_CBS_S gw_cbs = { .gw_reset_cb = __gw_reset_cb, .gw_upgrade_cb = __gw_upgrade_cb, .gw_active_stat_cb = __gw_active_stat_cb, .gw_reboot_cb = __gw_reboot_cb, };
#if defined(TY_BT_MOD) && (TY_BT_MOD == 1) tuya_os_adapt_reg_bt_intf();#endif
/* initiate os-layer service*/ tuya_os_intf_init();
/* initiate iot-layer service */ TUYA_CALL_ERR_RETURN(tuya_iot_init("./"));
/* set the logging level to debug */ SET_PR_DEBUG_LEVEL(TY_LOG_LEVEL_DEBUG);
PR_DEBUG("SDK INFO: %s", tuya_iot_get_sdk_info());
/* set uuid and authkey */ prod_info.uuid = UUID; prod_info.auth_key = AUTHKEY; TUYA_CALL_ERR_RETURN(tuya_iot_set_gw_prod_info(&prod_info));
/* pre-initiate sdk service */ TUYA_CALL_ERR_RETURN(tuya_iot_sdk_pre_init(TRUE));
/* initiate application service, more service in here */ TUYA_CALL_ERR_RETURN(tuya_user_svc_init(&gw_cbs));
/* initiate sdk service */#if defined(GW_SUPPORT_WIRED_WIFI) && (GW_SUPPORT_WIRED_WIFI==1) TUYA_CALL_ERR_RETURN(tuya_iot_wired_wf_sdk_init(IOT_GW_NET_WIRED_WIFI, GWCM_OLD, WF_START_SMART_ONLY, PID, USER_SW_VER, NULL, 0));#elif defined(WIFI_GW) && (WIFI_GW==1) TUYA_CALL_ERR_RETURN(tuya_iot_wf_sdk_init(GWCM_OLD, WF_START_SMART_ONLY, PID, USER_SW_VER, NULL, 0));#else // 有线 SDK,不支持无线配网 return OPRT_COM_ERROR;#endif
/* register net stat notification callback */ TUYA_CALL_ERR_RETURN(tuya_iot_sdk_reg_netstat_cb(__nw_stat_cb, __wired_stat_cb, NULL));
/* start application service, more service in here */ TUYA_CALL_ERR_RETURN(tuya_user_svc_start(NULL));
while (1) { sleep(10); }
return 0;}hal_wifi.c
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <pthread.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <net/if.h>
#include "uni_log.h"#include "tuya_os_adapt_wifi.h"
#define WLAN_DEV "wlan0"#define AP_DEFAULT_IP "192.168.133.1"#define MAX_SCAN_NUM 35
STATIC WF_WK_MD_E g_cur_mode = WWM_UNKNOWN;STATIC UCHAR_T g_cur_channel = 0;STATIC BOOL_T g_sniffer_en_flag = FALSE;STATIC pthread_t g_sniffer_tid;STATIC SNIFFER_CALLBACK g_sniffer_cb = NULL;
/** * Realtek RTL8197F 无线数据帧 */struct rtl_wifi_header { uint16_t pkt_len; uint16_t payload_len; uint8_t data[252];} __attribute__((packed));
/** * IEEE 802.11 无线协议帧头 */typedef struct wlan_80211_head { uint8_t frmae_ctl1; uint8_t frmae_ctl2; uint16_t duration; uint8_t addr1[6]; uint8_t addr2[6]; uint8_t addr3[6]; uint16_t seq_ctl; uint8_t addr4[6];} WLAN_80211_HEAD_S;
/** * frame_ctl1 (uint8_t) * Verion: mask 0x03 * Type: mask 0x0C * 00: Management Frame * 01: Control Frame * 10: Data Frame * 11: Reserved * Subtype: mask 0xF0 * 0000: Association Request * 0100: Probe Request * 1000: Beacon * 1010: Disassociation * * frame_ctl2 (uint8_t) - Frame Control Flags * To DS: mask 0x01 * From DS: mask 0x02 * +-----------------------------------------------------------------+ * | To Ds | From Ds | Address 1 | Address 2 | Address 3 | Address 4 | * +-----------------------------------------------------------------+ * | 0 | 0 | dest | src | bssid | N/A | * +-----------------------------------------------------------------+ * | 0 | 1 | dest | bssid | src | N/A | * +-----------------------------------------------------------------+ * | 1 | 0 | bssid | src | dest | N/A | * +-----------------------------------------------------------------+ * | 1 | 1 | RA | TA | DA | SA | * +-----------------------------------------------------------------+ * More Frag: mask 0x04 * Retry: mask 0x08 * Power Mgmt: mask 0x10 * More Data: mask 0x20 * Protected Frame: mask 0x40 * Order: mask 0x80 * */STATIC OPERATE_RET wlan_80211_packet_parse(BYTE_T *buf, UINT_T len){ WLAN_80211_HEAD_S *wbuf = (WLAN_80211_HEAD_S *)buf;
/* version must be 0x00 */ if ((wbuf->frmae_ctl1 & 0x03) != 0x00) { return OPRT_COM_ERROR; }
switch ((wbuf->frmae_ctl1) & 0x0c) { case 0x08: { /* Only Receive Data Frame */ break; } default: { return OPRT_COM_ERROR; } }
/* From DS = 1 */ if ((wbuf->frmae_ctl2 & 0x3) == 0x2) { /** * Filter out data by destination address. * 1. For Broadcast, FF:FF:FF:FF:FF:FF is valid * 2. For Multicast, prefix with 01:00:5E is valid */ if (!memcmp(wbuf->addr1, "\xff\xff\xff\xff\xff\xff", 6) || \ !memcmp(wbuf->addr1, "\x01\x00\x5e", 3)) { return OPRT_OK; } }
return OPRT_COM_ERROR;}
STATIC VOID *sniffer_run(VOID *arg){ INT_T sock = 0; struct ifreq ifr; size_t recv_len = 0; BYTE_T recv_buf[1024] = {0};
PR_DEBUG("sniffer task started");
sock = socket(PF_PACKET, SOCK_RAW, htons(0x03)); //ETH_P_ALL if (sock < 0) { PR_ERR("socket error"); return (VOID *)0; }
memset(&ifr, 0x00, SIZEOF(ifr)); strncpy(ifr.ifr_name, WLAN_DEV , SIZEOF(ifr.ifr_name) - 1); setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (VOID *)&ifr, SIZEOF(ifr));
while (g_sniffer_en_flag && (g_sniffer_cb != NULL)) { recv_len = recvfrom(sock, recv_buf, SIZEOF(recv_buf), 0, NULL, NULL); if (recv_len <= 0) { continue; }
struct rtl_wifi_header *rh = (struct rtl_wifi_header *)recv_buf; if ((rh->pkt_len > 0) && (rh->data[0] != 0xff)) { if (wlan_80211_packet_parse(rh->data, rh->pkt_len) == 0) { g_sniffer_cb(rh->data, rh->pkt_len, 99); } } }
g_sniffer_cb = NULL;
close(sock);
return (void *)0;}
/** * 获取无线 IP 地址接口 */OPERATE_RET tuya_adapter_wifi_get_ip(CONST WF_IF_E wf, NW_IP_S *ip){ CHAR_T buf[256] = {0}; CHAR_T *pstart = NULL; FILE *fp = NULL;
/** * 系统性错误,返回错误 */ fp = popen("ifconfig " WLAN_DEV, "r"); if (fp == NULL) { return OPRT_COM_ERROR; }
while (fgets(buf, SIZEOF(buf), fp) != NULL) { pstart = strstr(buf, "inet "); if (pstart != NULL) { break; } }
pclose(fp);
pstart = strstr(buf, "inet addr:"); if (pstart == NULL) { return OPRT_COM_ERROR; } sscanf(pstart + strlen("inet addr:"), "%s", ip->ip);
if (wf == WF_STATION) { /** * 避免获取到的是默认的 IP 地址 */ if (!strncmp(ip->ip, AP_DEFAULT_IP, SIZEOF(ip->ip))) { PR_TRACE("%s is default ip of AP", ip->ip); return OPRT_COM_ERROR; } }
PR_TRACE("IP Addr: %s", ip->ip);
return OPRT_OK;}
/** * 获取无线 MAC 地址接口 */OPERATE_RET tuya_adapter_wifi_get_mac(CONST WF_IF_E wf, NW_MAC_S *mac){ CHAR_T buf[256] = {0}; CHAR_T *pstart = NULL; FILE *fp = NULL;
/** * 系统性错误,返回错误 */ fp = popen("ifconfig " WLAN_DEV, "r"); if (fp == NULL) { return OPRT_COM_ERROR; }
while (fgets(buf, SIZEOF(buf), fp) != NULL) { pstart = strstr(buf, "HWaddr "); if (pstart != NULL) { INT_T x1, x2, x3, x4, x5, x6; sscanf(pstart + strlen("HWaddr "), "%x:%x:%x:%x:%x:%x", &x1, &x2, &x3, &x4, &x5, &x6); mac->mac[0] = x1 & 0xFF; mac->mac[1] = x2 & 0xFF; mac->mac[2] = x3 & 0xFF; mac->mac[3] = x4 & 0xFF; mac->mac[4] = x5 & 0xFF; mac->mac[5] = x6 & 0xFF; break; } }
pclose(fp);
PR_DEBUG("MAC Addr: %02X-%02X-%02X-%02X-%02X-%02X", mac->mac[0], mac->mac[1], mac->mac[2], mac->mac[3], mac->mac[4], mac->mac[5]);
return OPRT_OK;}
/** * 设置无线模式 */OPERATE_RET tuya_adapter_wifi_set_work_mode(CONST WF_WK_MD_E mode){ /** * Realtek RTL8197F SNIFFER 模式不需要设置,这里直接保存到全局变量 */ g_cur_mode = mode;
switch (mode) { case WWM_STATION: { system("iwpriv " WLAN_DEV " set_mib opmode=0x8"); break; } case WWM_SOFTAP: { system("iwpriv " WLAN_DEV " set_mib opmode=0x10"); break; } default: { break; } }
return OPRT_OK;}
/** * 获取无线模式 */OPERATE_RET tuya_adapter_wifi_get_work_mode(WF_WK_MD_E *mode){ CHAR_T buf[256] = {0}; CHAR_T *pstart = NULL; CHAR_T tmp[16] = {0}; FILE *fp = NULL;
/** * Realtek RTL8197F SNIFFER 模式不需要设置,直接从全局变量取值 */ if (g_cur_mode == WWM_SNIFFER) { *mode = WWM_SNIFFER; return OPRT_OK; }
/** * 系统性错误,返回错误 */ fp = popen("iwconfig " WLAN_DEV, "r"); if (fp == NULL) { return OPRT_COM_ERROR; }
while (fgets(buf, SIZEOF(buf), fp) != NULL) { pstart = strstr(buf, "Mode:"); if (pstart != NULL) { sscanf(pstart + strlen("Mode:"), "%s ", tmp); break; } } pclose(fp);
if (!strncasecmp(tmp, "Managed", strlen("Managed"))) { *mode = WWM_STATION; } else if (!strncasecmp(tmp, "Master", strlen("Master"))) { *mode = WWM_SOFTAP; } else { *mode = WWM_UNKNOWN; }
return OPRT_OK;}
/** * Station 连接路由器接口 */OPERATE_RET tuya_adapter_wifi_station_connect(CONST SCHAR_T *ssid, CONST SCHAR_T *passwd){ CHAR_T cmd_buf[128] = {0};
/** * 关闭 udhcpd 服务 */ system("killall udhcpd");
system("ifconfig " WLAN_DEV " down");
system("iwpriv " WLAN_DEV " set_mib opmode=0x8"); system("iwpriv " WLAN_DEV " set_mib band=11"); system("iwpriv " WLAN_DEV " set_mib deny_legacy=0"); system("iwpriv " WLAN_DEV " set_mib use40M=1"); system("iwpriv " WLAN_DEV " set_mib 802_1x=0"); snprintf(cmd_buf, SIZEOF(cmd_buf), "iwpriv %s set_mib ssid=\"%s\"", WLAN_DEV, ssid); system(cmd_buf);
if (!passwd || (strlen(passwd) == 0)) { system("iwpriv " WLAN_DEV " set_mib authtype=0"); system("iwpriv " WLAN_DEV " set_mib encmode=0"); } else { system("iwpriv " WLAN_DEV " set_mib authtype=2"); system("iwpriv " WLAN_DEV " set_mib psk_enable=3"); system("iwpriv " WLAN_DEV " set_mib encmode=2"); system("iwpriv " WLAN_DEV " set_mib wpa2_cipher=10"); system("iwpriv " WLAN_DEV " set_mib wpa_cipher=10"); snprintf(cmd_buf, SIZEOF(cmd_buf), "iwpriv %s set_mib passphrase=\"%s\"", WLAN_DEV, passwd); system(cmd_buf); }
system("ifconfig " WLAN_DEV " up");
system("ps | grep 'udhcpc -i wlan0' | grep -v grep | awk '{print $1}' | xargs kill -9"); system("ps | grep 'udhcpc -b -i wlan0' | grep -v grep | awk '{print $1}' | xargs kill -9"); system("busybox udhcpc -b -i wlan0 -A 3 -T 3 -s /tmp/tuya/8197wlan0_udhcpc.script&");
return OPRT_OK;}
/** * 断开 Station 连接接口 */OPERATE_RET tuya_adapter_wifi_station_disconnect(VOID_T){ system("ifconfig " WLAN_DEV " 0.0.0.0"); system("ps | grep 'udhcpc -i wlan0' | grep -v grep | awk '{print $1}' | xargs kill -9"); system("ps | grep 'udhcpc -b -i wlan0' | grep -v grep | awk '{print $1}' | xargs kill -9");
return OPRT_OK;}
/** * 获取 Station 连接状态接口 */OPERATE_RET tuya_adapter_wifi_station_get_status(WF_STATION_STAT_E *stat){ NW_IP_S ip = {0};
if (stat == NULL) { PR_ERR("invalid param"); return OPRT_INVALID_PARM; }
if (OPRT_OK == tuya_adapter_wifi_get_ip(WF_STATION, &ip)) { *stat = WSS_GOT_IP; } else { *stat = WSS_CONN_FAIL; }
return OPRT_OK;}
/** * 扫描 AP 接口,该接口主要用于获取当前环境的信道,只在存在的信道中接收无线包,提高配网 * 成功率,num 为 0 则轮询所有信道。 */OPERATE_RET tuya_adapter_wifi_all_ap_scan(AP_IF_S **aps, UINT_T *num){ INT_T idx = -1; FILE *fp = NULL; CHAR_T buf[128] = {0}; CHAR_T *pstart = NULL; STATIC AP_IF_S s_aps[MAX_SCAN_NUM];
memset(s_aps, 0, SIZEOF(s_aps)); *aps = s_aps; *num = 0;
system("iwpriv wlan0 at_ss && sleep 2"); fp = fopen("/proc/wlan0/SS_Result", "r"); if (fp == NULL) { return OPRT_OK; }
while (1) { if (fgets(buf, SIZEOF(buf), fp) == NULL) { break; } pstart = strstr(buf, "HwAddr"); if (pstart == NULL) { continue; }
idx++;
/* BSSID */ INT_T x1, x2, x3, x4, x5, x6; sscanf(pstart + strlen("HwAddr: "), "%02x%02x%02x%02x%02x%02x", &x1, &x2, &x3, &x4, &x5, &x6); s_aps[idx].bssid[0] = x1 & 0xFF; s_aps[idx].bssid[1] = x2 & 0xFF; s_aps[idx].bssid[2] = x3 & 0xFF; s_aps[idx].bssid[3] = x4 & 0xFF; s_aps[idx].bssid[4] = x5 & 0xFF; s_aps[idx].bssid[5] = x6 & 0xFF;
/* Channel */ memset(buf, 0, SIZEOF(buf)); if (fgets(buf, SIZEOF(buf), fp) == NULL) { break; } pstart = strstr(buf, "Channel"); if (pstart == NULL) { break; } sscanf(pstart + strlen("Channel: "), "%d", &(s_aps[idx].channel));
/* SSID */ memset(buf, 0, SIZEOF(buf)); if (fgets(buf, SIZEOF(buf), fp) == NULL) { break; } pstart = strstr(buf, "SSID"); if (pstart == NULL) { break; } sscanf(pstart + strlen("SSID: "), "%s", s_aps[idx].ssid); s_aps[idx].s_len = strlen(s_aps[idx].ssid); }
*num = idx + 1;
return OPRT_OK;}
/* 释放扫描 AP 接口申请的堆内存 */OPERATE_RET tuya_adapter_wifi_release_ap(AP_IF_S *ap){ return OPRT_OK;}
/* 设置信道接口,该接口用于切换信道,为了能在不同信道上接收无线包 */OPERATE_RET tuya_adapter_wifi_set_cur_channel(CONST UCHAR_T channel){ /** * Realtek RTL8197F 接收无线包不需要固定信道,这里直接保存到全局变量 */ g_cur_channel = channel;
return OPRT_OK;}
/* 获取信道接口,该接口用于锁定信道 */OPERATE_RET tuya_adapter_wifi_get_cur_channel(UCHAR_T *chan){ /** * Realtek RTL8197F 不需要固定信道,这里直接从全局变量取值 */ *chan = g_cur_channel;
return OPRT_OK;}
/** * 设置监听模式,需要完成以下逻辑: * a) 开启监听模式时,创建线程抓无线包,把抓取到的无线包给 SNIFFER_CALLBACK 回调处理。 * b) 关闭监听模式时,销毁线程。 */OPERATE_RET tuya_adapter_wifi_sniffer_set(CONST BOOL_T en, CONST SNIFFER_CALLBACK cb){ PR_DEBUG("WiFi Set Sniffer, en: %d", en);
if (en == g_sniffer_en_flag) { PR_WARN("sniffer status not changed, en: %d, en_flag: %d", en, g_sniffer_en_flag); return OPRT_OK; }
g_sniffer_en_flag = en;
if (en) { PR_DEBUG("enable sniffer"); g_sniffer_cb = cb; tuya_adapter_wifi_set_work_mode(WWM_SNIFFER); if (0 != pthread_create(&g_sniffer_tid, NULL, sniffer_run, NULL)) { PR_ERR("pthread_create error"); return OPRT_COM_ERROR; } } else { PR_DEBUG("disable sniffer"); pthread_join(g_sniffer_tid, NULL); tuya_hal_wifi_set_work_mode(WWM_SOFTAP); }
return OPRT_OK;}
/** * 获取无线信号强度 */OPERATE_RET tuya_adapter_wifi_station_get_conn_ap_rssi(SCHAR_T *rssi){ *rssi = 99;
return OPRT_OK;}
/* AP 配网接口 */OPERATE_RET tuya_adapter_wifi_ap_start(CONST WF_AP_CFG_IF_S *cfg){ return OPRT_OK;}
/* AP 配网接口 */OPERATE_RET tuya_adapter_wifi_ap_stop(VOID_T){ return OPRT_OK;}
/* 无线实现 */OPERATE_RET tuya_adapter_wifi_assign_ap_scan(CONST SCHAR_T *ssid, AP_IF_S **ap){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_set_mac(CONST WF_IF_E wf, CONST NW_MAC_S *mac){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_get_bssid(UCHAR_T *mac){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_set_country_code(CONST COUNTRY_CODE_E code){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_send_mgnt(CONST UCHAR_T *buf, CONST UINT_T len){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_register_recv_mgnt_callback(CONST BOOL_T enable, CONST WIFI_REV_MGNT_CB recv_cb){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_set_lp_mode(CONST BOOL_T en, CONST UCHAR_T dtim){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_get_connected_ap_info_v2(FAST_WF_CONNECTED_AP_INFO_V2_S **fast_ap_info){ return OPRT_COM_ERROR;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_fast_station_connect_v2(CONST FAST_WF_CONNECTED_AP_INFO_V2_S *fast_ap_info){ return OPRT_COM_ERROR;}
/* 无需实现 */BOOL_T tuya_adapter_wifi_rf_calibrated(VOID_T){ return FALSE;}```urn OPRT_OK;}
/* 无线实现 */OPERATE_RET tuya_adapter_wifi_assign_ap_scan(CONST SCHAR_T *ssid, AP_IF_S **ap){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_set_mac(CONST WF_IF_E wf, CONST NW_MAC_S *mac){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_get_bssid(UCHAR_T *mac){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_set_country_code(CONST COUNTRY_CODE_E code){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_send_mgnt(CONST UCHAR_T *buf, CONST UINT_T len){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_register_recv_mgnt_callback(CONST BOOL_T enable, CONST WIFI_REV_MGNT_CB recv_cb){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_set_lp_mode(CONST BOOL_T en, CONST UCHAR_T dtim){ return OPRT_OK;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_get_connected_ap_info_v2(FAST_WF_CONNECTED_AP_INFO_V2_S **fast_ap_info){ return OPRT_COM_ERROR;}
/* 无需实现 */OPERATE_RET tuya_adapter_wifi_fast_station_connect_v2(CONST FAST_WF_CONNECTED_AP_INFO_V2_S *fast_ap_info){ return OPRT_COM_ERROR;}
/* 无需实现 */BOOL_T tuya_adapter_wifi_rf_calibrated(VOID_T){ return FALSE;}