###1. wext

linux上, 无线网卡驱动的控制接口有两个标准:

  1. wext : Wireless Extension, 使用wext的工具通过ioctl接口与driver通信, 如iwpriv, ifconfig等
  2. 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 */
};
  1. iw_priv_arg.cmd 存储对应ioctl的id
  2. iw_priv_arg.set_args : ioctl的输入参数, 包括类型, 数目
  3. iw_priv_arg.get_args : ioctl的输出结果, 包括类型, 数目
    1. 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:

  1. 首先在 iw_priv_args 中定义一个ioctl, 但是不要定义别名, 应留为空字符串
  2. 在 iw_priv_args 中紧跟在上面一步中定义的定义ioctl之后, 定义sub-ioctl, id由自己定义
  3. 在 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);
  1. 如果使用iwreq_data来传递数据(即ioctl的参数比较少,直接传递), 则开始的4个byte用于存储sub-ioctl id
  2. 如果使用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的处理过程如下:

  1. 对于standard ioctl, 调用standard() (事实上是ioctl_standard_call()) 并且传递该ioctl的iw_handler(保存在iw_handler_def.standard中)
  2. 对于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(), 这一步是计算需要为拷贝参数来分配多少的内存空间:

  1. 对于SET型的ioctl,若要处理的是sub-ioctl, 则需要的空间为参数的大小加上4byte,若不为sub-ioctl, 则需要的空间大小为参数的大小
    • 若参数大小固定(IW_PRIV_SIZE_FIXED), 且总和小于16byte, 则说明输入参数可以直接通过iwreq_data来传递, 无需分配内存来拷贝参数, 因此返回0
    • 否则返回需要分配的内存的大小
  2. 对于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的总结

  1. 若参数大小固定且小于16byte(若为sub-ioctl则还需要计算ioctl id的4byte)的数据,使用iwreq.u.name来传递, 否则使用iwreq.u.data来传递
  2. ioctl id为偶数的为SET型ioctl, 为奇数的为GET型的ioctl,SET型的ioctl需要root权限
  3. private ioctl最多32个, 若不能满足使用,则需要使用sub ioctl来扩充
  4. 最好不要在一个ioctl里面同时使用set args和get args, 若需要这样做, 则应该使用相同的方式来传递set args和get args, 即iwreq.u.name 或者 iwreq.u.data
  5. 若通过iwreq.u.data来传递参数指针时, iwpriv中提供的buffer大小为4096byte, 注意这一限制
  6. 若通过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, 因此,最好声明合适的大小, 以同时满足两者的大小
  7. 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中