###1. wext
linux上, 无线网卡驱动的控制接口有两个标准:
- wext : Wireless Extension, 使用wext的工具通过ioctl接口与driver通信, 如iwpriv, ifconfig等
- nl80211 : 使用nl80211的工具通过netlink来与driver通信, 例如iw, iwconfig
wext只能由userspace来发起通信, 而nl80211则可由kernel来发起, 并且支持消息多播, nl80211是今后的标准, 而wext则不再添加新的功能, 只是维护现在的代码
虽然nl80211已经成为主流, 但是相比较之下, wext开发和使用较为简单, 因此, 现在的许多driver中wext和nl80211被共同使用, 其中, nl80211用于实现userspace和driver通信, 而wext则用于实现一些厂商的私有command, 用于支持一些调试工具
由于wext在某些版本的改动较大, 因此, 后续只讨论wext 11 之后的版本
###2. wext的ioctl接口
####2.1 ioctl id range wext的ioctl的id范围为 SIOCIWFIRST (0x8B00) ~ SIOCIWLASTPRIV (0x8BFF), 其中
- SIOCSIWCOMMIT (0x8B00) ~ SIOCSIWPMKSA (0x8B36): 用于wext的standard ioctl
- SIOCIWFIRSTPRIV (0x8BE0) ~ SIOCIWLASTPRIV (0x8BFF) : 用于wext的private ioctl, 由各driver自行分配
注意, 在 kernel<=2.5.X wext<11 时, 曾经使用 SIOCDEVPRIVATE ~ SIOCDEVPRIVATE+15 作为private ioctl的id range
####2.2 ioctl 交换数据
wext ioctl 通过 iwreq 结构体和内核交换数据
#define IFNAMSIZ 16
struct iwreq
{
union
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */
} ifr_ifrn;
union iwreq_data u;
};
- iwreq.ifrn_name保存网络接口的名称
- 对于大小固定, 且不超过 16 byte的数据, 直接存储在 iwreq.u.name 中传递
- 对于大小不固定, 或者大小超过16byte的数据, 通过 iwreq.u.data.pointers 传递指针, 通过 iwreq.u.data.length 传递长度, 由wext core或者对应ioctl的iw_handler自行拷贝数据
iwreq_data的定义如下
union iwreq_data
{
char name[IFNAMSIZ];
/* Name : used to verify the presence of wireless extensions.
* Name of the protocol/provider... */
struct iw_point essid; /* Extended network name */
struct iw_param nwid; /* network id (or domain - the cell) */
struct iw_freq freq; /* frequency or channel :
* 0-1000 = channel
* > 1000 = frequency in Hz */
struct iw_param sens; /* signal level threshold */
struct iw_param bitrate; /* default bit rate */
struct iw_param txpower; /* default transmit power */
struct iw_param rts; /* RTS threshold threshold */
struct iw_param frag; /* Fragmentation threshold */
__u32 mode; /* Operation mode */
struct iw_param retry; /* Retry limits & lifetime */
struct iw_point encoding; /* Encoding stuff : tokens */
struct iw_param power; /* PM duration/timeout */
struct iw_quality qual; /* Quality part of statistics */
struct sockaddr ap_addr; /* Access point address */
struct sockaddr addr; /* Destination address (hw/mac) */
struct iw_param param; /* Other small parameters */
struct iw_point data; /* Other large parameters */
};
struct iw_point
{
void __user *pointer; /* Pointer to the data (in user space) */
__u16 length; /* number of fields or size in bytes */
__u16 flags; /* Optional params */
};
####2.3 SET / GET
wext 将ioctl分为两类: SET/GET
- SET : ioctl id为偶数, 只限root用户, 且没有返回数据(即 iw_priv_args.get_args 为空)
- GET : ioctl id为奇数, 可以由任何用户发起, 且不期望传递任何参数
standard ioctl严格遵守这一限定, 在实现private ioctl时, 也应该遵守这一约定(若需要实现同时set和get, 可使用两个ioctl来分别get/set), 当然, 对于private ioctl来说, 这一要求不是强制的, 仍有可能在同一个ioctl中传递get和set参数:
若get和set的数据都能直接包含在iwreq_data中传递, 或者都通过iwreq_data来传递指针时,可以同时传递get/set数据, 若混合使用这两种方式在同一个private ioctl 中传递set args/get args,则iwpriv不能正确处理
###3. ioctl 从userspace到kernel中wext core
kernel中对应的系统调用定义为
/* userspace */
ioctl()
/* kernel */
SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) ->
do_vfs_ioctl() ->
vfs_ioctl() ->
filp->f_op->unlocked_ioctl() ->
而socket的create过程有
/* userspace */
socket()
/* kernel */
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) ->
sock_map_fd() ->
sock_alloc_file() ->
alloc_file(&path, FMODE_READ | FMODE_WRITE,&socket_file_ops) ->
file->f_op = fop;
static const struct file_operations socket_file_ops = {
......
.unlocked_ioctl = sock_ioctl,
......
}
因此, socket fd 的ioctl在kernel中由 sock_ioctl()来处理
接下来, 只关注wext ioctl的处理流程
/* kernel */
sock_ioctl() ->
dev_ioctl() ->
wext_handle_ioctl()
因此, 总结起来, wext ioctl的分发流程为
/* userspace */
ioctl()
/* kernel */
SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) ->
do_vfs_ioctl() ->
vfs_ioctl() ->
sock_ioctl() ->
dev_ioctl() ->
wext_handle_ioctl()
###3. driver实现wext接口
####3.1 wext ioctl id的范围
先来看WEXT定义的标准的ioctl,位于 kernel/include/uapi/linux/wireless.h中
/* Wireless Identification */
#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */
......
/* WPA2 : PMKSA cache management */
#define SIOCSIWPMKSA 0x8B36 /* PMKSA cache operation */
另外, 每一个netdevice, 可以定义自己私有的wext ioctl, 但是id只能位于以下区间, 因此, 每一个netdevice最多只能有32个private的wext ioctl,具体的使用由driver自己分配
#define SIOCIWFIRSTPRIV 0x8BE0
#define SIOCIWLASTPRIV 0x8BFF
####3.2 iw_handler_def
一个netdevice想要注册wext时, 需要定义一个iw_handler_def
struct iw_handler_def {
const iw_handler * standard;
__u16 num_standard;
#ifdef CONFIG_WEXT_PRIV
__u16 num_private;
const iw_handler * private;
const struct iw_priv_args * private_args;
#endif
struct iw_statistics* (*get_wireless_stats)(struct net_device *dev);
};
- iw_handler_def.standard : 保存wext standard ioctl的handler的数组
- iw_handler_def.num_standard : standard ioctl的iw_handler数组的个数
- iw_handler_defprivate : 保存wext private ioctl的handler的数组
- iw_handler_def.num_private : private ioctl的iw_handler数组的个数
- iw_handler_def.get_wireless_stats : 用于读取状态
- private_args : 因为wext standard的ioctl定义(例如参数,返回值)是固定的, 但是private的ioctl的定义却各不相同,因此, 为了让外部工具, 例如iwpriv能够了解private ioctl的定义, 需要使用private_args来描述private ioctl
####3.3 iw_handler
wext通过ioctl来与kernel通信, 无论是standard的ioctl还是private的ioctl, 都需要定义一个处理函数来被wext core来回调, 其原型为
typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra);
在实际操作时, 因为ioctl多于1个, 因此通常是定义一个数组, 并且其在数组中的顺序要按照其对应的ioctl 的id的顺序
####3.4 driver实现standard ioctl handler
iw_handler_def.standard保存了wext standard ioctl的handler的数组,且数组元素的个数保存在iw_handler_def.num_standard中, standard ioctl handler 从 SIOCSIWCOMMIT ~ SIOCSIWPMKSA, 依次定义, 对于未实现的, 填充NULL, 另外, ioctl id range中未使用的空洞部分, 也填NULL
例如, driver实现了 SIOCSIWCOMMIT 和 SIOCGIWNAME 的handler, 分别为 iw_set_commit() 和 iw_get_name()
static const iw_handler we_handler[] =
{
(iw_handler) iw_set_commit, /* SIOCSIWCOMMIT */
(iw_handler) iw_get_name, /* SIOCGIWNAME */
(iw_handler) NULL, /* SIOCSIWNWID */
(iw_handler) NULL, /* SIOCGIWNWID */
......
}
####3.5 driver实现private ioctl handler
private ioctl handler的定义也要按找ioctl id 的顺序, 示例如下:
#define WLAN_GET_LINK_SPEED (SIOCIWFIRSTPRIV + 31)
static const iw_handler we_private[] = {
......
[WLAN_GET_LINK_SPEED - SIOCIWFIRSTPRIV] = iw_get_linkspeed_priv,
......
}
除了实现private ioctl 的handler, 还需要提供 iw_priv_args
iw_priv_args 的定义如下
struct iw_priv_args
{
__u32 cmd; /* Number of the ioctl to issue */
__u16 set_args; /* Type and number of args */
__u16 get_args; /* Type and number of args */
char name[IFNAMSIZ]; /* Name of the extension */
};
- iw_priv_arg.cmd 存储对应ioctl的id
- iw_priv_arg.set_args : ioctl的输入参数, 包括类型, 数目
- iw_priv_arg.get_args : ioctl的输出结果, 包括类型, 数目
-
- iw_priv_arg.name : 该ioctl的别名,注意不能超过16byte,可以直接使用该别名通过iwpriv来发起该ioctl
其中ioctl 的输入参数和输出参数的类型(其中, 后12位存储数据的数目,字符串按字节计数)定义如下:
#define IW_PRIV_TYPE_NONE 0x0000
#define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */
#define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */
#define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */
#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */
#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */
#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */
#define IW_PRIV_SIZE_MASK 0x07FF
如果是固定大小的参数且参数个数固定,并且小于16byte 则会直接通过iwreq结构体来传递, 否则只传递数据在userspace的指针,在kernel中进行拷贝 几个示例如下:
IW_PRIV_TYPE_CHAR| WE_MAX_STR_LEN,
IW_PRIV_TYPE_CHAR | 18,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
IW_PRIV_TYPE_BYTE | MAX_OEM_DATA_RSP_LEN,
IW_PRIV_TYPE_BYTE | WE_MAX_STR_LEN,
iw_handler_def.private_args为一个数组, 其中每一个条目描述一条private ioctl, 因此条目个数应该等于iw_handler_def.num_private的个数, 一个iw_priv_arg 条目的示例如下:
{
WLAN_GET_LINK_SPEED,
IW_PRIV_TYPE_CHAR | 18,
IW_PRIV_TYPE_CHAR | 5,
"getLinkSpeed"
},
####3.6 注册wext接口
最后注册iw_handler_def, 将iw_handler_def赋值给net_device.wireless_handlers即可
dev->wireless_handlers = (struct iw_handler_def *)&we_handler_def;
####3.7 sub ioctl
有时候, 32个private ioctl id对于driver来说是不够的, 因此, wext引入 了sub-ioctl(在wext 24之前,一个ioctl的sub-ioctl不能呢个超过16个)
如何添加sub ioctl:
- 首先在 iw_priv_args 中定义一个ioctl, 但是不要定义别名, 应留为空字符串
- 在 iw_priv_args 中紧跟在上面一步中定义的定义ioctl之后, 定义sub-ioctl, id由自己定义
- 在 iw_handler_def.private_handler 中为第一步中定义的ioctl添加handler, 在该handler中处理所有的sub-ioctl
例如
/* handlers for main ioctl */
{ WLAN_PRIV_SET_INT_GET_NONE,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0,
"" },
/* handlers for sub-ioctl */
{ WE_SET_11D_STATE,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0,
"set11Dstate" },
......
static const iw_handler we_private[] = {
[WLAN_PRIV_SET_INT_GET_NONE - SIOCIWFIRSTPRIV] = iw_setint_getnone, //set priv ioctl
......
}
WLAN_PRIV_SET_INT_GET_NONE是一个main ioctl, 而WE_SET_11D_STATE则是一个sub ioctl
定义sub ioctl时, main ioctl中的set_args或者get_args的定义应该能满足所有的sub ioctl定义
若定义了sub ioctl则在main ioctl的handler被调用时, 获取sub-ioctl的id方式
typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra);
- 如果使用iwreq_data来传递数据(即ioctl的参数比较少,直接传递), 则开始的4个byte用于存储sub-ioctl id
- 如果使用iw_point来传递数据(即ioctl的参数比较多,传递指针来拷贝), 则sub-ioctl id存储在iw_point.flags中
####3.8 driver定义wext接口的示例
/* 定义 standard ioctl handler */
static const iw_handler we_handler[] =
{
(iw_handler) iw_set_commit, /* SIOCSIWCOMMIT */
(iw_handler) iw_get_name, /* SIOCGIWNAME */
(iw_handler) NULL, /* SIOCSIWNWID */
(iw_handler) NULL, /* SIOCGIWNWID */
....
};
/* 定义private ioctl id */
#define WLAN_PRIV_SET_INT_GET_NONE (SIOCIWFIRSTPRIV + 0) //用于扩展sub-ioctl
#define WE_SET_11D_STATE 1 //sub-ioctl
#define WE_WOWL 2 //sub-ioctl
......
#define WLAN_SET_BAND_CONFIG (SIOCIWFIRSTPRIV + 25)
......
#define WLAN_GET_LINK_SPEED (SIOCIWFIRSTPRIV + 31)
/* 定义private ioctl 的handler */
static const iw_handler we_private[] = {
[WLAN_PRIV_SET_INT_GET_NONE - SIOCIWFIRSTPRIV] = iw_setint_getnone, //set priv ioctl
......
[WLAN_SET_BAND_CONFIG - SIOCIWFIRSTPRIV] = iw_set_band_config,
......
[WLAN_GET_LINK_SPEED - SIOCIWFIRSTPRIV] = iw_get_linkspeed_priv,
};
/* 定义iw_priv-args */
static const struct iw_priv_args we_private_args[] = {
/* handlers for main ioctl */
{ WLAN_PRIV_SET_INT_GET_NONE,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0,
"" },
/* handlers for sub-ioctl */
{ WE_SET_11D_STATE,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0,
"set11Dstate" },
{ WE_WOWL,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0,
"wowl" },
......
{
WLAN_SET_BAND_CONFIG,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0,
"SETBAND" },
......
{
WLAN_GET_LINK_SPEED,
IW_PRIV_TYPE_CHAR | 18,
IW_PRIV_TYPE_CHAR | 5, "getLinkSpeed" },
};
/* 定义we_handler_def */
const struct iw_handler_def we_handler_def = {
.num_standard = sizeof(we_handler) / sizeof(we_handler[0]),
.num_private = sizeof(we_private) / sizeof(we_private[0]),
.num_private_args = sizeof(we_private_args) / sizeof(we_private_args[0]),
.standard = (iw_handler *)we_handler,
.private = (iw_handler *)we_private,
.private_args = we_private_args,
.get_wireless_stats = get_wireless_stats,
};
/* 注册we_handler_def */
dev->wireless_handlers = (struct iw_handler_def *)&we_handler_def;
###4. iwpriv 执行 private ioctl
首先要知道的是, iwpriv能够获取到对应的interface的 iw_priv_args, 因此iwpriv能够知道其支持的private ioctl以及其参数的定义,具体原理后面会有介绍
iwpriv 中的 set_private_cmd() 完成发起private ioctl的工作
- 首先,查询iw_priv_args, 检查要执行的ioctl是否是invalid
- 检查是否是sub ioctl,若是, 则ioctl id需要4byte空间
- 检查是否有set args, 若有则拷贝到buffer中
- 若set args所占内存空间大小固定且set args和sub ioctl所占空间小于16byte, 则依次将 sub ioctl id, set args拷贝到iwreq.u.name中 若无set args, 且get args所占内存空间大小固定且小于16byte, 则将sub ioctl id存储在iwreq.u.name前4byte中
- 否则, 将 iwreq.u.data.pointer 指向buffer, iwreq.u.data.flags 存储sub ioctl id
- 执行ioctl()
- 若定义了get args, 且所占内存大小固定, 则将其从 iwreq.u.name 中拷贝到buffer中
- 若定义了get args, 但是所占内存大小不固定, 则返回值在driver中已经被copy_to_user()拷贝到buffer中了, 因此,只需从iwreq.u.data.length中获取数据的大小
- 最后, 根据iw_priv_args 中定义的get args的大小和类型, 输出buffer中的内容
从上面的过程可以看到, 若需要在同一个 ioctl 中同时使用set args和get args, 需要满足以下条件, 需要满足set args和get args同时通过iwreq.u.name来传递的方式,或者同时使用iwreq.u.data来传递的方式,因为只要两者使用不同的方式, 则有如下的情形
- set args使用 iwreq.u.name, get args使用iwreq.u.data, 则在给iwreq.u.data.pointers赋值时, 会破坏iwreq.u.name中的set args数据
- set args使用 iwreq.u.date, get args使用iwreq.u.name, 则不会去给iwreq.u.data.pointers赋值, 在ioctl执行时不能成功获取set args
当然, 在一个ioctl中只使用set args 或 get args, 则不会有这种问题
iwpriv使用的buffer大小为4096byte, 在使用iwreq.u.data来传递 set args/get args时, 需要注意不要超出大小
###5. wext ioctl 的处理
int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
void __user *arg)
{
......
ret = wext_ioctl_dispatch(net, ifr, cmd, &info,
ioctl_standard_call,
ioctl_private_call);
if (ret >= 0 && IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct iwreq)))
return -EFAULT;
......
}
static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr,
unsigned int cmd, struct iw_request_info *info,
wext_ioctl_func standard,
wext_ioctl_func private)
{
......
ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private);
......
}
static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
unsigned int cmd,
struct iw_request_info *info,
wext_ioctl_func standard,
wext_ioctl_func private)
{
......
if (cmd == SIOCGIWSTATS)
return standard(dev, iwr, cmd, info,
&iw_handler_get_iwstats);
......
#ifdef CONFIG_WEXT_PRIV
if (cmd == SIOCGIWPRIV && dev->wireless_handlers)
return standard(dev, iwr, cmd, info,iw_handler_get_private);
#endif
......
handler = get_handler(dev, cmd);
if (handler) {
/* Standard and private are not the same */
if (cmd < SIOCIWFIRSTPRIV)
return standard(dev, iwr, cmd, info, handler);
else if (private)
return private(dev, iwr, cmd, info, handler);
}
/* Old driver API : call driver ioctl handler */
if (dev->netdev_ops->ndo_do_ioctl)
return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);
......
}
最终, wext ioctl的处理过程如下:
- 对于standard ioctl, 调用standard() (事实上是ioctl_standard_call()) 并且传递该ioctl的iw_handler(保存在iw_handler_def.standard中)
- 对于private ioctl, 调用private() (事实上是ioctl_private_call) 并且传递该ioctl的iw_handler(保存在iw_handler_def.private中)
####5.1 standard ioctl的处理
/* 参数大小固定且小于16byte, 直接通过iwreq_data 来传递参数的ioctl */
ioctl_standard_call() ->
handler(dev, info, &(iwr->u), NULL);
在通过iwreq_data直接传递参数的情况下, iw_handler接收到的参数为
- dev : 对应的net_device
- info : 其中, info.cmd 当前执行的ioctl的id
- wrqu : ioctl直接传递的参数,需根据不同的ioctl具体解释
- extra : 固定为NULL
在这种情况下, ioctl的返回数据存放在 iwreq_data 中返回
/* 参数不固定或者较大, 通过iwreq_data.pointers来传递参数指针的ioctl */
ioctl_standard_call() ->
ioctl_standard_iw_point(&iwr->u.data, cmd, descr, handler, dev, info); ->
handler(dev, info, (union iwreq_data *) iwp, extra); 而在通过 iwreq_data.pointers 来传递参数时,iw_handler接收到的参数为
- dev : 对应的net_device
- info : 其中, info.cmd 当前执行的ioctl的id
- wrqu : wrqu->data 保存了数据的指针, 长度
- extra : 对于SET型的ioctl, 若有传递参数, wext core会分配内存, 将参数拷贝到kernel space,并将extra指向这一块内存 对于GET型的ioctl, 若有数据返回userspace, 则wext core会分配内存,并将extra指向这一块内存, iw_handler只需将返回数据存储在这一块内存中, wext core会负责将数据拷贝到userspace
事实上, 类似于private ioctl的iw_priv_args, standard ioctl使用了一个 standard_ioctl 数组来描述各个standard ioctl的参数的约束
####5.2 private ioctl的处理
/* 参数大小固定且小于16byte, 直接通过iwreq_data 来传递参数的ioctl */
ioctl_private_call() ->
get_priv_descr_and_size(dev, cmd, &descr);
handler(dev, info, &(iwr->u), (char *) &(iwr->u));
先看get_priv_descr_and_size(), 这一步是计算需要为拷贝参数来分配多少的内存空间:
- 对于SET型的ioctl,若要处理的是sub-ioctl, 则需要的空间为参数的大小加上4byte,若不为sub-ioctl, 则需要的空间大小为参数的大小
- 若参数大小固定(IW_PRIV_SIZE_FIXED), 且总和小于16byte, 则说明输入参数可以直接通过iwreq_data来传递, 无需分配内存来拷贝参数, 因此返回0
- 否则返回需要分配的内存的大小
- 对于GET型的ioctl, 则需要的空间大小为参数的大小,
- 若参数大小固定(IW_PRIV_SIZE_FIXED), 且总和小于16byte, 则说明返回数据可以直接通过iwreq_data来传递, 无需分配内存来拷贝参数, 因此返回0
- 否则返回需要分配的内存的大小
在通过iwreq_data直接传递参数的情况下, iw_handler接收到的参数为:
- dev : 对应的net_device
- info : 其中, info.cmd 当前执行的ioctl的id
- wrqu : ioctl直接传递的参数,需根据不同的ioctl具体解释
- extra : 同wrqu相同
在这种情况下,如果是sub-ioctl, 则sub-ioctl的id 存储在extra所指的内存的前4byte中,后面的是输入参数,ioctl的返回数据直接拷贝到 iwreq_data 中返回
/* 参数不固定或者较大, 通过iwreq_data.pointers来传递参数指针的ioctl */
ioctl_private_call() ->
get_priv_descr_and_size(dev, cmd, &descr);
ioctl_private_iw_point(&iwr->u.data, cmd, descr,handler, dev, info, extra_size);
handler(dev, info, (union iwreq_data *) iwp, extra);
而在通过 iwreq_data.pointers 来传递参数时,iw_handler接收到的参数为
- dev : 对应的net_device
- info : 其中, info.cmd 当前执行的ioctl的id
- wrqu : wrqu->data 保存了数据的指针, 长度
- extra : 对于SET型的ioctl, 若有传递参数, wext core会分配内存, 将参数拷贝到kernel space,并将extra指向这一块内存 对于GET型的ioctl, 若有数据返回userspace, 则wext core会分配内存,并将extra指向这一块内存, iw_handler只需将返回数据存储在这一块内存中, wext core会负责将数据拷贝到userspace
在这种情况下, 如果是sub-ioctl, sub-ioctl 的id通过wrqu->data.flags来获取
###6. iwpriv 获取 driver支持的private ioctl
在使用iwpriv 时, iwpriv能够直接列出各个网络设备接口的支持的private ioctl,以及各个private ioctl的set & get参数, 例如
# iwpriv wlan0
wlan0 Available private ioctls :
set11Dstate (0001) : set 1 int & get 0
wowl (0002) : set 1 int & get 0
setPower (0003) : set 1 int & get 0
......
getLinkSpeed (8BFF) : set 18 char & get 5 char
iwpriv获取这些信息是通过一个wext standard ioctl SIOCGIWPRIV来实现的, 处理过程如下
/* userspace iwpriv*/
main() ->
iw_enum_devices() ->
print_priv_info() ->
iw_get_priv_info() ->
iw_get_ext(skfd, ifname, SIOCGIWPRIV, &wrq);
/* kernel */
wext_handle_ioctl() ->
wext_ioctl_dispatch()
wireless_process_ioctl()
iw_handler_get_private()
事实上, iw_handler_get_private() 会将网络接口对应的 iw_priv_args 数组返回到userspace中去, 由iwpriv解析, 因此, iwpriv能够获取driver所支持的private ioctl的信息
###7. iwpriv及private ioctl的总结
- 若参数大小固定且小于16byte(若为sub-ioctl则还需要计算ioctl id的4byte)的数据,使用iwreq.u.name来传递, 否则使用iwreq.u.data来传递
- ioctl id为偶数的为SET型ioctl, 为奇数的为GET型的ioctl,SET型的ioctl需要root权限
- private ioctl最多32个, 若不能满足使用,则需要使用sub ioctl来扩充
- 最好不要在一个ioctl里面同时使用set args和get args, 若需要这样做, 则应该使用相同的方式来传递set args和get args, 即iwreq.u.name 或者 iwreq.u.data
- 若通过iwreq.u.data来传递参数指针时, iwpriv中提供的buffer大小为4096byte, 注意这一限制
- 若通过iwreq.u.data来传递参数指针时, kernel中wext core会分配内存并完成数据的拷贝
- 若为SET型的ioctl,则按照iw_priv_args.set_args中定义的大小来分配buffer, 然后调用copy_from_user()来将数据拷贝到kernel的buffer中
- 若为GET型的ioctl,则按照iw_priv_args.get_args中定义的大小来分配buffer, 然后调用copy_to_user()来将数据拷贝到userspace的buffer中
- 若需要同时使用set args和get args, 由于wext core只会协助完成一次copy_xxx_user(), 因此要根据ioctl id属于SET型 还是GET型, 在iw_handler中调用一次copy_xxx_user(),另外, wext core分配的内存大小会根据ioctl id属于SET型 还是GET型来使用iw_priv_args.set_args或者iw_priv_args.get_args, 因此,最好声明合适的大小, 以同时满足两者的大小
- iw_handler中获取参数
- typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra);
- 若使用iwreq.u.name来传递参数,则wrqu 和 extra相同, 都指向iwreq.u, 对于SET型ioctl, 可直接从extra中获取 set args;对于GET型ioctl,get args直接拷贝到该段内存中即可返回 ,若为sub-ioctl, 则其id存储在extra的前4byte中
- 若使用iwreq.u.data来传递参数, 则wrqu指向一个iwreq.u, 而extra则指向一段kernel中的buffer, 对于SET型ioctl,可直接从extra中获取 set args,长度存储在wrqu.data.length中;对于GET型的ioctl,可将get args拷贝到该块内存中, 并设置wrqu.data.length, 若为sub-ioctl, 则id存储在wrqu.data.flags中