3.4.1 Linux Wireless Extensions介绍
从开发者角度来说,wext的用法相当简单。Linux平台中,wext API定义于wireless.h文件。Android平台上,其文件位置在external/kernel-headers/original/linux目录下,主要供驱动开发者使用。
注意 bionic/libc/kernel/common/linux目录中也有一个wireless.h,不过此文件由工具程序根据Kernel中的wireless.h自动生成而来,供用户空间使用。两个文件的区别主要是bionic下的wireless.h包含很少的注释。所以本节将分析Kernel中的wireless.h。Android 4.2中的wext版本为20,由wireless.h中的宏WIRELESS_EXT定义。
虽然前面提到说ioctl函数的一个缺点是其没有指明参数类型,但wext却比较严谨,提供了自己的数据类型。
1.常用数据结构
首先,所有用户空间发起的请求都统一包括在struct iwreq中,其原型如下。
[—>wireless.h::struct iwreq]
- /*
- wext API在设计时参考了系统中现有数据结构及命名方式。作为区分,wext中几乎所有数据结构、类型、宏
- 等名字中都带一个w以代表wireless。如下面的iwreq结构体,其对应的普通数据结构类型是ifreq。
- 该结构体专门用于往socket句柄传递ioctrl控制参数。
- */
- struct iwreq
- {
- union
- {
- char ifrn_name[IFNAMSIZ]; // 用于指定要操作的网卡设备名,如wlan0
- } ifr_ifrn;
- union iwreq_data u; // 用于存储具体的参数信息
- };
如iwreq结构所示,具体的参数信息存储在另外一个联合体iwreq_data中,其原型如下。
[—>wireless.h::union:iwreq_data]
- /*
- iwreq_data是一个联合体,其最大size为16字节。
- wext还自定义了一些小的数据结构,如iw_point、iw_param、iw_freq等。它们的作用如下。
- iw_point:当参数信息的长度超过16字节时,就只能通过iw_point指向另外一块内存区域,而参数就存储
- 在那个区域中。这个就是我们常用的指针方式。
- iw_param:当参数信息不超过16字节时,可以把信息存储在iw_param中。
- iw_freq:用于存储频率或信道值。其原型的介绍见本小节最后。
- */
- union iwreq_data
- {
- char name[IFNAMSIZ];
- struct iw_point essid; // 存储essid,也就是ssid
- struct iw_param nwid; // network id
- // 频率或信道。取值为0-1000时代表channel,大于1000则代表频率,单位为Hz
- struct iw_freq freq;
- struct iw_param sens; // 信号强度阈值
- struct iw_param bitrate; // 码率
- struct iw_param txpower;
- struct iw_param rts; // RTS阈值时间
- struct iw_param frag;
- __u32 mode; // 操作模式
- struct iw_param retry;
- struct iw_point encoding;
- struct iw_param power;
- struct iw_quality qual;
- struct sockaddr ap_addr; // AP地址
- struct sockaddr addr; // 目标地址
- struct iw_param param; // 其他参数
- struct iw_point data; // 其他字节数超过16的参数
- };
当参数字节超过16的时候,wext还定义了和功能相关的参数类型,下面来看专门用于触发无线网卡发起扫描请求的数据结构iw_scan_req,其原型如下所示。
[—>wireless.h::struct iw_scan_req]
- struct iw_scan_req
- {
- __u8 scan_type; // 可取值为IW_SCAN_TYPE_{ACTIVE,PASSIVE}
- // 代表主动或被动扫描
- __u8 essid_len; // essid字符串长度
- __u8 num_channels; // 指明信道个数,如果为0,则表示扫描所有可允许的信道
- __u8 flags; // 目前仅用于字节对齐
- // bssid用于指明BSS的地址。如果全为FF则为广播BSSID,即wildcard bssid
- struct sockaddr bssid;
- __u8 essid[IW_ESSID_MAX_SIZE];// essid
- /*
- min_channel_time:指示扫描过程中在每个信道等待到第一个回复的时间。如果在此时间内没有等
- 到回复,跳到下一个信道等待。如果等到一个回复,则一共在该信道等待的最大时间为max_channel_time。
- 所有时间单位均为TU(Time Units),即1024ms。
- */
- __u32 min_channel_time;
- __u32 max_channel_time;
- struct iw_freq channel_list[IW_MAX_FREQUENCIES];// IW_MAX_FREQUENCIES值为32
- };
下面来看最后一个常见的数据结构iw_freq,其原型如下。
[—>wireless.h::struct:iw_freq]:
- // 当频率小于109,m直接等于频率。否则m=f/(10e)
- struct iw_freq
- {
- __s32 m;
- __s16 e;
- __u8 i; // 该值表示此频率对象在channel_list数组中的索引
- __u8 flags; // 固定或自动
- };
提示 wext中的数据结构和定义还有许多,建议读者结合实际需要去学习wireless.h。wext API虽然简单,但相信读者已经体会到其背后所依赖的和802.11规范密切相关的理论知识了。
下面通过一个实际的例子来看看用户空间如何通过wext API来触发无线网卡扫描工作的。
2.wext API使用实例
本例来源于wpa_supplicant,它是一个运行于用户空间的专门和无线网卡进行交互的程序。其详情将在下一章节进行介绍。本节仅通过一个函数看看wpa_supplicant如何利用wext API和无线网卡交互。
[—>driver_wext.c:wpa_driver_wext_scan]
- int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params)
- {
- struct wpa_driver_wext_data *drv = priv;
- struct iwreq iwr; // 定义一个iwreq对象
- int ret = 0, timeout;
- struct iw_scan_req req; // 定义一个iw_scan_req对象
- // 获取调用者传递的ssid等参数
- const u8 *ssid = params->ssids[0].ssid;
- size_t ssid_len = params->ssids[0].ssid_len;
- ......
- os_memset(&iwr, 0, sizeof(iwr));
- // 为iwr的ifr_name传递需操作的网卡设备名
- os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
- if (ssid && ssid_len) {
- os_memset(&req, 0, sizeof(req));
- // 设置iw_scan_req的信息
- req.essid_len = ssid_len;
- req.bssid.sa_family = ARPHRD_ETHER;
- // 设置bssid的MAC地址全为0XFF,代表这是一个wildcard BSSID搜索
- os_memset(req.bssid.sa_data, 0xff, ETH_ALEN);
- os_memcpy(req.essid, ssid, ssid_len);
- // 通过data域指向这个iw_sca_req对象
- iwr.u.data.pointer = (caddr_t) &req;
- iwr.u.data.length = sizeof(req);
- // IW_SCAN_THIS_ESSID表示只扫描指定ESSID的无线网络
- iwr.u.data.flags = IW_SCAN_THIS_ESSID;
- }
- /*
- ioctl_sock指向一个socket句柄,其创建时候的代码如下:
- ioctl_sock = socket(PF_INET,SOCK_DGRAM,0)
- SIOCSIWSCAN用于通知驱动进行无线网络扫描。
- */
- if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
- // 返回错误
- }
- ......// 其他处理
- return ret;
- }
提示 wext相对简单,但其内部的数据结构定义、变量命名等都和规范中定义的原语有着莫大的关系。建议读者结合规范去阅读wireless.h以加深理解。