更新时间:2024-11-20 08:51:30下载pdf
本文详细介绍使用综合 SDK 如何实现 EZ 配网功能,我们将会实现通过 EZ 配网方式来激活网关设备。
EZ 配网也称 快速配网。其工作原理是,手机连接路由器,手机把令牌、路由器的 SSID 和密码等信息按一定方式进行编码,通过组播或广播方式传播。设备的无线接口处于监听模式,抓取无线报文并对其进行解析,提取出用于激活的令牌,以及路由器的 SSID 和密码,完成激活流程。
常用的编码方式有两种:
相比于 AP 配网,EZ 配网更加便捷,但是路由器和手机存在一定的兼容性问题,以及无线干扰产生丢包问题,会导致 EZ 配网失败,所以 EZ 配网无法保证 100% 成功。
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_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;
}
适配设置信道接口,实现设置信道功能。设置信道其实是为了让无线接口在该信道上接收无线包,可能有些无线驱动在监听模式抓包不需要固定信道,可以忽略不处理。
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 秒。
#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;
}
#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;
}
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈