1、WLAN管理框架简介
随着物联网快速发展,愈来愈多的嵌入式设备上搭载了 WIFI 无线网络设备,为了可以管理 WIFI 网络设备,RT-Thread 引入了 WLAN 设备管理框架。这套框架是 RT-Thread 开发的一套用于管理 WIFI 的中间件:对下链接具体的 WIFI 驱动,控制 WIFI 的链接、断开、扫描等操做;对上承载不一样的应用,为应用提供 WIFI 控制、事件、数据导流等操做,为上层应用提供统一的 WIFI 控制接口。html
WLAN 框架主要由四个部分组成:Device 驱动接口层,为 WLAN 框架提供统一的调用接口;Manage 管理层为用户提供 WIFI 扫描、链接、断线重连等具体功能;Protocol 协议负责处理 WIFI 上产生的网络数据流,可根据不一样的使用场景挂载不一样网络协议栈(好比 LWIP );Config配置层能够保存 WIFI 配置参数,为用户提供自动链接服务(可从Flash读取曾经链接过的热点配置信息)。WIFI 框架层次图示以下:
WLAN管理框架各层功能简介以下:git
APP应用层:是基于 WLAN 框架的具体应用,如 WiFi 相关的 Shell 命令;
Airkiss / Voice 配网层:提供无线配网和声波配网等功能;
WLAN Manager 管理层:可以对 WLAN 设备进行控制和管理,具有设置模式、链接热点、断开热点、启动热点、扫描热点等 WLAN 控制相关的功能,还提供断线重连、自动切换热点等管理功能;
WLAN Protocol 协议层:将数据流递交给具体网络协议进行解析,用户能够指定使用不一样的协议进行通讯(本文使用LwIP协议);
WLAN Config 参数管理层:管理链接成功的热点信息及密码,并写入非易失的存储介质中,能够为用户提供自动链接曾连热点的服务;
WLAN Device 驱动接口层:对接具体 WLAN 硬件(本文使用AP6181 WIFI 模块),为管理层提供统一的调用接口。
在WLAN Protocol 与 APP 层之间还应包含网络协议层(好比LwIP),甚至是套接字抽象层SAL(包括网络设备无关层netdev),这些并无表如今上面的WLAN 框架图中,下文介绍LwIP协议栈移植时再详说。github
2、WLAN Device实现与AP6181 WLAN驱动移植
2.1 WLAN Device驱动接口层
WLAN设备数据结构
// rt-thread-4.0.1\components\drivers\wlan\wlan_dev.h
struct rt_wlan_device
{
struct rt_device device;
rt_wlan_mode_t mode;
struct rt_mutex lock;
struct rt_wlan_dev_event_desc handler_table[RT_WLAN_DEV_EVT_MAX][RT_WLAN_DEV_EVENT_NUM];
rt_wlan_pormisc_callback_t pormisc_callback;
const struct rt_wlan_dev_ops *ops;
rt_uint32_t flags;
void *prot;
void *user_data;
};
typedef enum
{
RT_WLAN_NONE,
RT_WLAN_STATION,
RT_WLAN_AP,
RT_WLAN_MODE_MAX
} rt_wlan_mode_t;
struct rt_wlan_dev_event_desc
{
rt_wlan_dev_event_handler handler;
void *parameter;
};
typedef void (*rt_wlan_dev_event_handler)(struct rt_wlan_device *device, rt_wlan_dev_event_t event, struct rt_wlan_buff *buff, void *parameter);
typedef void (*rt_wlan_pormisc_callback_t)(struct rt_wlan_device *device, void *data, int len);
struct rt_wlan_dev_ops
{
rt_err_t (*wlan_init)(struct rt_wlan_device *wlan);
rt_err_t (*wlan_mode)(struct rt_wlan_device *wlan, rt_wlan_mode_t mode);
rt_err_t (*wlan_scan)(struct rt_wlan_device *wlan, struct rt_scan_info *scan_info);
rt_err_t (*wlan_join)(struct rt_wlan_device *wlan, struct rt_sta_info *sta_info);
rt_err_t (*wlan_softap)(struct rt_wlan_device *wlan, struct rt_ap_info *ap_info);
rt_err_t (*wlan_disconnect)(struct rt_wlan_device *wlan);
rt_err_t (*wlan_ap_stop)(struct rt_wlan_device *wlan);
rt_err_t (*wlan_ap_deauth)(struct rt_wlan_device *wlan, rt_uint8_t mac[]);
rt_err_t (*wlan_scan_stop)(struct rt_wlan_device *wlan);
int (*wlan_get_rssi)(struct rt_wlan_device *wlan);
rt_err_t (*wlan_set_powersave)(struct rt_wlan_device *wlan, int level);
int (*wlan_get_powersave)(struct rt_wlan_device *wlan);
rt_err_t (*wlan_cfg_promisc)(struct rt_wlan_device *wlan, rt_bool_t start);
rt_err_t (*wlan_cfg_filter)(struct rt_wlan_device *wlan, struct rt_wlan_filter *filter);
rt_err_t (*wlan_set_channel)(struct rt_wlan_device *wlan, int channel);
int (*wlan_get_channel)(struct rt_wlan_device *wlan);
rt_err_t (*wlan_set_country)(struct rt_wlan_device *wlan, rt_country_code_t country_code);
rt_country_code_t (*wlan_get_country)(struct rt_wlan_device *wlan);
rt_err_t (*wlan_set_mac)(struct rt_wlan_device *wlan, rt_uint8_t mac[]);
rt_err_t (*wlan_get_mac)(struct rt_wlan_device *wlan, rt_uint8_t mac[]);
int (*wlan_recv)(struct rt_wlan_device *wlan, void *buff, int len);
int (*wlan_send)(struct rt_wlan_device *wlan, void *buff, int len);
};
结构体 rt_wlan_device 继承自设备基类 rt_device,天然须要将其注册到 I/O 设备管理层。rt_wlan_device 成员还包括WLAN设备工做模式(Access Point模式仍是Station模式)、WLAN设备访问互斥锁、WLAN事件回调函数组、WLAN混杂模式回调函数、须要底层驱动实现并注册的WLAN接口函数集合rt_wlan_dev_ops、WLAN标识位(用于标识工做模式或自动链接状态等)、WLAN设备使用的网络协议栈信息、私有数据等。web
WLAN接口函数及设备注册过程
WLAN设备驱动(这里指的是AP6181 WLAN驱动)须要向WLAN管理框架注册接口函数集合rt_wlan_dev_ops,以便WLAN管理框架对外提供的接口能正常工做,这个函数集合rt_wlan_dev_ops是如何注册到WLAN管理框架的呢?算法
// rt-thread-4.0.1\components\drivers\wlan\wlan_dev.c
rt_err_t rt_wlan_dev_register(struct rt_wlan_device *wlan, const char *name, const struct rt_wlan_dev_ops *ops, rt_uint32_t flag, void *user_data)
{
rt_err_t err = RT_EOK;
if ((wlan == RT_NULL) || (name == RT_NULL) || (ops == RT_NULL))
......
rt_memset(wlan, 0, sizeof(struct rt_wlan_device));
#ifdef RT_USING_DEVICE_OPS
wlan->device.ops = &wlan_ops;
#else
......
#endif
wlan->device.user_data = RT_NULL;
wlan->device.type = RT_Device_Class_NetIf;
wlan->ops = ops;
wlan->user_data = user_data;
wlan->flags = flag;
err = rt_device_register(&wlan->device, name, RT_DEVICE_FLAG_RDWR);
return err;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops wlan_ops =
{
_rt_wlan_dev_init,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
_rt_wlan_dev_control
};
#endif
从函数rt_wlan_dev_register 的代码能够看出,该函数不只完成了将函数集合rt_wlan_dev_ops注册到WLAN管理框架的工做(经过参数传递),还完成了将函数集合wlan_ops(经过调用rt_wlan_dev_ops接口实现的rt_device_ops接口)注册到 I/O 设备管理框架的工做,注册的WLAN设备类型为网络接口设备RT_Device_Class_NetIf。编程
完成WLAN设备向WLAN管理框架和 I/O 设备管理框架的注册后,就可使用 I/O 设备管理层接口或WLAN Device层提供的接口访问WLAN设备了,咱们先看下WLAN设备向 I/O 设备管理层注册的函数集合 wlan_ops 的实现代码:api
// rt-thread-4.0.1\components\drivers\wlan\wlan_dev.c
static rt_err_t _rt_wlan_dev_init(rt_device_t dev)
{
struct rt_wlan_device *wlan = (struct rt_wlan_device *)dev;
rt_err_t result = RT_EOK;
rt_mutex_init(&wlan->lock, "wlan_dev", RT_IPC_FLAG_FIFO);
if (wlan->ops->wlan_init)
result = wlan->ops->wlan_init(wlan);
......
return result;
}
static rt_err_t _rt_wlan_dev_control(rt_device_t dev, int cmd, void *args)
{
struct rt_wlan_device *wlan = (struct rt_wlan_device *)dev;
rt_err_t err = RT_EOK;
WLAN_DEV_LOCK(wlan);
switch (cmd)
{
case RT_WLAN_CMD_MODE:
{
rt_wlan_mode_t mode = *((rt_wlan_mode_t *)args);
if (wlan->ops->wlan_mode)
err = wlan->ops->wlan_mode(wlan, mode);
break;
}
case RT_WLAN_CMD_SCAN:
{
struct rt_scan_info *scan_info = args;
if (wlan->ops->wlan_scan)
err = wlan->ops->wlan_scan(wlan, scan_info);
break;
}
case RT_WLAN_CMD_JOIN:
{
struct rt_sta_info *sta_info = args;
if (wlan->ops->wlan_join)
err = wlan->ops->wlan_join(wlan, sta_info);
break;
}
case RT_WLAN_CMD_SOFTAP:
{
struct rt_ap_info *ap_info = args;
if (wlan->ops->wlan_softap)
err = wlan->ops->wlan_softap(wlan, ap_info);
break;
}
case RT_WLAN_CMD_DISCONNECT:
{
if (wlan->ops->wlan_disconnect)
err = wlan->ops->wlan_disconnect(wlan);
break;
}
case RT_WLAN_CMD_AP_STOP:
{
if (wlan->ops->wlan_ap_stop)
err = wlan->ops->wlan_ap_stop(wlan);
break;
}
case RT_WLAN_CMD_AP_DEAUTH:
{
if (wlan->ops->wlan_ap_deauth)
err = wlan->ops->wlan_ap_deauth(wlan, args);
break;
}
case RT_WLAN_CMD_SCAN_STOP:
{
if (wlan->ops->wlan_scan_stop)
err = wlan->ops->wlan_scan_stop(wlan);
break;
}
case RT_WLAN_CMD_GET_RSSI:
{
int *rssi = args;
if (wlan->ops->wlan_get_rssi)
*rssi = wlan->ops->wlan_get_rssi(wlan);
break;
}
case RT_WLAN_CMD_SET_POWERSAVE:
{
int level = *((int *)args);
if (wlan->ops->wlan_set_powersave)
err = wlan->ops->wlan_set_powersave(wlan, level);
break;
}
case RT_WLAN_CMD_GET_POWERSAVE:
{
int *level = args;
if (wlan->ops->wlan_get_powersave)
*level = wlan->ops->wlan_get_powersave(wlan);
break;
}
case RT_WLAN_CMD_CFG_PROMISC:
{
rt_bool_t start = *((rt_bool_t *)args);
if (wlan->ops->wlan_cfg_promisc)
err = wlan->ops->wlan_cfg_promisc(wlan, start);
break;
}
case RT_WLAN_CMD_CFG_FILTER:
{
struct rt_wlan_filter *filter = args;
if (wlan->ops->wlan_cfg_filter)
err = wlan->ops->wlan_cfg_filter(wlan, filter);
break;
}
case RT_WLAN_CMD_SET_CHANNEL:
{
int channel = *(int *)args;
if (wlan->ops->wlan_set_channel)
err = wlan->ops->wlan_set_channel(wlan, channel);
break;
}
case RT_WLAN_CMD_GET_CHANNEL:
{
int *channel = args;
if (wlan->ops->wlan_get_channel)
*channel = wlan->ops->wlan_get_channel(wlan);
break;
}
case RT_WLAN_CMD_SET_COUNTRY:
{
rt_country_code_t country = *(rt_country_code_t *)args;
if (wlan->ops->wlan_set_country)
err = wlan->ops->wlan_set_country(wlan, country);
break;
}
case RT_WLAN_CMD_GET_COUNTRY:
{
rt_country_code_t *country = args;
if (wlan->ops->wlan_get_country)
*country = wlan->ops->wlan_get_country(wlan);
break;
}
case RT_WLAN_CMD_SET_MAC:
{
rt_uint8_t *mac = args;
if (wlan->ops->wlan_set_mac)
err = wlan->ops->wlan_set_mac(wlan, mac);
break;
}
case RT_WLAN_CMD_GET_MAC:
{
rt_uint8_t *mac = args;
if (wlan->ops->wlan_get_mac)
err = wlan->ops->wlan_get_mac(wlan, mac);
break;
}
default:
break;
}
WLAN_DEV_UNLOCK(wlan);
return err;
}
函数集合 wlan_ops 的实现最终都是靠调用WLAN设备驱动提供的函数集合rt_wlan_dev_ops,并且WLAN设备的管理配置主要靠函数rt_device_control 经过发送不一样的命令码和参数实现。WLAN Device层提供的接口函数又是经过调用函数集合 wlan_ops 实现的,下面给出WLAN Device层对外提供的接口函数声明:数组
// rt-thread-4.0.1\components\drivers\wlan\wlan_dev.h
/* wlan device init */
rt_err_t rt_wlan_dev_init(struct rt_wlan_device *device, rt_wlan_mode_t mode);
/* wlan device station interface */
rt_err_t rt_wlan_dev_connect(struct rt_wlan_device *device, struct rt_wlan_info *info, const char *password, int password_len);
rt_err_t rt_wlan_dev_disconnect(struct rt_wlan_device *device);
int rt_wlan_dev_get_rssi(struct rt_wlan_device *device);
/* wlan device ap interface */
rt_err_t rt_wlan_dev_ap_start(struct rt_wlan_device *device, struct rt_wlan_info *info, const char *password, int password_len);
rt_err_t rt_wlan_dev_ap_stop(struct rt_wlan_device *device);
rt_err_t rt_wlan_dev_ap_deauth(struct rt_wlan_device *device, rt_uint8_t mac[6]);
/* wlan device scan interface */
rt_err_t rt_wlan_dev_scan(struct rt_wlan_device *device, struct rt_wlan_info *info);
rt_err_t rt_wlan_dev_scan_stop(struct rt_wlan_device *device);
/* wlan device mac interface */
rt_err_t rt_wlan_dev_get_mac(struct rt_wlan_device *device, rt_uint8_t mac[6]);
rt_err_t rt_wlan_dev_set_mac(struct rt_wlan_device *device, rt_uint8_t mac[6]);
/* wlan device powersave interface */
rt_err_t rt_wlan_dev_set_powersave(struct rt_wlan_device *device, int level);
int rt_wlan_dev_get_powersave(struct rt_wlan_device *device);
/* wlan device event interface */
rt_err_t rt_wlan_dev_register_event_handler(struct rt_wlan_device *device, rt_wlan_dev_event_t event, rt_wlan_dev_event_handler handler, void *parameter);
rt_err_t rt_wlan_dev_unregister_event_handler(struct rt_wlan_device *device, rt_wlan_dev_event_t event, rt_wlan_dev_event_handler handler);
void rt_wlan_dev_indicate_event_handle(struct rt_wlan_device *device, rt_wlan_dev_event_t event, struct rt_wlan_buff *buff);
/* wlan device promisc interface */
rt_err_t rt_wlan_dev_enter_promisc(struct rt_wlan_device *device);
rt_err_t rt_wlan_dev_exit_promisc(struct rt_wlan_device *device);
rt_err_t rt_wlan_dev_set_promisc_callback(struct rt_wlan_device *device, rt_wlan_pormisc_callback_t callback);
void rt_wlan_dev_promisc_handler(struct rt_wlan_device *device, void *data, int len);
/* wlan device filter interface */
rt_err_t rt_wlan_dev_cfg_filter(struct rt_wlan_device *device, struct rt_wlan_filter *filter);
/* wlan device channel interface */
rt_err_t rt_wlan_dev_set_channel(struct rt_wlan_device *device, int channel);
int rt_wlan_dev_get_channel(struct rt_wlan_device *device);
/* wlan device country interface */
rt_err_t rt_wlan_dev_set_country(struct rt_wlan_device *device, rt_country_code_t country_code);
rt_country_code_t rt_wlan_dev_get_country(struct rt_wlan_device *device);
/* wlan device datat transfer interface */
rt_err_t rt_wlan_dev_report_data(struct rt_wlan_device *device, void *buff, int len);
/* wlan device register interface */
rt_err_t rt_wlan_dev_register(struct rt_wlan_device *wlan, const char *name,
const struct rt_wlan_dev_ops *ops, rt_uint32_t flag, void *user_data);
WLAN Device层提供的这些接口函数咱们虽然能够在应用程序中直接调用,但函数参数有不少结构体类型,在调用这些接口函数前,须要先构造接口函数参数须要的结构体,这就给函数调用带来了不便。WLAN Device层上面的WLAN Manager 层则对这些接口函数进行了再次封装,使用一些全局变量保存必要的信息,简化了参数的构造,咱们直接调用WLAN Manager 层提供的接口函数更加方便友好,这些接口函数在下文介绍。缓存
2.2 AP6181 WLAN驱动移植
Pandora开发板的程序源码包并无为咱们提供AP6181 WLAN驱动的源码,而是以库文件的形式给出的,因此这里也无法分析AP6181 WLAN驱动的实现原理,只能根据 SDIO 设备管理框架与WLAN 管理框架对WLAN设备驱动的要求推测一些AP6181 WLAN驱动移植时应实现或调用的函数。这里忍不住吐槽一下提供Pandora开发板 AP6181 WLAN驱动库文件的同窗,起码应该给出一些关于AP6181 WLAN驱动库文件如何使用、须要为其实现哪些接口函数、对外提供哪些接口函数、简单的实现原理之类的说明文档,如今缺乏这些信息为WLAN驱动移植和调试带来了很大的不便。sass
AP6181 WLAN固件配置
从前篇博客:SDIO设备对象管理 + AP6181(BCM43362) WiFi模块了解到,AP6181 WIFI 模组内部是须要运行WLAN固件程序的,AP6181 内部可能没有ROM空间,这就须要咱们将AP6181 内运行的WLAN固件程序存放到主控端的Flash 空间内。在使用WLAN设备前,由WLAN驱动程序负责将Host 端Flash内存放的WLAN固件读取并传送到AP6181 模组内,以便AP6181 WIFI 模组能正常工做(好比完成WIFI数据帧与以太网数据帧之间的转换)。
这里提醒一点,本文使用的AP6181的WLAN固件与驱动都是从Pandora开发板提供的源码包中得到的,且因为WLAN固件与驱动都是以库文件的形式提供的,对运行环境(好比RT-Thread版本)变动比较敏感,所以最好选择与本身使用的RT-Thread版本一致的 Pandora IOT 源码包。好比我使用的是RT-Thread 4.0.1,正点原子官网给的Pandora IOT 源码包默认的基于RT-Thread 4.0.0开发的,我就须要到GitHub 下载基于RT-Thread 4.0.1 版本的Pandora IOT 源码包(本文使用的是Release 1.2.0版本)。下文中使用的AP6181 WLAN固件与驱动都是从Pandora IOT Board Release 1.2.0版本源码包拷贝来的。
AP6181 WLAN固件所在路径:
.\IoT_Board\examples\16_iot_wifi_manager\bin\wifi_image_1.0.rbl
咱们须要先将该WLAN固件放入Flash(Pandora上的W25Q128芯片)的 wifi_image 分区,本文使用的工程文件是基于博客:FAL分区管理与easyflash变量管理中完成FAL与Easyflash组件移植后的工程文件为基础的。在上面的博客中已经FAL(Flash Abstraction Layer)的实现原理及接口函数,并且在移植FAL组件时配置到分区表也包括wifi_image 分区,这里能够直接该分区存储 AP6181 WLAN 固件镜像文件。
咱们如何将AP6181 WLAN固件(wifi_image_1.0.rbl)放到W25Q128 Flash内的wifi_image 分区呢?能够参考下面的文档:
.\IoT_Board\docs\UM3001-RT-Thread-IoT Board WIFI 模块固件下载手册.pdf
比较简单的方法是先将WLAN固件放到SD卡以下目录中:
/SYSTEM/WIFI/wifi_image_1.0.rbl
而后将SD卡插入到Pandora开发板的SD卡插槽,将综合例程文件(以下路径)烧录到Pandora开发板中:
.\IoT_Board\examples\30_iot_board_demo\bin\all.bin
综合例程文件烧录完成后,Pandora开发板检测到WLAN固件,会自动执行读取、校验、升级WLAN固件的操做,Pandora开发板的LCD也会显示相应的升级信息(若是wifi_image
分区已存在WLAN固件,且与放入SD卡中的WLAN固件版本一致,则不会有相应的加载或升级操做)。
接下来就是AP6181 WLAN驱动负责将存储在W25Q128 Flash wifi_image 分区的WLAN固件读取出来,并经过SDIO总线传输到AP6181 模组内。因为WLAN驱动是以库文件的形式提供的,咱们直接从Pandora源码包将WLAN驱动库文件和WLAN驱动移植文件复制到咱们的工程中使用,这些文件在Pandora源码包中的路径和复制到咱们工程目录的路径以下:
// Pandora IOT Board Release 1.2.0中WLAN驱动库文件和WLAN驱动移植文件路径
.\IoT_Board\libraries\wifi\libwifi_6181_0.2.5_armcm4_gcc.a
.\IoT_Board\libraries\wifi\libwifi_6181_0.2.5_armcm4_iar.a
.\IoT_Board\libraries\wifi\libwifi_6181_0.2.5_armcm4_keil.lib
.\IoT_Board\libraries\wifi\SConscript
.\IoT_Board\drivers\drv_wlan.h
.\IoT_Board\drivers\drv_wlan.c
// WLAN驱动库文件和WLAN驱动移植文件拷贝到咱们工程中的目标路径
.\RT-Thread_Projects\libraries\wifi\libwifi_6181_0.2.5_armcm4_gcc.a
.\RT-Thread_Projects\libraries\wifi\libwifi_6181_0.2.5_armcm4_iar.a
.\RT-Thread_Projects\libraries\wifi\libwifi_6181_0.2.5_armcm4_keil.lib
.\RT-Thread_Projects\libraries\wifi\SConscript
.\RT-Thread_Projects\libraries\HAL_Drivers\drv_wlan.h
.\RT-Thread_Projects\libraries\HAL_Drivers\drv_wlan.c
WLAN驱动库文件和WLAN驱动移植文件复制到咱们工程中后,须要能编译进咱们的工程,所以须要修改SConscript文件和SConstruct文件,将咱们拷贝过来的文件添加进编译脚本,新增编译代码以下:
// .\RT-Thread_Projects\libraries\HAL_Drivers\SConscript
......
# add wlan driver code
if GetDepen