All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] cfg80211 merge
@ 2007-01-31  1:37 John W. Linville
  2007-01-31  1:38 ` [RFC PATCH 1/3] cfg80211 and nl80211 John W. Linville
                   ` (2 more replies)
  0 siblings, 3 replies; 52+ messages in thread
From: John W. Linville @ 2007-01-31  1:37 UTC (permalink / raw)
  To: wireless

In London we agreed that it would be good to go ahead and merge
cfg80211.  This provides some exposure to the API and eases some of
the pain for driver maintainers (who won't have to follow wireless-dev
to get it).

I have refactored the cfg80211-related patches down to three patches.
This eliminates some of the churn in the series from wireless-dev,
and each patch leaves a buildable tree.

Let me know if I've left anything out, screwed anything up, or if
you no longer think a cfg80211 merge is appropriate at this time.

Thanks,

John
-- 
John W. Linville
linville@tuxdriver.com
_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC PATCH 1/3] cfg80211 and nl80211
  2007-01-31  1:37 [RFC] cfg80211 merge John W. Linville
@ 2007-01-31  1:38 ` John W. Linville
  2007-01-31  1:39   ` [RFC PATCH 2/3] wireless: move wext to net/wireless/ John W. Linville
  2007-01-31  2:46   ` [RFC PATCH 1/3] cfg80211 and nl80211 Michael Wu
  2007-01-31  2:48 ` [RFC] cfg80211 merge Jouni Malinen
  2007-02-07  0:46 ` [RFC v2] " John W. Linville
  2 siblings, 2 replies; 52+ messages in thread
From: John W. Linville @ 2007-01-31  1:38 UTC (permalink / raw)
  To: wireless

From: Johannes Berg <johannes@sipsolutions.net>

This patch adds cfg80211, a new configuration system for wireless hardware
as well as nl80211, the netlink-based userspace interface for it.

It currently features a bunch of configuration requests, support for
adding and removing virtual interfaces, the ability to inject packets and
more.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
 include/linux/Kbuild       |    1 +
 include/linux/netdevice.h  |    1 +
 include/linux/nl80211.h    |  276 ++++++++++++
 include/net/cfg80211.h     |  193 ++++++++
 net/Kconfig                |    3 +
 net/Makefile               |    1 +
 net/wireless/Makefile      |    4 +
 net/wireless/core.c        |  235 ++++++++++
 net/wireless/core.h        |   57 +++
 net/wireless/nl80211.c     | 1049 ++++++++++++++++++++++++++++++++++++++++++++
 net/wireless/nl80211.h     |    7 +
 net/wireless/wext-compat.c |   25 +
 12 files changed, 1852 insertions(+), 0 deletions(-)

diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 157db77..a0657c6 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -115,6 +115,7 @@ header-y += netrom.h
 header-y += nfs2.h
 header-y += nfs4_mount.h
 header-y += nfs_mount.h
+header-y += nl80211.h
 header-y += oom.h
 header-y += param.h
 header-y += pci_regs.h
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index fea0d9d..c1e9962 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -398,6 +398,7 @@ struct net_device
 	void                    *ip6_ptr;       /* IPv6 specific data */
 	void			*ec_ptr;	/* Econet specific data	*/
 	void			*ax25_ptr;	/* AX.25 specific data */
+	void			*ieee80211_ptr;	/* IEEE 802.11 specific data */
 
 /*
  * Cache line mostly used on receive path (including eth_type_trans())
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
new file mode 100644
index 0000000..7bb84bb
--- /dev/null
+++ b/include/linux/nl80211.h
@@ -0,0 +1,276 @@
+#ifndef __LINUX_NL80211_H
+#define __LINUX_NL80211_H
+/*
+ * 802.11 netlink interface public header
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+/* currently supported commands
+ * don't change the order or add anything inbetween, this is ABI! */
+enum {
+	/* There's no technical reason to not use command 0 but malformed
+	 * zeroed messages may have it and this catches that */
+	NL80211_CMD_UNSPEC,
+
+	/* Get supported commands by ifindex,
+	 * uses NL80211_ATTR_CMDS (output) and NL80211_ATTR_IFINDEX (input) */
+	NL80211_CMD_GET_CMDLIST,
+
+	/* Supported commands returned */
+	NL80211_CMD_NEW_CMDLIST,
+
+	/* Inject a frame using NL80211_ATTR_FLAGS and NL80211_ATTR_FRAME.
+	 * If kernel sends this, it's a status notification for the injected
+	 * frame. */
+	NL80211_CMD_INJECT,
+
+	/* add a virtual interface to a group that is identified by any
+	 * other ifindex in the group of a wiphy index, needs the
+	 * NL80211_IF_NAME attribute */
+	NL80211_CMD_ADD_VIRTUAL_INTERFACE,
+
+	/* remove a given (with NL80211_ATTR_IFINDEX) virtual device */
+	NL80211_CMD_DEL_VIRTUAL_INTERFACE,
+
+	/* get list of all wiphys */
+	NL80211_CMD_GET_WIPHYS,
+
+	/* get list of all wiphys */
+	NL80211_CMD_NEW_WIPHYS,
+
+	/* get list of all interfaces belonging to a wiphy */
+	NL80211_CMD_GET_INTERFACES,
+
+	/* get list of all interfaces belonging to a wiphy */
+	NL80211_CMD_NEW_INTERFACES,
+
+	/* configure device */
+	NL80211_CMD_CONFIGURE,
+
+	/* request configuration */
+	NL80211_CMD_GET_CONFIG,
+
+	/* configuration sent from kernel */
+	NL80211_CMD_NEW_CONFIG,
+
+	/* initiate scan.
+	 * Takes a CHANNEL_LIST attribute containing nested
+	 * attributes which in turn contain CHANNEL and FLAGS
+	 * attributes.
+	 * The top level can also contain a FLAGS attribute
+	 * which is then the default for each channel.
+	 * If no channel list is given (or it is empty)
+	 * all channels shall be scanned. */
+	NL80211_CMD_INITIATE_SCAN,
+
+	/* scan result (kernel -> userspace) */
+	NL80211_CMD_SCAN_RESULT,
+
+	/* change roaming control */
+	NL80211_CMD_SET_ROAMING_CONTROL,
+
+	/* get roaming control setting */
+	NL80211_CMD_GET_ROAMING_CONTROL,
+
+	/* answer to that */
+	NL80211_CMD_ROAMING_CONTROL,
+
+	/* set access point BSSID for userspace roaming */
+	NL80211_CMD_SET_FIXED_BSSID,
+
+	/* get currently set userspace roaming BSSID */
+	NL80211_CMD_GET_FIXED_BSSID,
+
+	/* currently set roaming BSSID */
+	NL80211_CMD_FIXED_BSSID,
+
+	/* get current association information, if not associated then
+	 * the BSSID attribute is not present in response */
+	NL80211_CMD_GET_ASSOCIATION,
+
+	/* association notification and response to GET_BSSID */
+	NL80211_CMD_ASSOCIATION_CHANGED,
+
+	/* disassociate from current AP */
+	NL80211_CMD_DISASSOCIATE,
+
+	/* deauth from current AP */
+	NL80211_CMD_DEAUTH,
+
+	/* re-associate with current settings
+	 * (SSID and BSSID if roaming control in userspace) */
+	NL80211_CMD_REASSOCIATE,
+
+	/* request the full list of BSSs the device is
+	 * authenticated with */
+	NL80211_CMD_GET_AUTH_LIST,
+
+	/* sent as a response to GET_AUTH_LIST containing
+	 * an ATTR_BSSID_LIST */
+	NL80211_CMD_AUTH_LIST,
+
+	/* sent when authenticating/deauthenticating.
+	 * contains an ATTR_BSSID and possibly an
+	 * ATTR_DEAUTHENTICATED */
+	NL80211_CMD_AUTHENTICATION_CHANGED,
+
+	/* add commands here */
+
+	/* used to define NL80211_CMD_MAX below */
+	__NL80211_CMD_AFTER_LAST,
+};
+#define NL80211_CMD_MAX (__NL80211_CMD_AFTER_LAST - 1)
+
+
+/* currently supported attributes.
+ * don't change the order or add anything inbetween, this is ABI! */
+enum {
+	NL80211_ATTR_UNSPEC,
+
+	/* network device (ifindex) to operate on */
+	NL80211_ATTR_IFINDEX,
+
+	/* wiphy index to operate on */
+	NL80211_ATTR_WIPHY,
+
+	/* list of u8 cmds that a given device implements */
+	NL80211_ATTR_CMDS,
+
+	/* flags for injection and other commands, see below */
+	NL80211_ATTR_FLAGS,
+
+	/* which hardware queue to use */
+	NL80211_ATTR_QUEUE,
+
+	/* frame to inject or received frame for mgmt frame subscribers */
+	NL80211_ATTR_FRAME,
+
+	/* interface name */
+	NL80211_ATTR_IFNAME,
+
+	/* type of (virtual) interface */
+	NL80211_ATTR_IFTYPE,
+
+	/* interface list */
+	NL80211_ATTR_INTERFACE_LIST,
+
+	/* wiphy list */
+	NL80211_ATTR_WIPHY_LIST,
+
+	/* attributes used for configuration */
+	/* network ID (pre 802.11 HW) */
+	NL80211_ATTR_NETWORK_ID,
+
+	/* channel, 1-14 are B/G */
+	NL80211_ATTR_CHANNEL,
+
+	/* channel list for scan determination */
+	NL80211_ATTR_CHANNEL_LIST,
+
+	/* receiver sensitivity in dBm */
+	NL80211_ATTR_RX_SENSITIVITY,
+
+	/* BSSID to associate to, only used when roaming control
+	 * is in userspace */
+	NL80211_ATTR_BSSID,
+
+	/* list of multiple BSSIDs, this is a nested attribute
+	 * containing an index->(attrs) mapping */
+	NL80211_ATTR_BSSID_LIST,
+
+	/* this is a flag for when an authentication is lost */
+	NL80211_ATTR_DEAUTHENTICATED,
+
+	/* SSID of ESS to associate to */
+	NL80211_ATTR_SSID,
+
+	/* transmit power in mW */
+	NL80211_ATTR_TRANSMIT_POWER,
+
+	/* fragmentation threshold in bytes */
+	NL80211_ATTR_FRAG_THRESHOLD,
+
+	/* one or more information elements */
+	NL80211_ATTR_INFORMATION_ELEMENT,
+
+	NL80211_ATTR_ROAMING_CONTROL,
+
+	NL80211_ATTR_SCAN_TYPE,
+
+	/* add attributes here */
+
+	/* used to define NL80211_ATTR_MAX below */
+	__NL80211_ATTR_AFTER_LAST,
+};
+#define NL80211_ATTR_MAX (__NL80211_ATTR_AFTER_LAST - 1)
+
+/**
+ * NL80211_FLAG_TXSTATUS - send transmit status indication
+ */
+#define NL80211_FLAG_TXSTATUS		(1<<0)
+/**
+ * NL80211_FLAG_ENCRYPT - encrypt this packet
+ * Warning: This looks inside the packet header!
+ */
+#define NL80211_FLAG_ENCRYPT		(1<<1)
+
+/**
+ * NL80211_FLAG_SCAN_TYPE_ACTIVE - set this with a scan
+ * request to have it scan actively, can also be used
+ * within the nested CHANNEL_LIST...
+ */
+#define NL80211_FLAG_SCAN_TYPE_ACTIVE	(1<<2)
+
+/**
+ * maximum length of a frame that can be injected
+ */
+#define NL80211_MAX_FRAME_LEN 2500
+
+/* this is an arbitrary limit, 516 means two full-length
+ * IEs would fit... */
+/**
+ * maximum length of IE(s) passed in an NL80211_ATTR_INFORMATION_ELEMENT.
+ */
+#define NL80211_MAX_IE_LEN 516
+
+/* may need to be bumped? */
+/**
+ * maximum number of items in an ATTR_CHANNEL_LIST
+ */
+#define NL80211_MAX_CHANNEL_LIST_ITEM 20
+
+/**
+ * &enum nl80211_iftype - (virtual) interface types
+ *
+ * This structure is used with the NL80211_ATTR_IFTYPE
+ * to set the type of an interface.
+ * Note that these are intentionally compatible with
+ * the IW_MODE_* constants except for the removal of
+ * IW_MODE_AUTO.
+ *
+ */
+enum {
+	NL80211_IFTYPE_UNSPECIFIED,
+	NL80211_IFTYPE_ADHOC,
+	NL80211_IFTYPE_STATION,
+	NL80211_IFTYPE_AP,
+	NL80211_IFTYPE_WDS,
+	NL80211_IFTYPE_SECONDARY,
+	NL80211_IFTYPE_MONITOR,
+
+	/* keep last */
+	__NL80211_IFTYPE_AFTER_LAST
+};
+#define NL80211_IFTYPE_MAX (__NL80211_IFTYPE_AFTER_LAST - 1)
+
+enum {
+	NL80211_ROAMING_CONTROL_KERNEL,
+	NL80211_ROAMING_CONTROL_USERSPACE,
+
+	/* keep last */
+	__NL80211_ROAMING_CONTROL_AFTER_LAST
+};
+#define NL80211_ROAMING_CONTROL_MAX (__NL80211_ROAMING_CONTROL_AFTER_LAST-1)
+
+#endif /* __LINUX_NL80211_H */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
new file mode 100644
index 0000000..d83c47f
--- /dev/null
+++ b/include/net/cfg80211.h
@@ -0,0 +1,193 @@
+#ifndef __NET_CFG80211_H
+#define __NET_CFG80211_H
+
+#include <linux/netlink.h>
+#include <linux/nl80211.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <net/genetlink.h>
+
+/*
+ * 802.11 configuration in-kernel interface
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+/**
+ * struct cfg80211_config - description of a configuration (request)
+ */
+struct cfg80211_config {
+	/* first fields with 'internal' validity */
+
+	/* SSID to use, valid if not NULL, ssid_len then
+	 * contains the length. change forces reassociation */
+	s8 ssid_len;
+	u8 *ssid;
+
+	/* now fields with explicit validity */
+#define CFG80211_CFG_VALID_NWID			(1<<0)
+#define CFG80211_CFG_VALID_RX_SENSITIVITY	(1<<1)
+#define CFG80211_CFG_VALID_TRANSMIT_POWER	(1<<2)
+#define CFG80211_CFG_VALID_FRAG_THRESHOLD	(1<<3)
+#define CFG80211_CFG_VALID_CHANNEL		(1<<4)
+	unsigned int valid;
+
+	u16 network_id;
+	s32 rx_sensitivity;
+	u32 transmit_power;
+	u32 fragmentation_threshold;
+	u32 channel;
+};
+
+struct scan_channel {
+	u32 channel;
+	int active;
+};
+
+struct scan_params {
+	/* number of items in 'channels' array
+	 * or -1 to indicate scanning all channels
+	 * (in that case 'channels' is NULL) */
+	int n_channels;
+
+	/* use only when n_channels is -1 to determine
+	 * whether scanning should be active or not */
+	int active;
+
+	/* the channel list if any */
+	struct scan_channel *channels;
+};
+
+/**
+ * struct cfg80211_ops - backend description for wireless configuration
+ *
+ * This struct is registered by fullmac card drivers and/or wireless stacks
+ * in order to handle configuration requests on their interfaces.
+ *
+ * The priv pointer passed to each call is the pointer that was
+ * registered in cfg80211_register_driver().
+ *
+ * All callbacks except where otherwise noted should return 0
+ * on success or a negative error code.
+ *
+ * @list_interfaces: for each interfaces belonging to the wiphy identified
+ *		     by the priv pointer, call the one() function with the
+ *		     given data and the ifindex. This callback is required.
+ *
+ * @inject_packet: inject the given frame with the NL80211_FLAG_*
+ *		   flags onto the given queue.
+ *
+ * @add_virtual_intf: create a new virtual interface with the given name
+ *
+ * @del_virtual_intf: remove the virtual interface determined by ifindex.
+ *
+ * @configure: configure the given interface as requested in the config struct.
+ *	       must not ignore any configuration item, if something is
+ *	       is requested that cannot be fulfilled return an error
+ *
+ * @get_config: fill the given config structure with the current configuration
+ *
+ * @reassociate: reassociate with current settings (SSID, BSSID if
+ *		 userspace roaming is enabled)
+ *
+ * @disassociate: disassociate from current AP
+ *
+ * @deauth: deauth from current AP
+ *
+ * @initiate_scan: ...
+ *
+ * @set_roaming: set who gets to control roaming, the roaming_control
+ *		 parameter is passed NL80211_ROAMING_CONTROL_* values.
+ *
+ * @get_roaming: return where roaming control currently is done or
+ *		 a negative error.
+ *
+ * @set_fixed_bssid: set BSSID to use with userspace roaming, forces
+ *		     reassociation if changing.
+ * @get_fixed_bssid: get BSSID that is used with userspace roaming,
+ *		     the bssid parameter has space for 6 bytes
+ *
+ * @get_association: get BSSID of the BSS that the device is currently
+ *		     associated to and return 1, or return 0 if not
+ *		     associated (or a negative error code)
+ * @get_auth_list: get list of BSSIDs of all BSSs the device has
+ *		   authenticated with, must call next_bssid for each,
+ *		   next_bssid returns non-zero on error, the given data
+ *		   is to be passed to that callback
+ */
+struct cfg80211_ops {
+	int	(*list_interfaces)(void *priv, void *data,
+				   int (*one)(void *data, int ifindex));
+
+
+	int	(*inject_packet)(void *priv, void *frame, int framelen,
+				 u32 flags, int queue);
+
+
+	int	(*add_virtual_intf)(void *priv, char *name,
+				    unsigned int type);
+	int	(*del_virtual_intf)(void *priv, int ifindex);
+
+
+	int	(*configure)(void *priv, struct net_device *dev,
+			     struct cfg80211_config *cfg);
+	void	(*get_config)(void *priv, struct net_device *dev,
+			      struct cfg80211_config *cfg);
+
+
+	int	(*reassociate)(void *priv, struct net_device *dev);
+	int	(*disassociate)(void *priv, struct net_device *dev);
+	int	(*deauth)(void *priv, struct net_device *dev);
+
+
+	int	(*initiate_scan)(void *priv, struct net_device *dev,
+				 struct scan_params *params);
+
+
+	int	(*set_roaming)(void *priv, struct net_device *dev,
+			       int roaming_control);
+	int	(*get_roaming)(void *priv, struct net_device *dev);
+	int	(*set_fixed_bssid)(void *priv, struct net_device *dev,
+				   u8 *bssid);
+	int	(*get_fixed_bssid)(void *priv, struct net_device *dev,
+				   u8 *bssid);
+
+
+	int	(*get_association)(void *priv, struct net_device *dev,
+				   u8 *bssid);
+
+	int	(*get_auth_list)(void *priv, struct net_device *dev,
+				 void *data,
+				 int (*next_bssid)(void *data, u8 *bssid));
+};
+
+/**
+ * cfg80211_register - register a wiphy with cfg80211
+ *
+ * register a given method structure with the cfg80211 system
+ * and associate the 'priv' pointer with it.
+ *
+ * Returns a non-negative wiphy index or a negative error code.
+ *
+ * NOTE: for proper operation, this priv pointer MUST also be
+ * assigned to each &struct net_device's @ieee80211_ptr member!
+ */
+extern int cfg80211_register(struct cfg80211_ops *ops, void *priv);
+
+/**
+ * cfg80211_unregister - deregister a wiphy from cfg80211
+ *
+ * unregister a device with the given priv pointer.
+ * After this call, no more requests can be made with this priv
+ * pointer, but the call may sleep to wait for an outstanding
+ * request that is being handled.
+ */
+extern void cfg80211_unregister(void *priv);
+
+/* helper functions specific to nl80211 */
+extern void *nl80211hdr_put(struct sk_buff *skb, u32 pid,
+			    u32 seq, int flags, u8 cmd);
+extern void *nl80211msg_new(struct sk_buff **skb, u32 pid,
+			    u32 seq, int flags, u8 cmd);
+
+#endif /* __NET_CFG80211_H */
diff --git a/net/Kconfig b/net/Kconfig
index 7dfc949..8d121a5 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -226,6 +226,9 @@ config WIRELESS_EXT
 config FIB_RULES
 	bool
 
+config CFG80211
+	tristate "Improved wireless configuration API"
+
 endif   # if NET
 endmenu # Networking
 
diff --git a/net/Makefile b/net/Makefile
index ad4d14f..278d6bf 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_ECONET)		+= econet/
 obj-$(CONFIG_VLAN_8021Q)	+= 8021q/
 obj-$(CONFIG_IP_DCCP)		+= dccp/
 obj-$(CONFIG_IP_SCTP)		+= sctp/
+obj-$(CONFIG_CFG80211)		+= wireless/
 obj-$(CONFIG_IEEE80211)		+= ieee80211/
 obj-$(CONFIG_TIPC)		+= tipc/
 obj-$(CONFIG_NETLABEL)		+= netlabel/
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
new file mode 100644
index 0000000..c030b12
--- /dev/null
+++ b/net/wireless/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_CFG80211) += cfg80211.o
+
+cfg80211-objs := \
+	core.o nl80211.o
diff --git a/net/wireless/core.c b/net/wireless/core.c
new file mode 100644
index 0000000..4a10ec2
--- /dev/null
+++ b/net/wireless/core.c
@@ -0,0 +1,235 @@
+/*
+ * This is the new wireless configuration interface.
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include "core.h"
+#include "nl80211.h"
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <net/genetlink.h>
+#include <net/cfg80211.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+
+MODULE_AUTHOR("Johannes Berg");
+MODULE_LICENSE("GPL");
+
+/* RCU might be appropriate here since we usually
+ * only read the list, and that can happen quite
+ * often because we need to do it for each command */
+LIST_HEAD(cfg80211_drv_list);
+DEFINE_MUTEX(cfg80211_drv_mutex);
+static int wiphy_counter;
+
+/* requires nl80211_drv_mutex to be held! */
+static struct cfg80211_registered_driver *cfg80211_drv_by_priv(void *priv)
+{
+	struct cfg80211_registered_driver *result = NULL, *drv;
+
+	if (!priv)
+		return NULL;
+
+	list_for_each_entry(drv, &cfg80211_drv_list, list) {
+		if (drv->priv == priv) {
+			result = drv;
+			break;
+		}
+	}
+
+	return result;
+}
+
+/* requires cfg80211_drv_mutex to be held! */
+static struct cfg80211_registered_driver *cfg80211_drv_by_wiphy(int wiphy)
+{
+	struct cfg80211_registered_driver *result = NULL, *drv;
+
+	list_for_each_entry(drv, &cfg80211_drv_list, list) {
+		if (drv->wiphy == wiphy) {
+			result = drv;
+			break;
+		}
+	}
+
+	return result;
+}
+
+/* requires cfg80211_drv_mutex to be held! */
+static struct cfg80211_registered_driver *
+__cfg80211_drv_from_info(struct genl_info *info)
+{
+	int ifindex;
+	struct cfg80211_registered_driver *bywiphy = NULL, *byifidx = NULL;
+	struct net_device *dev;
+	int err = -EINVAL;
+
+	if (info->attrs[NL80211_ATTR_WIPHY]) {
+		bywiphy = cfg80211_drv_by_wiphy(
+				nla_get_u32(info->attrs[NL80211_ATTR_WIPHY]));
+		err = -ENODEV;
+	}
+
+	if (info->attrs[NL80211_ATTR_IFINDEX]) {
+		ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
+		dev = dev_get_by_index(ifindex);
+		if (dev) {
+			byifidx = cfg80211_drv_by_priv(dev->ieee80211_ptr);
+			dev_put(dev);
+		}
+		err = -ENODEV;
+	}
+
+	if (bywiphy && byifidx) {
+		if (bywiphy != byifidx)
+			return ERR_PTR(-EINVAL);
+		else
+			return bywiphy; /* == byifidx */
+	}
+	if (bywiphy)
+		return bywiphy;
+
+	if (byifidx)
+		return byifidx;
+
+	return ERR_PTR(err);
+}
+
+struct cfg80211_registered_driver *
+cfg80211_get_drv_from_info(struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+
+	mutex_lock(&cfg80211_drv_mutex);
+	drv = __cfg80211_drv_from_info(info);
+
+	/* if it is not an error we grab the lock on
+	 * it to assure it won't be going away while
+	 * we operate on it */
+	if (!IS_ERR(drv))
+		mutex_lock(&drv->mtx);
+
+	mutex_unlock(&cfg80211_drv_mutex);
+
+	return drv;
+}
+
+/* wext will need this */
+struct cfg80211_registered_driver *
+cfg80211_get_drv_from_ifindex(int ifindex)
+{
+	struct cfg80211_registered_driver *drv = ERR_PTR(-ENODEV);
+	struct net_device *dev;
+
+	mutex_lock(&cfg80211_drv_mutex);
+	dev = dev_get_by_index(ifindex);
+	if (!dev)
+		goto out;
+	drv = cfg80211_drv_by_priv(dev->ieee80211_ptr);
+	if (drv)
+		mutex_lock(&drv->mtx);
+	else
+		drv = ERR_PTR(-ENODEV);
+	dev_put(dev);
+ out:
+	mutex_unlock(&cfg80211_drv_mutex);
+	return drv;
+}
+
+void cfg80211_put_drv(struct cfg80211_registered_driver *drv)
+{
+	BUG_ON(IS_ERR(drv));
+	mutex_unlock(&drv->mtx);
+}
+
+/* exported functions */
+
+int cfg80211_register(struct cfg80211_ops *ops, void *priv)
+{
+	struct cfg80211_registered_driver *drv;
+	int res;
+
+	if (!priv || !ops->list_interfaces)
+		return -EINVAL;
+
+	mutex_lock(&cfg80211_drv_mutex);
+
+	if (cfg80211_drv_by_priv(priv)) {
+		res = -EALREADY;
+		goto out_unlock;
+	}
+
+	drv = kzalloc(sizeof(struct cfg80211_registered_driver), GFP_KERNEL);
+	if (!drv) {
+		res = -ENOMEM;
+		goto out_unlock;
+	}
+
+	drv->ops = ops;
+	drv->priv = priv;
+
+	if (unlikely(wiphy_counter<0)) {
+		/* ugh, wrapped! */
+		kfree(drv);
+		res = -ENOSPC;
+		goto out_unlock;
+	}
+	mutex_init(&drv->mtx);
+	drv->wiphy = wiphy_counter;
+	list_add(&drv->list, &cfg80211_drv_list);
+	/* return wiphy number */
+	res = drv->wiphy;
+
+	/* now increase counter for the next time */
+	wiphy_counter++;
+
+ out_unlock:
+	mutex_unlock(&cfg80211_drv_mutex);
+	return res;
+}
+EXPORT_SYMBOL_GPL(cfg80211_register);
+
+void cfg80211_unregister(void *priv)
+{
+	struct cfg80211_registered_driver *drv;
+
+	mutex_lock(&cfg80211_drv_mutex);
+	drv = cfg80211_drv_by_priv(priv);
+	if (!drv) {
+		printk(KERN_ERR "deregistering cfg80211 backend that "
+		       " was never registered!\n");
+		mutex_unlock(&cfg80211_drv_mutex);
+		return;
+	}
+
+	/* hold registered driver mutex during list removal as well
+	 * to make sure no commands are in progress at the moment */
+	mutex_lock(&drv->mtx);
+	list_del(&drv->list);
+	mutex_unlock(&drv->mtx);
+
+	mutex_unlock(&cfg80211_drv_mutex);
+
+	mutex_destroy(&drv->mtx);
+	kfree(drv);
+}
+EXPORT_SYMBOL_GPL(cfg80211_unregister);
+
+/* module initialisation/exit functions */
+
+static int cfg80211_init(void)
+{
+	/* possibly need to do more later */
+	return nl80211_init();
+}
+
+static void cfg80211_exit(void)
+{
+	/* possibly need to do more later */
+	nl80211_exit();
+}
+
+module_init(cfg80211_init);
+module_exit(cfg80211_exit);
diff --git a/net/wireless/core.h b/net/wireless/core.h
new file mode 100644
index 0000000..562c476
--- /dev/null
+++ b/net/wireless/core.h
@@ -0,0 +1,57 @@
+/*
+ * Wireless configuration interface internals.
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+#ifndef __NET_WIRELESS_CORE_H
+#define __NET_WIRELESS_CORE_H
+#include <net/cfg80211.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <net/genetlink.h>
+
+struct cfg80211_registered_driver {
+	struct cfg80211_ops *ops;
+	int wiphy;
+	void *priv;
+	struct list_head list;
+	/* we hold this mutex during any call so that
+	 * we cannot do multiple calls at once, and also
+	 * to avoid the deregister call to proceed while
+	 * any call is in progress */
+	struct mutex mtx;
+};
+
+extern struct mutex cfg80211_drv_mutex;
+extern struct list_head cfg80211_drv_list;
+
+/*
+ * This function returns a pointer to the driver
+ * that the genl_info item that is passed refers to.
+ * If successful, it returns non-NULL and also locks
+ * the driver's mutex!
+ *
+ * This means that you need to call cfg80211_put_drv()
+ * before being allowed to acquire &cfg80211_drv_mutex!
+ *
+ * This is necessary because we need to lock the global
+ * mutex to get an item off the list safely, and then
+ * we lock the drv mutex so it doesn't go away under us.
+ *
+ * We don't want to keep cfg80211_drv_mutex locked
+ * for all the time in order to allow requests on
+ * other interfaces to go through at the same time.
+ *
+ * The result of this can be a PTR_ERR and hence must
+ * be checked with IS_ERR() for errors.
+ */
+extern struct cfg80211_registered_driver *
+cfg80211_get_drv_from_info(struct genl_info *info);
+
+/* identical to cfg80211_get_drv_from_info but only operate on ifindex */
+extern struct cfg80211_registered_driver *
+cfg80211_get_drv_from_ifindex(int ifindex);
+
+extern void cfg80211_put_drv(struct cfg80211_registered_driver *drv);
+
+#endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
new file mode 100644
index 0000000..7a1a888
--- /dev/null
+++ b/net/wireless/nl80211.c
@@ -0,0 +1,1049 @@
+/*
+ * This is the new netlink-based wireless configuration interface.
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <net/genetlink.h>
+#include <net/cfg80211.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/if_ether.h>
+#include "core.h"
+#include "nl80211.h"
+
+/* the netlink family */
+static struct genl_family nl80211_fam = {
+	.id = GENL_ID_GENERATE,	/* don't bother with a hardcoded ID */
+	.name = "nl80211",	/* have users key off the name instead */
+	.hdrsize = 0,		/* no private header */
+	.version = 1,		/* no particular meaning now */
+	.maxattr = NL80211_ATTR_MAX,
+};
+
+/* internal helper: validate an information element attribute */
+static int check_information_element(struct nlattr *nla)
+{
+	int len = nla_len(nla);
+	u8 *data = nla_data(nla);
+	int elementlen;
+
+	while (len >= 2) {
+		/* 1 byte ID, 1 byte len, `len' bytes data */
+		elementlen = *(data+1) + 2;
+		data += elementlen;
+		len -= elementlen;
+	}
+	return len ? -EINVAL : 0;
+}
+
+/* internal helper: get drv and dev */
+static int get_drv_dev_by_info_ifindex(struct genl_info *info,
+				       struct cfg80211_registered_driver **drv,
+				       struct net_device **dev)
+{
+	int ifindex;
+
+	if (!info->attrs[NL80211_ATTR_IFINDEX])
+		return -EINVAL;
+
+	ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
+	*dev = dev_get_by_index(ifindex);
+	if (!dev)
+		return -ENODEV;
+
+	*drv = cfg80211_get_drv_from_ifindex(ifindex);
+	if (IS_ERR(*drv)) {
+		dev_put(*dev);
+		return PTR_ERR(*drv);
+	}
+
+	return 0;
+}
+
+/* policy for the attributes */
+static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
+	[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
+	[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
+	[NL80211_ATTR_FLAGS] = { .type = NLA_U32 },
+	[NL80211_ATTR_QUEUE] = { .type = NLA_U32 },
+	[NL80211_ATTR_FRAME] = { .type = NLA_STRING,
+				 .len = NL80211_MAX_FRAME_LEN },
+	[NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
+	[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
+	[NL80211_ATTR_NETWORK_ID] = { .type = NLA_U16 },
+	[NL80211_ATTR_CHANNEL] = { .type = NLA_U32 },
+	[NL80211_ATTR_RX_SENSITIVITY] = { .type = NLA_U32 },
+	[NL80211_ATTR_BSSID] = { .len = ETH_ALEN },
+	[NL80211_ATTR_SSID] = { .type = NLA_STRING, .len = 32 },
+	[NL80211_ATTR_TRANSMIT_POWER] = { .type = NLA_U32 },
+	[NL80211_ATTR_FRAG_THRESHOLD] = { .type = NLA_U32 },
+	[NL80211_ATTR_INFORMATION_ELEMENT] = { .type = NLA_STRING,
+					       .len = NL80211_MAX_IE_LEN },
+	[NL80211_ATTR_ROAMING_CONTROL] = { .type = NLA_U32 },
+	[NL80211_ATTR_SCAN_TYPE] = { .type = NLA_U32 },
+};
+
+/* netlink command implementations */
+
+#define CHECK_CMD(ptr, cmd)				\
+	if (drv->ops->ptr)				\
+		NLA_PUT_FLAG(msg, NL80211_CMD_##cmd);
+
+static int nl80211_get_cmdlist(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	struct sk_buff *msg;
+	void *hdr;
+	int err;
+	struct nlattr *start;
+
+	drv = cfg80211_get_drv_from_info(info);
+	if (IS_ERR(drv))
+		return PTR_ERR(drv);
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_NEW_CMDLIST);
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
+
+	start = nla_nest_start(msg, NL80211_ATTR_CMDS);
+	if (!start)
+		goto nla_put_failure;
+
+	/* unconditionally allow some common commands we handle centrally
+	 * or where we require the implementation */
+	NLA_PUT_FLAG(msg, NL80211_CMD_GET_CMDLIST);
+	NLA_PUT_FLAG(msg, NL80211_CMD_GET_WIPHYS);
+	NLA_PUT_FLAG(msg, NL80211_CMD_GET_INTERFACES);
+
+	CHECK_CMD(inject_packet, INJECT);
+	CHECK_CMD(add_virtual_intf, ADD_VIRTUAL_INTERFACE);
+	CHECK_CMD(del_virtual_intf, DEL_VIRTUAL_INTERFACE);
+	CHECK_CMD(configure, CONFIGURE);
+	CHECK_CMD(get_config, GET_CONFIG);
+	CHECK_CMD(reassociate, REASSOCIATE);
+	CHECK_CMD(disassociate, DISASSOCIATE);
+	CHECK_CMD(deauth, DEAUTH);
+	CHECK_CMD(initiate_scan, INITIATE_SCAN);
+	CHECK_CMD(set_roaming, SET_ROAMING_CONTROL);
+	CHECK_CMD(get_roaming, GET_ROAMING_CONTROL);
+	CHECK_CMD(set_fixed_bssid, SET_FIXED_BSSID);
+	CHECK_CMD(get_fixed_bssid, GET_FIXED_BSSID);
+	CHECK_CMD(get_association, GET_ASSOCIATION);
+	CHECK_CMD(get_auth_list, GET_AUTH_LIST);
+
+	nla_nest_end(msg, start);
+
+	genlmsg_end(msg, hdr);
+
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto put_drv;
+
+ nla_put_failure:
+ 	err = -ENOBUFS;
+	nlmsg_free(msg);
+ put_drv:
+	cfg80211_put_drv(drv);
+	return err;
+}
+#undef CHECK_CMD
+
+static int nl80211_get_wiphys(struct sk_buff *skb, struct genl_info *info)
+{
+	struct sk_buff *msg;
+	void *hdr;
+	struct nlattr *start, *indexstart;
+	struct cfg80211_registered_driver *drv;
+	int idx = 1;
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_NEW_WIPHYS);
+	if (IS_ERR(hdr))
+		return PTR_ERR(hdr);
+
+	start = nla_nest_start(msg, NL80211_ATTR_WIPHY_LIST);
+	if (!start)
+		goto nla_outer_nest_failure;
+
+	mutex_lock(&cfg80211_drv_mutex);
+	list_for_each_entry(drv, &cfg80211_drv_list, list) {
+		indexstart = nla_nest_start(msg, idx++);
+		if (!indexstart)
+			goto nla_put_failure;
+		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
+		nla_nest_end(msg, indexstart);
+	}
+	mutex_unlock(&cfg80211_drv_mutex);
+
+	nla_nest_end(msg, start);
+
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_unicast(msg, info->snd_pid);
+
+ nla_put_failure:
+	mutex_unlock(&cfg80211_drv_mutex);
+ nla_outer_nest_failure:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+struct add_cb_data {
+	int idx;
+	struct sk_buff *skb;
+};
+
+static int addifidx(void *data, int ifidx)
+{
+	struct add_cb_data *cb = data;
+	struct net_device *dev = dev_get_by_index(ifidx);
+	int err = -ENOBUFS;
+	struct nlattr *start;
+
+	/* not that this can happen, since the caller
+	 * should hold the device open... */
+	if (!dev)
+		return -ENODEV;
+
+	start = nla_nest_start(cb->skb, cb->idx++);
+	if (!start)
+		goto nla_put_failure;
+
+	NLA_PUT_U32(cb->skb, NL80211_ATTR_IFINDEX, ifidx);
+	NLA_PUT_STRING(cb->skb, NL80211_ATTR_IFNAME, dev->name);
+
+	nla_nest_end(cb->skb, start);
+	err = 0;
+
+ nla_put_failure:
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_get_intfs(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	struct sk_buff *msg;
+	void *hdr;
+	int err;
+	struct nlattr *start;
+	struct add_cb_data cb;
+
+	drv = cfg80211_get_drv_from_info(info);
+	if (IS_ERR(drv))
+		return PTR_ERR(drv);
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_NEW_INTERFACES);
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
+
+	start = nla_nest_start(msg, NL80211_ATTR_INTERFACE_LIST);
+	if (!start) {
+		err = -ENOBUFS;
+		goto msg_free;
+	}
+
+	cb.skb = msg;
+	cb.idx = 1;
+	err = drv->ops->list_interfaces(drv->priv, &cb, addifidx);
+	if (err)
+		goto msg_free;
+
+	nla_nest_end(msg, start);
+
+	genlmsg_end(msg, hdr);
+
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto put_drv;
+
+ nla_put_failure:
+	err = -ENOBUFS;
+ msg_free:
+	nlmsg_free(msg);
+ put_drv:
+	cfg80211_put_drv(drv);
+	return err;
+}
+
+static int nl80211_do_inject(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	u32 flags = 0;
+	int err, queue = -1;
+
+	if (!info->attrs[NL80211_ATTR_FRAME])
+		return -EINVAL;
+	if (info->attrs[NL80211_ATTR_FLAGS])
+		flags = nla_get_u32(info->attrs[NL80211_ATTR_FLAGS]);
+	if (info->attrs[NL80211_ATTR_QUEUE])
+		queue = (int) nla_get_u32(info->attrs[NL80211_ATTR_QUEUE]);
+
+	drv = cfg80211_get_drv_from_info(info);
+	if (IS_ERR(drv))
+		return PTR_ERR(drv);
+
+	if (!drv->ops->inject_packet) {
+		err = -ENOSYS;
+		goto unlock;
+	}
+
+	err = drv->ops->inject_packet(drv->priv,
+		nla_data(info->attrs[NL80211_ATTR_FRAME]),
+		nla_len(info->attrs[NL80211_ATTR_FRAME]),
+		flags,
+		queue);
+ unlock:
+	cfg80211_put_drv(drv);
+	return err;
+}
+
+static int nl80211_add_virt_intf(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	unsigned int type = NL80211_IFTYPE_UNSPECIFIED;
+
+	if (!info->attrs[NL80211_ATTR_IFNAME])
+		return -EINVAL;
+
+	if (info->attrs[NL80211_ATTR_IFTYPE]) {
+		type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
+		if (type > NL80211_IFTYPE_MAX)
+			return -EINVAL;
+	}
+
+	drv = cfg80211_get_drv_from_info(info);
+	if (IS_ERR(drv))
+		return PTR_ERR(drv);
+
+	if (!drv->ops->add_virtual_intf) {
+		err = -ENOSYS;
+		goto unlock;
+	}
+
+	err = drv->ops->add_virtual_intf(drv->priv,
+		nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
+
+ unlock:
+	cfg80211_put_drv(drv);
+	return err;
+}
+
+static int nl80211_del_virt_intf(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int ifindex, err;
+	struct net_device *dev;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+	ifindex = dev->ifindex;
+	dev_put(dev);
+
+	if (!drv->ops->del_virtual_intf) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	err = drv->ops->del_virtual_intf(drv->priv, ifindex);
+
+ out:
+	cfg80211_put_drv(drv);
+	return err;
+}
+
+static int nl80211_configure(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	struct cfg80211_config config;
+	struct nlattr *attr;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->configure) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	memset(&config, 0, sizeof(config));
+
+	attr = info->attrs[NL80211_ATTR_SSID];
+	if (attr) {
+		config.ssid = nla_data(attr);
+		config.ssid_len = nla_len(attr);
+	}
+
+	attr = info->attrs[NL80211_ATTR_NETWORK_ID];
+	if (attr) {
+		config.valid |= CFG80211_CFG_VALID_NWID;
+		config.network_id = nla_get_u16(attr);
+	}
+
+	attr = info->attrs[NL80211_ATTR_RX_SENSITIVITY];
+	if (attr) {
+		config.valid |= CFG80211_CFG_VALID_RX_SENSITIVITY;
+		config.rx_sensitivity = (s32) nla_get_u32(attr);
+	}
+
+	attr = info->attrs[NL80211_ATTR_TRANSMIT_POWER];
+	if (attr) {
+		config.valid |= CFG80211_CFG_VALID_TRANSMIT_POWER;
+		config.transmit_power = nla_get_u32(attr);
+	}
+
+	attr = info->attrs[NL80211_ATTR_FRAG_THRESHOLD];
+	if (attr) {
+		config.valid |= CFG80211_CFG_VALID_FRAG_THRESHOLD;
+		config.fragmentation_threshold = nla_get_u32(attr);
+	}
+
+	attr = info->attrs[NL80211_ATTR_CHANNEL];
+	if (attr) {
+		config.valid |= CFG80211_CFG_VALID_CHANNEL;
+		config.channel = nla_get_u32(attr);
+	}
+
+	err = drv->ops->configure(drv->priv, dev, &config);
+ out:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_get_config(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	struct cfg80211_config config;
+	struct sk_buff *msg;
+	void *hdr;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->get_config) {
+		err = -EOPNOTSUPP;
+		goto out_put_drv;
+	}
+
+	memset(&config, 0, sizeof(config));
+
+	drv->ops->get_config(drv->priv, dev, &config);
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_NEW_CONFIG);
+
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto out_put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+
+	if (config.ssid)
+		NLA_PUT_STRING(msg, NL80211_ATTR_SSID, config.ssid);
+
+	if (config.valid & CFG80211_CFG_VALID_NWID)
+		NLA_PUT_U16(msg, NL80211_ATTR_NETWORK_ID, config.network_id);
+
+	if (config.valid & CFG80211_CFG_VALID_RX_SENSITIVITY)
+		NLA_PUT_U32(msg, NL80211_ATTR_RX_SENSITIVITY, (u32)config.rx_sensitivity);
+
+	if (config.valid & CFG80211_CFG_VALID_TRANSMIT_POWER)
+		NLA_PUT_U32(msg, NL80211_ATTR_TRANSMIT_POWER, config.transmit_power);
+
+	if (config.valid & CFG80211_CFG_VALID_FRAG_THRESHOLD)
+		NLA_PUT_U32(msg, NL80211_ATTR_FRAG_THRESHOLD, config.fragmentation_threshold);
+
+	if (config.valid & CFG80211_CFG_VALID_CHANNEL)
+		NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL, config.channel);
+
+	genlmsg_end(msg, hdr);
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto out_put_drv;
+
+ nla_put_failure:
+	err = -ENOBUFS;
+	nlmsg_free(msg);
+ out_put_drv:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_set_roaming(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	int roaming_control;
+
+	if (!info->attrs[NL80211_ATTR_ROAMING_CONTROL])
+		return -EINVAL;
+	roaming_control = nla_get_u32(info->attrs[NL80211_ATTR_ROAMING_CONTROL]);
+
+	if (roaming_control > NL80211_ROAMING_CONTROL_MAX)
+		return -EINVAL;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->set_roaming) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	err = drv->ops->set_roaming(drv->priv, dev, roaming_control);
+ out:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_get_roaming(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	struct sk_buff *msg;
+	void *hdr;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->get_roaming) {
+		err = -EOPNOTSUPP;
+		goto out_put_drv;
+	}
+
+	err = drv->ops->get_roaming(drv->priv, dev);
+	if (err < 0)
+		goto out_put_drv;
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_ROAMING_CONTROL);
+
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto out_put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+	NLA_PUT_U32(msg, NL80211_ATTR_ROAMING_CONTROL, err);
+
+	genlmsg_end(msg, hdr);
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto out_put_drv;
+
+ nla_put_failure:
+	err = -ENOBUFS;
+	nlmsg_free(msg);
+ out_put_drv:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_set_fixed_bssid(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	u8 *bssid;
+
+	if (!info->attrs[NL80211_ATTR_BSSID])
+		return -EINVAL;
+	bssid = nla_data(info->attrs[NL80211_ATTR_BSSID]);
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->set_fixed_bssid) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	err = drv->ops->set_fixed_bssid(drv->priv, dev, bssid);
+ out:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_get_fixed_bssid(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	struct sk_buff *msg;
+	void *hdr;
+	u8 bssid[ETH_ALEN];
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->get_fixed_bssid) {
+		err = -EOPNOTSUPP;
+		goto out_put_drv;
+	}
+
+	err = drv->ops->get_fixed_bssid(drv->priv, dev, bssid);
+	if (err < 0)
+		goto out_put_drv;
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_FIXED_BSSID);
+
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto out_put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+	NLA_PUT(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
+
+	genlmsg_end(msg, hdr);
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto out_put_drv;
+
+ nla_put_failure:
+	err = -ENOBUFS;
+	nlmsg_free(msg);
+ out_put_drv:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_get_association(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	struct sk_buff *msg;
+	void *hdr;
+	u8 bssid[ETH_ALEN];
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->get_association) {
+		err = -EOPNOTSUPP;
+		goto out_put_drv;
+	}
+
+	err = drv->ops->get_association(drv->priv, dev, bssid);
+	if (err < 0)
+		goto out_put_drv;
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_ASSOCIATION_CHANGED);
+
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto out_put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+	if (err == 1)
+		NLA_PUT(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
+
+	genlmsg_end(msg, hdr);
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto out_put_drv;
+
+ nla_put_failure:
+	err = -ENOBUFS;
+	nlmsg_free(msg);
+ out_put_drv:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_assoc_deauth(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	int (*act)(void *priv, struct net_device *dev);
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	switch (info->genlhdr->cmd) {
+	case NL80211_CMD_DISASSOCIATE:
+		act = drv->ops->disassociate;
+		break;
+	case NL80211_CMD_REASSOCIATE:
+		act = drv->ops->reassociate;
+		break;
+	case NL80211_CMD_DEAUTH:
+		act = drv->ops->deauth;
+		break;
+	default:
+		act = NULL;
+	}
+
+	if (!act) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	err = act(drv->priv, dev);
+ out:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int add_bssid(void *data, u8 *bssid)
+{
+	struct add_cb_data *cb = data;
+	int err = -ENOBUFS;
+	struct nlattr *start;
+
+	start = nla_nest_start(cb->skb, cb->idx++);
+	if (!start)
+		goto nla_put_failure;
+
+	NLA_PUT(cb->skb, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
+
+	nla_nest_end(cb->skb, start);
+	err = 0;
+
+ nla_put_failure:
+	return err;
+}
+
+static int nl80211_get_auth_list(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	struct net_device *dev;
+	struct sk_buff *msg;
+	void *hdr;
+	int err;
+	struct nlattr *start;
+	struct add_cb_data cb;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->get_auth_list) {
+		err = -EOPNOTSUPP;
+		goto put_drv;
+	}
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_AUTH_LIST);
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+
+	start = nla_nest_start(msg, NL80211_ATTR_BSSID_LIST);
+	if (!start) {
+		err = -ENOBUFS;
+		goto msg_free;
+	}
+
+	cb.skb = msg;
+	cb.idx = 1;
+	err = drv->ops->get_auth_list(drv->priv, dev, &cb, add_bssid);
+	if (err)
+		goto msg_free;
+
+	nla_nest_end(msg, start);
+
+	genlmsg_end(msg, hdr);
+
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto put_drv;
+
+ nla_put_failure:
+	err = -ENOBUFS;
+ msg_free:
+	nlmsg_free(msg);
+ put_drv:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_initiate_scan(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	struct scan_params params;
+	struct scan_channel *channels = NULL;
+	int count = -1;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->initiate_scan) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	params.active = 0;
+
+	if (info->attrs[NL80211_ATTR_FLAGS])
+		params.active = !!(nla_get_u32(info->attrs[NL80211_ATTR_FLAGS])
+					& NL80211_FLAG_SCAN_TYPE_ACTIVE);
+
+	if (info->attrs[NL80211_ATTR_CHANNEL_LIST]) {
+		struct nlattr *attr = info->attrs[NL80211_ATTR_CHANNEL_LIST];
+		struct nlattr *nla;
+		int rem;
+		struct nlattr **tb;
+
+		/* let's count first */
+		count = 0;
+		nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem)
+			count++;
+
+		if (count == 0) {
+			/* assume we should actually scan all channels,
+			 * scanning no channels make no sense */
+			count = -1;
+			goto done_channels;
+		}
+
+		channels = kmalloc(count * sizeof(struct scan_channel),
+				   GFP_KERNEL);
+		tb = kmalloc((NL80211_ATTR_MAX+1) * sizeof(struct nlattr),
+			     GFP_KERNEL);
+
+		count = 0;
+		nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem) {
+			err = nla_parse(tb, NL80211_ATTR_MAX, nla_data(nla),
+					nla_len(nla), nl80211_policy);
+			if (err || !tb[NL80211_ATTR_CHANNEL]) {
+				err = -EINVAL;
+				kfree(tb);
+				kfree(channels);
+				goto out;
+			}
+			channels[count].channel =
+				nla_get_u32(tb[NL80211_ATTR_CHANNEL]);
+
+			channels[count].active = params.active;
+
+			if (tb[NL80211_ATTR_FLAGS])
+				channels[count].active =
+					!!(nla_get_u32(tb[NL80211_ATTR_FLAGS])
+					     & NL80211_FLAG_SCAN_TYPE_ACTIVE);
+			count++;
+		}
+		kfree(tb);
+	}
+
+ done_channels:
+	params.channels = channels;
+	params.n_channels = count;
+
+	err = drv->ops->initiate_scan(drv->priv, dev, &params);
+
+	kfree(channels);
+ out:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static struct genl_ops nl80211_ops[] = {
+	{
+		.cmd = NL80211_CMD_GET_CMDLIST,
+		.doit = nl80211_get_cmdlist,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_GET_WIPHYS,
+		.doit = nl80211_get_wiphys,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_GET_INTERFACES,
+		.doit = nl80211_get_intfs,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_INJECT,
+		.doit = nl80211_do_inject,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_ADD_VIRTUAL_INTERFACE,
+		.doit = nl80211_add_virt_intf,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_DEL_VIRTUAL_INTERFACE,
+		.doit = nl80211_del_virt_intf,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_CONFIGURE,
+		.doit = nl80211_configure,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_GET_CONFIG,
+		.doit = nl80211_get_config,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_SET_ROAMING_CONTROL,
+		.doit = nl80211_set_roaming,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_GET_ROAMING_CONTROL,
+		.doit = nl80211_get_roaming,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_SET_FIXED_BSSID,
+		.doit = nl80211_set_fixed_bssid,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_GET_FIXED_BSSID,
+		.doit = nl80211_get_fixed_bssid,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_GET_ASSOCIATION,
+		.doit = nl80211_get_association,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_DISASSOCIATE,
+		.doit = nl80211_assoc_deauth,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_DEAUTH,
+		.doit = nl80211_assoc_deauth,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_REASSOCIATE,
+		.doit = nl80211_assoc_deauth,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_GET_AUTH_LIST,
+		.doit = nl80211_get_auth_list,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_INITIATE_SCAN,
+		.doit = nl80211_initiate_scan,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+};
+
+
+/* exported functions */
+
+void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq, int flags, u8 cmd)
+{
+	/* since there is no private header just add the generic one */
+	return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
+}
+EXPORT_SYMBOL_GPL(nl80211hdr_put);
+
+void *nl80211msg_new(struct sk_buff **skb, u32 pid, u32 seq, int flags, u8 cmd)
+{
+	void *hdr;
+
+	*skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!*skb)
+		return ERR_PTR(-ENOBUFS);
+
+	hdr = nl80211hdr_put(*skb, pid, seq, flags, cmd);
+	if (!hdr) {
+		nlmsg_free(*skb);
+		return ERR_PTR(-ENOBUFS);
+	}
+
+	return hdr;
+}
+EXPORT_SYMBOL_GPL(nl80211msg_new);
+
+/* initialisation/exit functions */
+
+int nl80211_init(void)
+{
+	int err, i;
+
+	err = genl_register_family(&nl80211_fam);
+	if (err)
+		return err;
+
+	for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
+		err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
+		if (err)
+			goto err_out;
+	}
+	return 0;
+ err_out:
+ 	genl_unregister_family(&nl80211_fam);
+	return err;
+}
+
+void nl80211_exit(void)
+{
+	genl_unregister_family(&nl80211_fam);
+}
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
new file mode 100644
index 0000000..0edc7a4
--- /dev/null
+++ b/net/wireless/nl80211.h
@@ -0,0 +1,7 @@
+#ifndef __NET_WIRELESS_NL80211_H
+#define __NET_WIRELESS_NL80211_H
+
+extern int nl80211_init(void);
+extern void nl80211_exit(void);
+
+#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
new file mode 100644
index 0000000..1c7c361
--- /dev/null
+++ b/net/wireless/wext-compat.c
@@ -0,0 +1,25 @@
+/* NOT YET */
+
+To implement compatibility, we add a new field to struct net_device
+that contains the pending configuration structure. This is dynamically
+allocated when needed and freed when committed.
+In a way it replaces the wireless_handlers field in there which is now
+done by dynamic lookup. No worries. No one is going to have thousands
+of wireless devices, and if that changes we can still trivially change
+this assumption :)
+
+Commit is done some time after the last parameter was changed
+(with each parameter change simply (re-)schedule a timer) or
+if explicitly asked for. This is probably not what most people
+would expect, but perfectly fine in the WE API.
+
+compatibility mappings:
+
+SIOCSIWAP
+  -> if bssid is all-ones: set roaming to kernel, reassociate
+  -> if bssid is all-zeroes: set roaming to kernel
+  -> otherwise: set roaming to userspace, set bssid
+
+SIOCGIWAP
+  -> get association parameters and fill return bssid appropriately
+
-- 
1.4.4.2

-- 
John W. Linville
linville@tuxdriver.com
_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RFC PATCH 2/3] wireless: move wext to net/wireless/
  2007-01-31  1:38 ` [RFC PATCH 1/3] cfg80211 and nl80211 John W. Linville
@ 2007-01-31  1:39   ` John W. Linville
  2007-01-31  1:41     ` [RFC PATCH 3/3] cfg80211: add wext-compatible client John W. Linville
  2007-01-31 14:40     ` [RFC PATCH 2/3] wireless: move wext to net/wireless/ Christoph Hellwig
  2007-01-31  2:46   ` [RFC PATCH 1/3] cfg80211 and nl80211 Michael Wu
  1 sibling, 2 replies; 52+ messages in thread
From: John W. Linville @ 2007-01-31  1:39 UTC (permalink / raw)
  To: wireless

From: Johannes Berg <johannes@sipsolutions.net>

This patch moves net/core/wireless.c to net/wireless/wext.c. I
refrained from further cleanups though I was tempted. Hence this
is a pure file move plus various build system adjustments.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
 net/Makefile            |    2 +-
 net/core/Makefile       |    1 -
 net/core/dev.c          |    2 +-
 net/core/wireless.c     | 2353 -----------------------------------------------
 net/wireless/Makefile   |    2 +
 net/wireless/wext-old.c | 2353 +++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 2357 insertions(+), 2356 deletions(-)

diff --git a/net/Makefile b/net/Makefile
index 278d6bf..ed07ec0 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -44,7 +44,7 @@ obj-$(CONFIG_ECONET)		+= econet/
 obj-$(CONFIG_VLAN_8021Q)	+= 8021q/
 obj-$(CONFIG_IP_DCCP)		+= dccp/
 obj-$(CONFIG_IP_SCTP)		+= sctp/
-obj-$(CONFIG_CFG80211)		+= wireless/
+obj-y				+= wireless/
 obj-$(CONFIG_IEEE80211)		+= ieee80211/
 obj-$(CONFIG_TIPC)		+= tipc/
 obj-$(CONFIG_NETLABEL)		+= netlabel/
diff --git a/net/core/Makefile b/net/core/Makefile
index 73272d5..4751613 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -13,7 +13,6 @@ obj-y		     += dev.o ethtool.o dev_mcast.o dst.o netevent.o \
 obj-$(CONFIG_XFRM) += flow.o
 obj-$(CONFIG_SYSFS) += net-sysfs.o
 obj-$(CONFIG_NET_PKTGEN) += pktgen.o
-obj-$(CONFIG_WIRELESS_EXT) += wireless.o
 obj-$(CONFIG_NETPOLL) += netpoll.o
 obj-$(CONFIG_NET_DMA) += user_dma.o
 obj-$(CONFIG_FIB_RULES) += fib_rules.o
diff --git a/net/core/dev.c b/net/core/dev.c
index e660cb5..c8822aa 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2811,7 +2811,7 @@ int dev_ioctl(unsigned int cmd, void __user *arg)
 				}
 				dev_load(ifr.ifr_name);
 				rtnl_lock();
-				/* Follow me in net/core/wireless.c */
+				/* Follow me in net/wireless/wext-old.c */
 				ret = wireless_process_ioctl(&ifr, cmd);
 				rtnl_unlock();
 				if (IW_IS_GET(cmd) &&
diff --git a/net/core/wireless.c b/net/core/wireless.c
deleted file mode 100644
index f69ab7b..0000000
--- a/net/core/wireless.c
+++ /dev/null
@@ -1,2353 +0,0 @@
-/*
- * This file implement the Wireless Extensions APIs.
- *
- * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved.
- *
- * (As all part of the Linux kernel, this file is GPL)
- */
-
-/************************** DOCUMENTATION **************************/
-/*
- * API definition :
- * --------------
- * See <linux/wireless.h> for details of the APIs and the rest.
- *
- * History :
- * -------
- *
- * v1 - 5.12.01 - Jean II
- *	o Created this file.
- *
- * v2 - 13.12.01 - Jean II
- *	o Move /proc/net/wireless stuff from net/core/dev.c to here
- *	o Make Wireless Extension IOCTLs go through here
- *	o Added iw_handler handling ;-)
- *	o Added standard ioctl description
- *	o Initial dumb commit strategy based on orinoco.c
- *
- * v3 - 19.12.01 - Jean II
- *	o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call
- *	o Add event dispatcher function
- *	o Add event description
- *	o Propagate events as rtnetlink IFLA_WIRELESS option
- *	o Generate event on selected SET requests
- *
- * v4 - 18.04.02 - Jean II
- *	o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1
- *
- * v5 - 21.06.02 - Jean II
- *	o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup)
- *	o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
- *	o Add IWEVCUSTOM for driver specific event/scanning token
- *	o Turn on WE_STRICT_WRITE by default + kernel warning
- *	o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num)
- *	o Fix off-by-one in test (extra_size <= IFNAMSIZ)
- *
- * v6 - 9.01.03 - Jean II
- *	o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
- *	o Add enhanced spy support : iw_handler_set_thrspy() and event.
- *	o Add WIRELESS_EXT version display in /proc/net/wireless
- *
- * v6 - 18.06.04 - Jean II
- *	o Change get_spydata() method for added safety
- *	o Remove spy #ifdef, they are always on -> cleaner code
- *	o Allow any size GET request if user specifies length > max
- *		and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV
- *	o Start migrating get_wireless_stats to struct iw_handler_def
- *	o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus
- * Based on patch from Pavel Roskin <proski@gnu.org> :
- *	o Fix kernel data leak to user space in private handler handling
- *
- * v7 - 18.3.05 - Jean II
- *	o Remove (struct iw_point *)->pointer from events and streams
- *	o Remove spy_offset from struct iw_handler_def
- *	o Start deprecating dev->get_wireless_stats, output a warning
- *	o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
- *	o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
- *
- * v8 - 17.02.06 - Jean II
- *	o RtNetlink requests support (SET/GET)
- *
- * v8b - 03.08.06 - Herbert Xu
- *	o Fix Wireless Event locking issues.
- *
- * v9 - 14.3.06 - Jean II
- *	o Change length in ESSID and NICK to strlen() instead of strlen()+1
- *	o Make standard_ioctl_num and standard_event_num unsigned
- *	o Remove (struct net_device *)->get_wireless_stats()
- */
-
-/***************************** INCLUDES *****************************/
-
-#include <linux/module.h>
-#include <linux/types.h>		/* off_t */
-#include <linux/netdevice.h>		/* struct ifreq, dev_get_by_name() */
-#include <linux/proc_fs.h>
-#include <linux/rtnetlink.h>		/* rtnetlink stuff */
-#include <linux/seq_file.h>
-#include <linux/init.h>			/* for __init */
-#include <linux/if_arp.h>		/* ARPHRD_ETHER */
-#include <linux/etherdevice.h>		/* compare_ether_addr */
-#include <linux/interrupt.h>
-
-#include <linux/wireless.h>		/* Pretty obvious */
-#include <net/iw_handler.h>		/* New driver API */
-#include <net/netlink.h>
-
-#include <asm/uaccess.h>		/* copy_to_user() */
-
-/**************************** CONSTANTS ****************************/
-
-/* Debugging stuff */
-#undef WE_IOCTL_DEBUG		/* Debug IOCTL API */
-#undef WE_RTNETLINK_DEBUG	/* Debug RtNetlink API */
-#undef WE_EVENT_DEBUG		/* Debug Event dispatcher */
-#undef WE_SPY_DEBUG		/* Debug enhanced spy support */
-
-/* Options */
-//CONFIG_NET_WIRELESS_RTNETLINK	/* Wireless requests over RtNetlink */
-#define WE_EVENT_RTNETLINK	/* Propagate events using RtNetlink */
-#define WE_SET_EVENT		/* Generate an event on some set commands */
-
-/************************* GLOBAL VARIABLES *************************/
-/*
- * You should not use global variables, because of re-entrancy.
- * On our case, it's only const, so it's OK...
- */
-/*
- * Meta-data about all the standard Wireless Extension request we
- * know about.
- */
-static const struct iw_ioctl_description standard_ioctl[] = {
-	[SIOCSIWCOMMIT	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWNAME	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_CHAR,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWNWID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWNWID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWFREQ	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_FREQ,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWFREQ	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_FREQ,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWMODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_UINT,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWMODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_UINT,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWSENS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWSENS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWRANGE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWRANGE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_range),
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWPRIV	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWPRIV	- SIOCIWFIRST] = { /* (handled directly by us) */
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct iw_priv_args),
-		.max_tokens	= 16,
-		.flags		= IW_DESCR_FLAG_NOMAX,
-	},
-	[SIOCSIWSTATS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWSTATS	- SIOCIWFIRST] = { /* (handled directly by us) */
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_statistics),
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct sockaddr),
-		.max_tokens	= IW_MAX_SPY,
-	},
-	[SIOCGIWSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct sockaddr) +
-				  sizeof(struct iw_quality),
-		.max_tokens	= IW_MAX_SPY,
-	},
-	[SIOCSIWTHRSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct iw_thrspy),
-		.min_tokens	= 1,
-		.max_tokens	= 1,
-	},
-	[SIOCGIWTHRSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct iw_thrspy),
-		.min_tokens	= 1,
-		.max_tokens	= 1,
-	},
-	[SIOCSIWAP	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-	},
-	[SIOCGIWAP	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWMLME	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_mlme),
-		.max_tokens	= sizeof(struct iw_mlme),
-	},
-	[SIOCGIWAPLIST	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct sockaddr) +
-				  sizeof(struct iw_quality),
-		.max_tokens	= IW_MAX_AP,
-		.flags		= IW_DESCR_FLAG_NOMAX,
-	},
-	[SIOCSIWSCAN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= 0,
-		.max_tokens	= sizeof(struct iw_scan_req),
-	},
-	[SIOCGIWSCAN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_SCAN_MAX_DATA,
-		.flags		= IW_DESCR_FLAG_NOMAX,
-	},
-	[SIOCSIWESSID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWESSID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWNICKN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-	},
-	[SIOCGIWNICKN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-	},
-	[SIOCSIWRATE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWRATE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWRTS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWRTS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWFRAG	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWFRAG	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWTXPOW	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWTXPOW	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWRETRY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWRETRY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWENCODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ENCODING_TOKEN_MAX,
-		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
-	},
-	[SIOCGIWENCODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ENCODING_TOKEN_MAX,
-		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
-	},
-	[SIOCSIWPOWER	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWPOWER	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWGENIE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[SIOCGIWGENIE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[SIOCSIWAUTH	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWAUTH	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWENCODEEXT - SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_encode_ext),
-		.max_tokens	= sizeof(struct iw_encode_ext) +
-				  IW_ENCODING_TOKEN_MAX,
-	},
-	[SIOCGIWENCODEEXT - SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_encode_ext),
-		.max_tokens	= sizeof(struct iw_encode_ext) +
-				  IW_ENCODING_TOKEN_MAX,
-	},
-	[SIOCSIWPMKSA - SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_pmksa),
-		.max_tokens	= sizeof(struct iw_pmksa),
-	},
-};
-static const unsigned standard_ioctl_num = (sizeof(standard_ioctl) /
-					    sizeof(struct iw_ioctl_description));
-
-/*
- * Meta-data about all the additional standard Wireless Extension events
- * we know about.
- */
-static const struct iw_ioctl_description standard_event[] = {
-	[IWEVTXDROP	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-	},
-	[IWEVQUAL	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_QUAL,
-	},
-	[IWEVCUSTOM	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_CUSTOM_MAX,
-	},
-	[IWEVREGISTERED	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-	},
-	[IWEVEXPIRED	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR, 
-	},
-	[IWEVGENIE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[IWEVMICHAELMICFAILURE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT, 
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_michaelmicfailure),
-	},
-	[IWEVASSOCREQIE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[IWEVASSOCRESPIE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[IWEVPMKIDCAND	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_pmkid_cand),
-	},
-};
-static const unsigned standard_event_num = (sizeof(standard_event) /
-					    sizeof(struct iw_ioctl_description));
-
-/* Size (in bytes) of the various private data types */
-static const char iw_priv_type_size[] = {
-	0,				/* IW_PRIV_TYPE_NONE */
-	1,				/* IW_PRIV_TYPE_BYTE */
-	1,				/* IW_PRIV_TYPE_CHAR */
-	0,				/* Not defined */
-	sizeof(__u32),			/* IW_PRIV_TYPE_INT */
-	sizeof(struct iw_freq),		/* IW_PRIV_TYPE_FLOAT */
-	sizeof(struct sockaddr),	/* IW_PRIV_TYPE_ADDR */
-	0,				/* Not defined */
-};
-
-/* Size (in bytes) of various events */
-static const int event_type_size[] = {
-	IW_EV_LCP_LEN,			/* IW_HEADER_TYPE_NULL */
-	0,
-	IW_EV_CHAR_LEN,			/* IW_HEADER_TYPE_CHAR */
-	0,
-	IW_EV_UINT_LEN,			/* IW_HEADER_TYPE_UINT */
-	IW_EV_FREQ_LEN,			/* IW_HEADER_TYPE_FREQ */
-	IW_EV_ADDR_LEN,			/* IW_HEADER_TYPE_ADDR */
-	0,
-	IW_EV_POINT_LEN,		/* Without variable payload */
-	IW_EV_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */
-	IW_EV_QUAL_LEN,			/* IW_HEADER_TYPE_QUAL */
-};
-
-/************************ COMMON SUBROUTINES ************************/
-/*
- * Stuff that may be used in various place or doesn't fit in one
- * of the section below.
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * Return the driver handler associated with a specific Wireless Extension.
- * Called from various place, so make sure it remains efficient.
- */
-static inline iw_handler get_handler(struct net_device *dev,
-				     unsigned int cmd)
-{
-	/* Don't "optimise" the following variable, it will crash */
-	unsigned int	index;		/* *MUST* be unsigned */
-
-	/* Check if we have some wireless handlers defined */
-	if(dev->wireless_handlers == NULL)
-		return NULL;
-
-	/* Try as a standard command */
-	index = cmd - SIOCIWFIRST;
-	if(index < dev->wireless_handlers->num_standard)
-		return dev->wireless_handlers->standard[index];
-
-	/* Try as a private command */
-	index = cmd - SIOCIWFIRSTPRIV;
-	if(index < dev->wireless_handlers->num_private)
-		return dev->wireless_handlers->private[index];
-
-	/* Not found */
-	return NULL;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Get statistics out of the driver
- */
-static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
-{
-	/* New location */
-	if((dev->wireless_handlers != NULL) &&
-	   (dev->wireless_handlers->get_wireless_stats != NULL))
-		return dev->wireless_handlers->get_wireless_stats(dev);
-
-	/* Not found */
-	return (struct iw_statistics *) NULL;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Call the commit handler in the driver
- * (if exist and if conditions are right)
- *
- * Note : our current commit strategy is currently pretty dumb,
- * but we will be able to improve on that...
- * The goal is to try to agreagate as many changes as possible
- * before doing the commit. Drivers that will define a commit handler
- * are usually those that need a reset after changing parameters, so
- * we want to minimise the number of reset.
- * A cool idea is to use a timer : at each "set" command, we re-set the
- * timer, when the timer eventually fires, we call the driver.
- * Hopefully, more on that later.
- *
- * Also, I'm waiting to see how many people will complain about the
- * netif_running(dev) test. I'm open on that one...
- * Hopefully, the driver will remember to do a commit in "open()" ;-)
- */
-static inline int call_commit_handler(struct net_device *	dev)
-{
-	if((netif_running(dev)) &&
-	   (dev->wireless_handlers->standard[0] != NULL)) {
-		/* Call the commit handler on the driver */
-		return dev->wireless_handlers->standard[0](dev, NULL,
-							   NULL, NULL);
-	} else
-		return 0;		/* Command completed successfully */
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Calculate size of private arguments
- */
-static inline int get_priv_size(__u16	args)
-{
-	int	num = args & IW_PRIV_SIZE_MASK;
-	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;
-
-	return num * iw_priv_type_size[type];
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Re-calculate the size of private arguments
- */
-static inline int adjust_priv_size(__u16		args,
-				   union iwreq_data *	wrqu)
-{
-	int	num = wrqu->data.length;
-	int	max = args & IW_PRIV_SIZE_MASK;
-	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;
-
-	/* Make sure the driver doesn't goof up */
-	if (max < num)
-		num = max;
-
-	return num * iw_priv_type_size[type];
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Standard Wireless Handler : get wireless stats
- *	Allow programatic access to /proc/net/wireless even if /proc
- *	doesn't exist... Also more efficient...
- */
-static int iw_handler_get_iwstats(struct net_device *		dev,
-				  struct iw_request_info *	info,
-				  union iwreq_data *		wrqu,
-				  char *			extra)
-{
-	/* Get stats from the driver */
-	struct iw_statistics *stats;
-
-	stats = get_wireless_stats(dev);
-	if (stats != (struct iw_statistics *) NULL) {
-
-		/* Copy statistics to extra */
-		memcpy(extra, stats, sizeof(struct iw_statistics));
-		wrqu->data.length = sizeof(struct iw_statistics);
-
-		/* Check if we need to clear the updated flag */
-		if(wrqu->data.flags != 0)
-			stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
-		return 0;
-	} else
-		return -EOPNOTSUPP;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Standard Wireless Handler : get iwpriv definitions
- * Export the driver private handler definition
- * They will be picked up by tools like iwpriv...
- */
-static int iw_handler_get_private(struct net_device *		dev,
-				  struct iw_request_info *	info,
-				  union iwreq_data *		wrqu,
-				  char *			extra)
-{
-	/* Check if the driver has something to export */
-	if((dev->wireless_handlers->num_private_args == 0) ||
-	   (dev->wireless_handlers->private_args == NULL))
-		return -EOPNOTSUPP;
-
-	/* Check if there is enough buffer up there */
-	if(wrqu->data.length < dev->wireless_handlers->num_private_args) {
-		/* User space can't know in advance how large the buffer
-		 * needs to be. Give it a hint, so that we can support
-		 * any size buffer we want somewhat efficiently... */
-		wrqu->data.length = dev->wireless_handlers->num_private_args;
-		return -E2BIG;
-	}
-
-	/* Set the number of available ioctls. */
-	wrqu->data.length = dev->wireless_handlers->num_private_args;
-
-	/* Copy structure to the user buffer. */
-	memcpy(extra, dev->wireless_handlers->private_args,
-	       sizeof(struct iw_priv_args) * wrqu->data.length);
-
-	return 0;
-}
-
-
-/******************** /proc/net/wireless SUPPORT ********************/
-/*
- * The /proc/net/wireless file is a human readable user-space interface
- * exporting various wireless specific statistics from the wireless devices.
- * This is the most popular part of the Wireless Extensions ;-)
- *
- * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
- * The content of the file is basically the content of "struct iw_statistics".
- */
-
-#ifdef CONFIG_PROC_FS
-
-/* ---------------------------------------------------------------- */
-/*
- * Print one entry (line) of /proc/net/wireless
- */
-static __inline__ void wireless_seq_printf_stats(struct seq_file *seq,
-						 struct net_device *dev)
-{
-	/* Get stats from the driver */
-	struct iw_statistics *stats = get_wireless_stats(dev);
-
-	if (stats) {
-		seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
-				"%6d %6d   %6d\n",
-			   dev->name, stats->status, stats->qual.qual,
-			   stats->qual.updated & IW_QUAL_QUAL_UPDATED
-			   ? '.' : ' ',
-			   ((__s32) stats->qual.level) - 
-			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
-			   stats->qual.updated & IW_QUAL_LEVEL_UPDATED
-			   ? '.' : ' ',
-			   ((__s32) stats->qual.noise) - 
-			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
-			   stats->qual.updated & IW_QUAL_NOISE_UPDATED
-			   ? '.' : ' ',
-			   stats->discard.nwid, stats->discard.code,
-			   stats->discard.fragment, stats->discard.retries,
-			   stats->discard.misc, stats->miss.beacon);
-		stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
-	}
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Print info for /proc/net/wireless (print all entries)
- */
-static int wireless_seq_show(struct seq_file *seq, void *v)
-{
-	if (v == SEQ_START_TOKEN)
-		seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
-				"packets               | Missed | WE\n"
-				" face | tus | link level noise |  nwid  "
-				"crypt   frag  retry   misc | beacon | %d\n",
-			   WIRELESS_EXT);
-	else
-		wireless_seq_printf_stats(seq, v);
-	return 0;
-}
-
-static struct seq_operations wireless_seq_ops = {
-	.start = dev_seq_start,
-	.next  = dev_seq_next,
-	.stop  = dev_seq_stop,
-	.show  = wireless_seq_show,
-};
-
-static int wireless_seq_open(struct inode *inode, struct file *file)
-{
-	return seq_open(file, &wireless_seq_ops);
-}
-
-static struct file_operations wireless_seq_fops = {
-	.owner	 = THIS_MODULE,
-	.open    = wireless_seq_open,
-	.read    = seq_read,
-	.llseek  = seq_lseek,
-	.release = seq_release,
-};
-
-int __init wireless_proc_init(void)
-{
-	/* Create /proc/net/wireless entry */
-	if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
-		return -ENOMEM;
-
-	return 0;
-}
-#endif	/* CONFIG_PROC_FS */
-
-/************************** IOCTL SUPPORT **************************/
-/*
- * The original user space API to configure all those Wireless Extensions
- * is through IOCTLs.
- * In there, we check if we need to call the new driver API (iw_handler)
- * or just call the driver ioctl handler.
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a standard Wireless Extension handler.
- * We do various checks and also take care of moving data between
- * user space and kernel space.
- */
-static int ioctl_standard_call(struct net_device *	dev,
-			       struct ifreq *		ifr,
-			       unsigned int		cmd,
-			       iw_handler		handler)
-{
-	struct iwreq *				iwr = (struct iwreq *) ifr;
-	const struct iw_ioctl_description *	descr;
-	struct iw_request_info			info;
-	int					ret = -EINVAL;
-
-	/* Get the description of the IOCTL */
-	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
-		return -EOPNOTSUPP;
-	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
-
-#ifdef WE_IOCTL_DEBUG
-	printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n",
-	       ifr->ifr_name, cmd);
-	printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif	/* WE_IOCTL_DEBUG */
-
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
-	/* Check if we have a pointer to user space data or not */
-	if(descr->header_type != IW_HEADER_TYPE_POINT) {
-
-		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, &(iwr->u), NULL);
-
-#ifdef WE_SET_EVENT
-		/* Generate an event to notify listeners of the change */
-		if((descr->flags & IW_DESCR_FLAG_EVENT) &&
-		   ((ret == 0) || (ret == -EIWCOMMIT)))
-			wireless_send_event(dev, cmd, &(iwr->u), NULL);
-#endif	/* WE_SET_EVENT */
-	} else {
-		char *	extra;
-		int	extra_size;
-		int	user_length = 0;
-		int	err;
-		int	essid_compat = 0;
-
-		/* Calculate space needed by arguments. Always allocate
-		 * for max space. Easier, and won't last long... */
-		extra_size = descr->max_tokens * descr->token_size;
-
-		/* Check need for ESSID compatibility for WE < 21 */
-		switch (cmd) {
-		case SIOCSIWESSID:
-		case SIOCGIWESSID:
-		case SIOCSIWNICKN:
-		case SIOCGIWNICKN:
-			if (iwr->u.data.length == descr->max_tokens + 1)
-				essid_compat = 1;
-			else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
-				char essid[IW_ESSID_MAX_SIZE + 1];
-
-				err = copy_from_user(essid, iwr->u.data.pointer,
-						     iwr->u.data.length *
-						     descr->token_size);
-				if (err)
-					return -EFAULT;
-
-				if (essid[iwr->u.data.length - 1] == '\0')
-					essid_compat = 1;
-			}
-			break;
-		default:
-			break;
-		}
-
-		iwr->u.data.length -= essid_compat;
-
-		/* Check what user space is giving us */
-		if(IW_IS_SET(cmd)) {
-			/* Check NULL pointer */
-			if((iwr->u.data.pointer == NULL) &&
-			   (iwr->u.data.length != 0))
-				return -EFAULT;
-			/* Check if number of token fits within bounds */
-			if(iwr->u.data.length > descr->max_tokens)
-				return -E2BIG;
-			if(iwr->u.data.length < descr->min_tokens)
-				return -EINVAL;
-		} else {
-			/* Check NULL pointer */
-			if(iwr->u.data.pointer == NULL)
-				return -EFAULT;
-			/* Save user space buffer size for checking */
-			user_length = iwr->u.data.length;
-
-			/* Don't check if user_length > max to allow forward
-			 * compatibility. The test user_length < min is
-			 * implied by the test at the end. */
-
-			/* Support for very large requests */
-			if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
-			   (user_length > descr->max_tokens)) {
-				/* Allow userspace to GET more than max so
-				 * we can support any size GET requests.
-				 * There is still a limit : -ENOMEM. */
-				extra_size = user_length * descr->token_size;
-				/* Note : user_length is originally a __u16,
-				 * and token_size is controlled by us,
-				 * so extra_size won't get negative and
-				 * won't overflow... */
-			}
-		}
-
-#ifdef WE_IOCTL_DEBUG
-		printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
-		       dev->name, extra_size);
-#endif	/* WE_IOCTL_DEBUG */
-
-		/* Create the kernel buffer */
-		/*    kzalloc ensures NULL-termination for essid_compat */
-		extra = kzalloc(extra_size, GFP_KERNEL);
-		if (extra == NULL) {
-			return -ENOMEM;
-		}
-
-		/* If it is a SET, get all the extra data in here */
-		if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
-			err = copy_from_user(extra, iwr->u.data.pointer,
-					     iwr->u.data.length *
-					     descr->token_size);
-			if (err) {
-				kfree(extra);
-				return -EFAULT;
-			}
-#ifdef WE_IOCTL_DEBUG
-			printk(KERN_DEBUG "%s (WE) : Got %d bytes\n",
-			       dev->name,
-			       iwr->u.data.length * descr->token_size);
-#endif	/* WE_IOCTL_DEBUG */
-		}
-
-		/* Call the handler */
-		ret = handler(dev, &info, &(iwr->u), extra);
-
-		iwr->u.data.length += essid_compat;
-
-		/* If we have something to return to the user */
-		if (!ret && IW_IS_GET(cmd)) {
-			/* Check if there is enough buffer up there */
-			if(user_length < iwr->u.data.length) {
-				kfree(extra);
-				return -E2BIG;
-			}
-
-			err = copy_to_user(iwr->u.data.pointer, extra,
-					   iwr->u.data.length *
-					   descr->token_size);
-			if (err)
-				ret =  -EFAULT;				   
-#ifdef WE_IOCTL_DEBUG
-			printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n",
-			       dev->name,
-			       iwr->u.data.length * descr->token_size);
-#endif	/* WE_IOCTL_DEBUG */
-		}
-
-#ifdef WE_SET_EVENT
-		/* Generate an event to notify listeners of the change */
-		if((descr->flags & IW_DESCR_FLAG_EVENT) &&
-		   ((ret == 0) || (ret == -EIWCOMMIT))) {
-			if(descr->flags & IW_DESCR_FLAG_RESTRICT)
-				/* If the event is restricted, don't
-				 * export the payload */
-				wireless_send_event(dev, cmd, &(iwr->u), NULL);
-			else
-				wireless_send_event(dev, cmd, &(iwr->u),
-						    extra);
-		}
-#endif	/* WE_SET_EVENT */
-
-		/* Cleanup - I told you it wasn't that long ;-) */
-		kfree(extra);
-	}
-
-	/* Call commit handler if needed and defined */
-	if(ret == -EIWCOMMIT)
-		ret = call_commit_handler(dev);
-
-	/* Here, we will generate the appropriate event if needed */
-
-	return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a private Wireless Extension handler.
- * We do various checks and also take care of moving data between
- * user space and kernel space.
- * It's not as nice and slimline as the standard wrapper. The cause
- * is struct iw_priv_args, which was not really designed for the
- * job we are going here.
- *
- * IMPORTANT : This function prevent to set and get data on the same
- * IOCTL and enforce the SET/GET convention. Not doing it would be
- * far too hairy...
- * If you need to set and get data at the same time, please don't use
- * a iw_handler but process it in your ioctl handler (i.e. use the
- * old driver API).
- */
-static inline int ioctl_private_call(struct net_device *	dev,
-				     struct ifreq *		ifr,
-				     unsigned int		cmd,
-				     iw_handler		handler)
-{
-	struct iwreq *			iwr = (struct iwreq *) ifr;
-	const struct iw_priv_args *	descr = NULL;
-	struct iw_request_info		info;
-	int				extra_size = 0;
-	int				i;
-	int				ret = -EINVAL;
-
-	/* Get the description of the IOCTL */
-	for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
-		if(cmd == dev->wireless_handlers->private_args[i].cmd) {
-			descr = &(dev->wireless_handlers->private_args[i]);
-			break;
-		}
-
-#ifdef WE_IOCTL_DEBUG
-	printk(KERN_DEBUG "%s (WE) : Found private handler for 0x%04X\n",
-	       ifr->ifr_name, cmd);
-	if(descr) {
-		printk(KERN_DEBUG "%s (WE) : Name %s, set %X, get %X\n",
-		       dev->name, descr->name,
-		       descr->set_args, descr->get_args);
-	}
-#endif	/* WE_IOCTL_DEBUG */
-
-	/* Compute the size of the set/get arguments */
-	if(descr != NULL) {
-		if(IW_IS_SET(cmd)) {
-			int	offset = 0;	/* For sub-ioctls */
-			/* Check for sub-ioctl handler */
-			if(descr->name[0] == '\0')
-				/* Reserve one int for sub-ioctl index */
-				offset = sizeof(__u32);
-
-			/* Size of set arguments */
-			extra_size = get_priv_size(descr->set_args);
-
-			/* Does it fits in iwr ? */
-			if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
-			   ((extra_size + offset) <= IFNAMSIZ))
-				extra_size = 0;
-		} else {
-			/* Size of get arguments */
-			extra_size = get_priv_size(descr->get_args);
-
-			/* Does it fits in iwr ? */
-			if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
-			   (extra_size <= IFNAMSIZ))
-				extra_size = 0;
-		}
-	}
-
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
-	/* Check if we have a pointer to user space data or not. */
-	if(extra_size == 0) {
-		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
-	} else {
-		char *	extra;
-		int	err;
-
-		/* Check what user space is giving us */
-		if(IW_IS_SET(cmd)) {
-			/* Check NULL pointer */
-			if((iwr->u.data.pointer == NULL) &&
-			   (iwr->u.data.length != 0))
-				return -EFAULT;
-
-			/* Does it fits within bounds ? */
-			if(iwr->u.data.length > (descr->set_args &
-						 IW_PRIV_SIZE_MASK))
-				return -E2BIG;
-		} else {
-			/* Check NULL pointer */
-			if(iwr->u.data.pointer == NULL)
-				return -EFAULT;
-		}
-
-#ifdef WE_IOCTL_DEBUG
-		printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
-		       dev->name, extra_size);
-#endif	/* WE_IOCTL_DEBUG */
-
-		/* Always allocate for max space. Easier, and won't last
-		 * long... */
-		extra = kmalloc(extra_size, GFP_KERNEL);
-		if (extra == NULL) {
-			return -ENOMEM;
-		}
-
-		/* If it is a SET, get all the extra data in here */
-		if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
-			err = copy_from_user(extra, iwr->u.data.pointer,
-					     extra_size);
-			if (err) {
-				kfree(extra);
-				return -EFAULT;
-			}
-#ifdef WE_IOCTL_DEBUG
-			printk(KERN_DEBUG "%s (WE) : Got %d elem\n",
-			       dev->name, iwr->u.data.length);
-#endif	/* WE_IOCTL_DEBUG */
-		}
-
-		/* Call the handler */
-		ret = handler(dev, &info, &(iwr->u), extra);
-
-		/* If we have something to return to the user */
-		if (!ret && IW_IS_GET(cmd)) {
-
-			/* Adjust for the actual length if it's variable,
-			 * avoid leaking kernel bits outside. */
-			if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
-				extra_size = adjust_priv_size(descr->get_args,
-							      &(iwr->u));
-			}
-
-			err = copy_to_user(iwr->u.data.pointer, extra,
-					   extra_size);
-			if (err)
-				ret =  -EFAULT;				   
-#ifdef WE_IOCTL_DEBUG
-			printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n",
-			       dev->name, iwr->u.data.length);
-#endif	/* WE_IOCTL_DEBUG */
-		}
-
-		/* Cleanup - I told you it wasn't that long ;-) */
-		kfree(extra);
-	}
-
-
-	/* Call commit handler if needed and defined */
-	if(ret == -EIWCOMMIT)
-		ret = call_commit_handler(dev);
-
-	return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Main IOCTl dispatcher. Called from the main networking code
- * (dev_ioctl() in net/core/dev.c).
- * Check the type of IOCTL and call the appropriate wrapper...
- */
-int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
-{
-	struct net_device *dev;
-	iw_handler	handler;
-
-	/* Permissions are already checked in dev_ioctl() before calling us.
-	 * The copy_to/from_user() of ifr is also dealt with in there */
-
-	/* Make sure the device exist */
-	if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
-		return -ENODEV;
-
-	/* A bunch of special cases, then the generic case...
-	 * Note that 'cmd' is already filtered in dev_ioctl() with
-	 * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
-	switch(cmd) 
-	{
-		case SIOCGIWSTATS:
-			/* Get Wireless Stats */
-			return ioctl_standard_call(dev,
-						   ifr,
-						   cmd,
-						   &iw_handler_get_iwstats);
-
-		case SIOCGIWPRIV:
-			/* Check if we have some wireless handlers defined */
-			if(dev->wireless_handlers != NULL) {
-				/* We export to user space the definition of
-				 * the private handler ourselves */
-				return ioctl_standard_call(dev,
-							   ifr,
-							   cmd,
-							   &iw_handler_get_private);
-			}
-			// ## Fall-through for old API ##
-		default:
-			/* Generic IOCTL */
-			/* Basic check */
-			if (!netif_device_present(dev))
-				return -ENODEV;
-			/* New driver API : try to find the handler */
-			handler = get_handler(dev, cmd);
-			if(handler != NULL) {
-				/* Standard and private are not the same */
-				if(cmd < SIOCIWFIRSTPRIV)
-					return ioctl_standard_call(dev,
-								   ifr,
-								   cmd,
-								   handler);
-				else
-					return ioctl_private_call(dev,
-								  ifr,
-								  cmd,
-								  handler);
-			}
-			/* Old driver API : call driver ioctl handler */
-			if (dev->do_ioctl) {
-				return dev->do_ioctl(dev, ifr, cmd);
-			}
-			return -EOPNOTSUPP;
-	}
-	/* Not reached */
-	return -EINVAL;
-}
-
-/********************** RTNETLINK REQUEST API **********************/
-/*
- * The alternate user space API to configure all those Wireless Extensions
- * is through RtNetlink.
- * This API support only the new driver API (iw_handler).
- *
- * This RtNetlink API use the same query/reply model as the ioctl API.
- * Maximum effort has been done to fit in the RtNetlink model, and
- * we support both RtNetlink Set and RtNelink Get operations.
- * On the other hand, we don't offer Dump operations because of the
- * following reasons :
- *	o Large number of parameters, most optional
- *	o Large size of some parameters (> 100 bytes)
- *	o Each parameters need to be extracted from hardware
- *	o Scan requests can take seconds and disable network activity.
- * Because of this high cost/overhead, we want to return only the
- * parameters the user application is really interested in.
- * We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag.
- *
- * The API uses the standard RtNetlink socket. When the RtNetlink code
- * find a IFLA_WIRELESS field in a RtNetlink SET_LINK request,
- * it calls here.
- */
-
-#ifdef CONFIG_NET_WIRELESS_RTNETLINK
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a standard Wireless Extension GET handler.
- * We do various checks and call the handler with the proper args.
- */
-static int rtnetlink_standard_get(struct net_device *	dev,
-				  struct iw_event *	request,
-				  int			request_len,
-				  iw_handler		handler,
-				  char **		p_buf,
-				  int *			p_len)
-{
-	const struct iw_ioctl_description *	descr = NULL;
-	unsigned int				cmd;
-	union iwreq_data *			wrqu;
-	int					hdr_len;
-	struct iw_request_info			info;
-	char *					buffer = NULL;
-	int					buffer_size = 0;
-	int					ret = -EINVAL;
-
-	/* Get the description of the Request */
-	cmd = request->cmd;
-	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
-		return -EOPNOTSUPP;
-	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
-
-#ifdef WE_RTNETLINK_DEBUG
-	printk(KERN_DEBUG "%s (WE.r) : Found standard handler for 0x%04X\n",
-	       dev->name, cmd);
-	printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-	/* Check if wrqu is complete */
-	hdr_len = event_type_size[descr->header_type];
-	if(request_len < hdr_len) {
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG
-		       "%s (WE.r) : Wireless request too short (%d)\n",
-		       dev->name, request_len);
-#endif	/* WE_RTNETLINK_DEBUG */
-		return -EINVAL;
-	}
-
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
-	/* Check if we have extra data in the reply or not */
-	if(descr->header_type != IW_HEADER_TYPE_POINT) {
-
-		/* Create the kernel buffer that we will return.
-		 * It's at an offset to match the TYPE_POINT case... */
-		buffer_size = request_len + IW_EV_POINT_OFF;
-		buffer = kmalloc(buffer_size, GFP_KERNEL);
-		if (buffer == NULL) {
-			return -ENOMEM;
-		}
-		/* Copy event data */
-		memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
-		/* Use our own copy of wrqu */
-		wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
-					     + IW_EV_LCP_LEN);
-
-		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, wrqu, NULL);
-
-	} else {
-		union iwreq_data	wrqu_point;
-		char *			extra = NULL;
-		int			extra_size = 0;
-
-		/* Get a temp copy of wrqu (skip pointer) */
-		memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
-		       ((char *) request) + IW_EV_LCP_LEN,
-		       IW_EV_POINT_LEN - IW_EV_LCP_LEN);
-
-		/* Calculate space needed by arguments. Always allocate
-		 * for max space. Easier, and won't last long... */
-		extra_size = descr->max_tokens * descr->token_size;
-		/* Support for very large requests */
-		if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
-		   (wrqu_point.data.length > descr->max_tokens))
-			extra_size = (wrqu_point.data.length
-				      * descr->token_size);
-		buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
-		       dev->name, extra_size, buffer_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-		/* Create the kernel buffer that we will return */
-		buffer = kmalloc(buffer_size, GFP_KERNEL);
-		if (buffer == NULL) {
-			return -ENOMEM;
-		}
-
-		/* Put wrqu in the right place (just before extra).
-		 * Leave space for IWE header and dummy pointer...
-		 * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
-		 */
-		memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
-		       ((char *) &wrqu_point) + IW_EV_POINT_OFF,
-		       IW_EV_POINT_LEN - IW_EV_LCP_LEN);
-		wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
-
-		/* Extra comes logically after that. Offset +12 bytes. */
-		extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
-
-		/* Call the handler */
-		ret = handler(dev, &info, wrqu, extra);
-
-		/* Calculate real returned length */
-		extra_size = (wrqu->data.length * descr->token_size);
-		/* Re-adjust reply size */
-		request->len = extra_size + IW_EV_POINT_LEN;
-
-		/* Put the iwe header where it should, i.e. scrap the
-		 * dummy pointer. */
-		memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
-
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-		/* Check if there is enough buffer up there */
-		if(wrqu_point.data.length < wrqu->data.length)
-			ret = -E2BIG;
-	}
-
-	/* Return the buffer to the caller */
-	if (!ret) {
-		*p_buf = buffer;
-		*p_len = request->len;
-	} else {
-		/* Cleanup */
-		if(buffer)
-			kfree(buffer);
-	}
-
-	return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a standard Wireless Extension SET handler.
- * We do various checks and call the handler with the proper args.
- */
-static inline int rtnetlink_standard_set(struct net_device *	dev,
-					 struct iw_event *	request,
-					 int			request_len,
-					 iw_handler		handler)
-{
-	const struct iw_ioctl_description *	descr = NULL;
-	unsigned int				cmd;
-	union iwreq_data *			wrqu;
-	union iwreq_data			wrqu_point;
-	int					hdr_len;
-	char *					extra = NULL;
-	int					extra_size = 0;
-	struct iw_request_info			info;
-	int					ret = -EINVAL;
-
-	/* Get the description of the Request */
-	cmd = request->cmd;
-	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
-		return -EOPNOTSUPP;
-	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
-
-#ifdef WE_RTNETLINK_DEBUG
-	printk(KERN_DEBUG "%s (WE.r) : Found standard SET handler for 0x%04X\n",
-	       dev->name, cmd);
-	printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-	/* Extract fixed header from request. This is properly aligned. */
-	wrqu = &request->u;
-
-	/* Check if wrqu is complete */
-	hdr_len = event_type_size[descr->header_type];
-	if(request_len < hdr_len) {
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG
-		       "%s (WE.r) : Wireless request too short (%d)\n",
-		       dev->name, request_len);
-#endif	/* WE_RTNETLINK_DEBUG */
-		return -EINVAL;
-	}
-
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
-	/* Check if we have extra data in the request or not */
-	if(descr->header_type != IW_HEADER_TYPE_POINT) {
-
-		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, wrqu, NULL);
-
-	} else {
-		int	extra_len;
-
-		/* Put wrqu in the right place (skip pointer) */
-		memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
-		       wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
-		/* Don't forget about the event code... */
-		wrqu = &wrqu_point;
-
-		/* Check if number of token fits within bounds */
-		if(wrqu_point.data.length > descr->max_tokens)
-			return -E2BIG;
-		if(wrqu_point.data.length < descr->min_tokens)
-			return -EINVAL;
-
-		/* Real length of payload */
-		extra_len = wrqu_point.data.length * descr->token_size;
-
-		/* Check if request is self consistent */
-		if((request_len - hdr_len) < extra_len) {
-#ifdef WE_RTNETLINK_DEBUG
-			printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
-			       dev->name, extra_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-			return -EINVAL;
-		}
-
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
-		       dev->name, extra_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-		/* Always allocate for max space. Easier, and won't last
-		 * long... */
-		extra_size = descr->max_tokens * descr->token_size;
-		extra = kmalloc(extra_size, GFP_KERNEL);
-		if (extra == NULL)
-			return -ENOMEM;
-
-		/* Copy extra in aligned buffer */
-		memcpy(extra, ((char *) request) + hdr_len, extra_len);
-
-		/* Call the handler */
-		ret = handler(dev, &info, &wrqu_point, extra);
-	}
-
-#ifdef WE_SET_EVENT
-	/* Generate an event to notify listeners of the change */
-	if((descr->flags & IW_DESCR_FLAG_EVENT) &&
-	   ((ret == 0) || (ret == -EIWCOMMIT))) {
-		if(descr->flags & IW_DESCR_FLAG_RESTRICT)
-			/* If the event is restricted, don't
-			 * export the payload */
-			wireless_send_event(dev, cmd, wrqu, NULL);
-		else
-			wireless_send_event(dev, cmd, wrqu, extra);
-	}
-#endif	/* WE_SET_EVENT */
-
-	/* Cleanup - I told you it wasn't that long ;-) */
-	if(extra)
-		kfree(extra);
-
-	/* Call commit handler if needed and defined */
-	if(ret == -EIWCOMMIT)
-		ret = call_commit_handler(dev);
-
-	return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a private Wireless Extension GET handler.
- * Same as above...
- * It's not as nice and slimline as the standard wrapper. The cause
- * is struct iw_priv_args, which was not really designed for the
- * job we are going here.
- *
- * IMPORTANT : This function prevent to set and get data on the same
- * IOCTL and enforce the SET/GET convention. Not doing it would be
- * far too hairy...
- * If you need to set and get data at the same time, please don't use
- * a iw_handler but process it in your ioctl handler (i.e. use the
- * old driver API).
- */
-static inline int rtnetlink_private_get(struct net_device *	dev,
-					struct iw_event *	request,
-					int			request_len,
-					iw_handler		handler,
-					char **			p_buf,
-					int *			p_len)
-{
-	const struct iw_priv_args *	descr = NULL;
-	unsigned int			cmd;
-	union iwreq_data *		wrqu;
-	int				hdr_len;
-	struct iw_request_info		info;
-	int				extra_size = 0;
-	int				i;
-	char *				buffer = NULL;
-	int				buffer_size = 0;
-	int				ret = -EINVAL;
-
-	/* Get the description of the Request */
-	cmd = request->cmd;
-	for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
-		if(cmd == dev->wireless_handlers->private_args[i].cmd) {
-			descr = &(dev->wireless_handlers->private_args[i]);
-			break;
-		}
-	if(descr == NULL)
-		return -EOPNOTSUPP;
-
-#ifdef WE_RTNETLINK_DEBUG
-	printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
-	       dev->name, cmd);
-	printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
-	       dev->name, descr->name, descr->set_args, descr->get_args);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-	/* Compute the max size of the get arguments */
-	extra_size = get_priv_size(descr->get_args);
-
-	/* Does it fits in wrqu ? */
-	if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
-	   (extra_size <= IFNAMSIZ)) {
-		hdr_len = extra_size;
-		extra_size = 0;
-	} else {
-		hdr_len = IW_EV_POINT_LEN;
-	}
-
-	/* Check if wrqu is complete */
-	if(request_len < hdr_len) {
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG
-		       "%s (WE.r) : Wireless request too short (%d)\n",
-		       dev->name, request_len);
-#endif	/* WE_RTNETLINK_DEBUG */
-		return -EINVAL;
-	}
-
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
-	/* Check if we have a pointer to user space data or not. */
-	if(extra_size == 0) {
-
-		/* Create the kernel buffer that we will return.
-		 * It's at an offset to match the TYPE_POINT case... */
-		buffer_size = request_len + IW_EV_POINT_OFF;
-		buffer = kmalloc(buffer_size, GFP_KERNEL);
-		if (buffer == NULL) {
-			return -ENOMEM;
-		}
-		/* Copy event data */
-		memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
-		/* Use our own copy of wrqu */
-		wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
-					     + IW_EV_LCP_LEN);
-
-		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, wrqu, (char *) wrqu);
-
-	} else {
-		char *	extra;
-
-		/* Buffer for full reply */
-		buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
-
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
-		       dev->name, extra_size, buffer_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-		/* Create the kernel buffer that we will return */
-		buffer = kmalloc(buffer_size, GFP_KERNEL);
-		if (buffer == NULL) {
-			return -ENOMEM;
-		}
-
-		/* Put wrqu in the right place (just before extra).
-		 * Leave space for IWE header and dummy pointer...
-		 * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
-		 */
-		memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
-		       ((char *) request) + IW_EV_LCP_LEN,
-		       IW_EV_POINT_LEN - IW_EV_LCP_LEN);
-		wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
-
-		/* Extra comes logically after that. Offset +12 bytes. */
-		extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
-
-		/* Call the handler */
-		ret = handler(dev, &info, wrqu, extra);
-
-		/* Adjust for the actual length if it's variable,
-		 * avoid leaking kernel bits outside. */
-		if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
-			extra_size = adjust_priv_size(descr->get_args, wrqu);
-		/* Re-adjust reply size */
-		request->len = extra_size + IW_EV_POINT_LEN;
-
-		/* Put the iwe header where it should, i.e. scrap the
-		 * dummy pointer. */
-		memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
-
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-	}
-
-	/* Return the buffer to the caller */
-	if (!ret) {
-		*p_buf = buffer;
-		*p_len = request->len;
-	} else {
-		/* Cleanup */
-		if(buffer)
-			kfree(buffer);
-	}
-
-	return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a private Wireless Extension SET handler.
- * Same as above...
- * It's not as nice and slimline as the standard wrapper. The cause
- * is struct iw_priv_args, which was not really designed for the
- * job we are going here.
- *
- * IMPORTANT : This function prevent to set and get data on the same
- * IOCTL and enforce the SET/GET convention. Not doing it would be
- * far too hairy...
- * If you need to set and get data at the same time, please don't use
- * a iw_handler but process it in your ioctl handler (i.e. use the
- * old driver API).
- */
-static inline int rtnetlink_private_set(struct net_device *	dev,
-					struct iw_event *	request,
-					int			request_len,
-					iw_handler		handler)
-{
-	const struct iw_priv_args *	descr = NULL;
-	unsigned int			cmd;
-	union iwreq_data *		wrqu;
-	union iwreq_data		wrqu_point;
-	int				hdr_len;
-	char *				extra = NULL;
-	int				extra_size = 0;
-	int				offset = 0;	/* For sub-ioctls */
-	struct iw_request_info		info;
-	int				i;
-	int				ret = -EINVAL;
-
-	/* Get the description of the Request */
-	cmd = request->cmd;
-	for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
-		if(cmd == dev->wireless_handlers->private_args[i].cmd) {
-			descr = &(dev->wireless_handlers->private_args[i]);
-			break;
-		}
-	if(descr == NULL)
-		return -EOPNOTSUPP;
-
-#ifdef WE_RTNETLINK_DEBUG
-	printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
-	       ifr->ifr_name, cmd);
-	printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
-	       dev->name, descr->name, descr->set_args, descr->get_args);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-	/* Compute the size of the set arguments */
-	/* Check for sub-ioctl handler */
-	if(descr->name[0] == '\0')
-		/* Reserve one int for sub-ioctl index */
-		offset = sizeof(__u32);
-
-	/* Size of set arguments */
-	extra_size = get_priv_size(descr->set_args);
-
-	/* Does it fits in wrqu ? */
-	if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
-	   (extra_size <= IFNAMSIZ)) {
-		hdr_len = IW_EV_LCP_LEN + extra_size;
-		extra_size = 0;
-	} else {
-		hdr_len = IW_EV_POINT_LEN;
-	}
-
-	/* Extract fixed header from request. This is properly aligned. */
-	wrqu = &request->u;
-
-	/* Check if wrqu is complete */
-	if(request_len < hdr_len) {
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG
-		       "%s (WE.r) : Wireless request too short (%d)\n",
-		       dev->name, request_len);
-#endif	/* WE_RTNETLINK_DEBUG */
-		return -EINVAL;
-	}
-
-	/* Prepare the call */
-	info.cmd = cmd;
-	info.flags = 0;
-
-	/* Check if we have a pointer to user space data or not. */
-	if(extra_size == 0) {
-
-		/* No extra arguments. Trivial to handle */
-		ret = handler(dev, &info, wrqu, (char *) wrqu);
-
-	} else {
-		int	extra_len;
-
-		/* Put wrqu in the right place (skip pointer) */
-		memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
-		       wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
-
-		/* Does it fits within bounds ? */
-		if(wrqu_point.data.length > (descr->set_args &
-					     IW_PRIV_SIZE_MASK))
-			return -E2BIG;
-
-		/* Real length of payload */
-		extra_len = adjust_priv_size(descr->set_args, &wrqu_point);
-
-		/* Check if request is self consistent */
-		if((request_len - hdr_len) < extra_len) {
-#ifdef WE_RTNETLINK_DEBUG
-			printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
-			       dev->name, extra_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-			return -EINVAL;
-		}
-
-#ifdef WE_RTNETLINK_DEBUG
-		printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
-		       dev->name, extra_size);
-#endif	/* WE_RTNETLINK_DEBUG */
-
-		/* Always allocate for max space. Easier, and won't last
-		 * long... */
-		extra = kmalloc(extra_size, GFP_KERNEL);
-		if (extra == NULL)
-			return -ENOMEM;
-
-		/* Copy extra in aligned buffer */
-		memcpy(extra, ((char *) request) + hdr_len, extra_len);
-
-		/* Call the handler */
-		ret = handler(dev, &info, &wrqu_point, extra);
-
-		/* Cleanup - I told you it wasn't that long ;-) */
-		kfree(extra);
-	}
-
-	/* Call commit handler if needed and defined */
-	if(ret == -EIWCOMMIT)
-		ret = call_commit_handler(dev);
-
-	return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Main RtNetlink dispatcher. Called from the main networking code
- * (do_getlink() in net/core/rtnetlink.c).
- * Check the type of Request and call the appropriate wrapper...
- */
-int wireless_rtnetlink_get(struct net_device *	dev,
-			   char *		data,
-			   int			len,
-			   char **		p_buf,
-			   int *		p_len)
-{
-	struct iw_event *	request = (struct iw_event *) data;
-	iw_handler		handler;
-
-	/* Check length */
-	if(len < IW_EV_LCP_LEN) {
-		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
-		       dev->name, len);
-		return -EINVAL;
-	}
-
-	/* ReCheck length (len may have padding) */
-	if(request->len > len) {
-		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
-		       dev->name, request->len, len);
-		return -EINVAL;
-	}
-
-	/* Only accept GET requests in here */
-	if(!IW_IS_GET(request->cmd))
-		return -EOPNOTSUPP;
-
-	/* If command is `get the encoding parameters', check if
-	 * the user has the right to do it */
-	if (request->cmd == SIOCGIWENCODE ||
-	    request->cmd == SIOCGIWENCODEEXT) {
-		if (!capable(CAP_NET_ADMIN))
-			return -EPERM;
-	}
-
-	/* Special cases */
-	if(request->cmd == SIOCGIWSTATS)
-		/* Get Wireless Stats */
-		return rtnetlink_standard_get(dev,
-					      request,
-					      request->len,
-					      &iw_handler_get_iwstats,
-					      p_buf, p_len);
-	if(request->cmd == SIOCGIWPRIV) {
-		/* Check if we have some wireless handlers defined */
-		if(dev->wireless_handlers == NULL)
-			return -EOPNOTSUPP;
-		/* Get Wireless Stats */
-		return rtnetlink_standard_get(dev,
-					      request,
-					      request->len,
-					      &iw_handler_get_private,
-					      p_buf, p_len);
-	}
-
-	/* Basic check */
-	if (!netif_device_present(dev))
-		return -ENODEV;
-
-	/* Try to find the handler */
-	handler = get_handler(dev, request->cmd);
-	if(handler != NULL) {
-		/* Standard and private are not the same */
-		if(request->cmd < SIOCIWFIRSTPRIV)
-			return rtnetlink_standard_get(dev,
-						      request,
-						      request->len,
-						      handler,
-						      p_buf, p_len);
-		else
-			return rtnetlink_private_get(dev,
-						     request,
-						     request->len,
-						     handler,
-						     p_buf, p_len);
-	}
-
-	return -EOPNOTSUPP;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Main RtNetlink dispatcher. Called from the main networking code
- * (do_setlink() in net/core/rtnetlink.c).
- * Check the type of Request and call the appropriate wrapper...
- */
-int wireless_rtnetlink_set(struct net_device *	dev,
-			   char *		data,
-			   int			len)
-{
-	struct iw_event *	request = (struct iw_event *) data;
-	iw_handler		handler;
-
-	/* Check length */
-	if(len < IW_EV_LCP_LEN) {
-		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
-		       dev->name, len);
-		return -EINVAL;
-	}
-
-	/* ReCheck length (len may have padding) */
-	if(request->len > len) {
-		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
-		       dev->name, request->len, len);
-		return -EINVAL;
-	}
-
-	/* Only accept SET requests in here */
-	if(!IW_IS_SET(request->cmd))
-		return -EOPNOTSUPP;
-
-	/* Basic check */
-	if (!netif_device_present(dev))
-		return -ENODEV;
-
-	/* New driver API : try to find the handler */
-	handler = get_handler(dev, request->cmd);
-	if(handler != NULL) {
-		/* Standard and private are not the same */
-		if(request->cmd < SIOCIWFIRSTPRIV)
-			return rtnetlink_standard_set(dev,
-						      request,
-						      request->len,
-						      handler);
-		else
-			return rtnetlink_private_set(dev,
-						     request,
-						     request->len,
-						     handler);
-	}
-
-	return -EOPNOTSUPP;
-}
-#endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
-
-
-/************************* EVENT PROCESSING *************************/
-/*
- * Process events generated by the wireless layer or the driver.
- * Most often, the event will be propagated through rtnetlink
- */
-
-#ifdef WE_EVENT_RTNETLINK
-/* ---------------------------------------------------------------- */
-/*
- * Locking...
- * ----------
- *
- * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
- * the locking issue in here and implementing this code !
- *
- * The issue : wireless_send_event() is often called in interrupt context,
- * while the Netlink layer can never be called in interrupt context.
- * The fully formed RtNetlink events are queued, and then a tasklet is run
- * to feed those to Netlink.
- * The skb_queue is interrupt safe, and its lock is not held while calling
- * Netlink, so there is no possibility of dealock.
- * Jean II
- */
-
-static struct sk_buff_head wireless_nlevent_queue;
-
-static int __init wireless_nlevent_init(void)
-{
-	skb_queue_head_init(&wireless_nlevent_queue);
-	return 0;
-}
-
-subsys_initcall(wireless_nlevent_init);
-
-static void wireless_nlevent_process(unsigned long data)
-{
-	struct sk_buff *skb;
-
-	while ((skb = skb_dequeue(&wireless_nlevent_queue)))
-		rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
-}
-
-static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
-
-/* ---------------------------------------------------------------- */
-/*
- * Fill a rtnetlink message with our event data.
- * Note that we propage only the specified event and don't dump the
- * current wireless config. Dumping the wireless config is far too
- * expensive (for each parameter, the driver need to query the hardware).
- */
-static inline int rtnetlink_fill_iwinfo(struct sk_buff *	skb,
-					struct net_device *	dev,
-					int			type,
-					char *			event,
-					int			event_len)
-{
-	struct ifinfomsg *r;
-	struct nlmsghdr  *nlh;
-	unsigned char	 *b = skb->tail;
-
-	nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
-	r = NLMSG_DATA(nlh);
-	r->ifi_family = AF_UNSPEC;
-	r->__ifi_pad = 0;
-	r->ifi_type = dev->type;
-	r->ifi_index = dev->ifindex;
-	r->ifi_flags = dev_get_flags(dev);
-	r->ifi_change = 0;	/* Wireless changes don't affect those flags */
-
-	/* Add the wireless events in the netlink packet */
-	RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
-
-	nlh->nlmsg_len = skb->tail - b;
-	return skb->len;
-
-nlmsg_failure:
-rtattr_failure:
-	skb_trim(skb, b - skb->data);
-	return -1;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Create and broadcast and send it on the standard rtnetlink socket
- * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c
- * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
- * within a RTM_NEWLINK event.
- */
-static inline void rtmsg_iwinfo(struct net_device *	dev,
-				char *			event,
-				int			event_len)
-{
-	struct sk_buff *skb;
-	int size = NLMSG_GOODSIZE;
-
-	skb = alloc_skb(size, GFP_ATOMIC);
-	if (!skb)
-		return;
-
-	if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
-				  event, event_len) < 0) {
-		kfree_skb(skb);
-		return;
-	}
-	NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
-	skb_queue_tail(&wireless_nlevent_queue, skb);
-	tasklet_schedule(&wireless_nlevent_tasklet);
-}
-
-#endif	/* WE_EVENT_RTNETLINK */
-
-/* ---------------------------------------------------------------- */
-/*
- * Main event dispatcher. Called from other parts and drivers.
- * Send the event on the appropriate channels.
- * May be called from interrupt context.
- */
-void wireless_send_event(struct net_device *	dev,
-			 unsigned int		cmd,
-			 union iwreq_data *	wrqu,
-			 char *			extra)
-{
-	const struct iw_ioctl_description *	descr = NULL;
-	int extra_len = 0;
-	struct iw_event  *event;		/* Mallocated whole event */
-	int event_len;				/* Its size */
-	int hdr_len;				/* Size of the event header */
-	int wrqu_off = 0;			/* Offset in wrqu */
-	/* Don't "optimise" the following variable, it will crash */
-	unsigned	cmd_index;		/* *MUST* be unsigned */
-
-	/* Get the description of the Event */
-	if(cmd <= SIOCIWLAST) {
-		cmd_index = cmd - SIOCIWFIRST;
-		if(cmd_index < standard_ioctl_num)
-			descr = &(standard_ioctl[cmd_index]);
-	} else {
-		cmd_index = cmd - IWEVFIRST;
-		if(cmd_index < standard_event_num)
-			descr = &(standard_event[cmd_index]);
-	}
-	/* Don't accept unknown events */
-	if(descr == NULL) {
-		/* Note : we don't return an error to the driver, because
-		 * the driver would not know what to do about it. It can't
-		 * return an error to the user, because the event is not
-		 * initiated by a user request.
-		 * The best the driver could do is to log an error message.
-		 * We will do it ourselves instead...
-		 */
-	  	printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
-		       dev->name, cmd);
-		return;
-	}
-#ifdef WE_EVENT_DEBUG
-	printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n",
-	       dev->name, cmd);
-	printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif	/* WE_EVENT_DEBUG */
-
-	/* Check extra parameters and set extra_len */
-	if(descr->header_type == IW_HEADER_TYPE_POINT) {
-		/* Check if number of token fits within bounds */
-		if(wrqu->data.length > descr->max_tokens) {
-		  	printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
-			return;
-		}
-		if(wrqu->data.length < descr->min_tokens) {
-		  	printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
-			return;
-		}
-		/* Calculate extra_len - extra is NULL for restricted events */
-		if(extra != NULL)
-			extra_len = wrqu->data.length * descr->token_size;
-		/* Always at an offset in wrqu */
-		wrqu_off = IW_EV_POINT_OFF;
-#ifdef WE_EVENT_DEBUG
-		printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len);
-#endif	/* WE_EVENT_DEBUG */
-	}
-
-	/* Total length of the event */
-	hdr_len = event_type_size[descr->header_type];
-	event_len = hdr_len + extra_len;
-
-#ifdef WE_EVENT_DEBUG
-	printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, wrqu_off %d, event_len %d\n", dev->name, cmd, hdr_len, wrqu_off, event_len);
-#endif	/* WE_EVENT_DEBUG */
-
-	/* Create temporary buffer to hold the event */
-	event = kmalloc(event_len, GFP_ATOMIC);
-	if(event == NULL)
-		return;
-
-	/* Fill event */
-	event->len = event_len;
-	event->cmd = cmd;
-	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
-	if(extra != NULL)
-		memcpy(((char *) event) + hdr_len, extra, extra_len);
-
-#ifdef WE_EVENT_RTNETLINK
-	/* Send via the RtNetlink event channel */
-	rtmsg_iwinfo(dev, (char *) event, event_len);
-#endif	/* WE_EVENT_RTNETLINK */
-
-	/* Cleanup */
-	kfree(event);
-
-	return;		/* Always success, I guess ;-) */
-}
-
-/********************** ENHANCED IWSPY SUPPORT **********************/
-/*
- * In the old days, the driver was handling spy support all by itself.
- * Now, the driver can delegate this task to Wireless Extensions.
- * It needs to use those standard spy iw_handler in struct iw_handler_def,
- * push data to us via wireless_spy_update() and include struct iw_spy_data
- * in its private part (and export it in net_device->wireless_data->spy_data).
- * One of the main advantage of centralising spy support here is that
- * it becomes much easier to improve and extend it without having to touch
- * the drivers. One example is the addition of the Spy-Threshold events.
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * Return the pointer to the spy data in the driver.
- * Because this is called on the Rx path via wireless_spy_update(),
- * we want it to be efficient...
- */
-static inline struct iw_spy_data * get_spydata(struct net_device *dev)
-{
-	/* This is the new way */
-	if(dev->wireless_data)
-		return(dev->wireless_data->spy_data);
-	return NULL;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : set Spy List
- */
-int iw_handler_set_spy(struct net_device *	dev,
-		       struct iw_request_info *	info,
-		       union iwreq_data *	wrqu,
-		       char *			extra)
-{
-	struct iw_spy_data *	spydata = get_spydata(dev);
-	struct sockaddr *	address = (struct sockaddr *) extra;
-
-	/* Make sure driver is not buggy or using the old API */
-	if(!spydata)
-		return -EOPNOTSUPP;
-
-	/* Disable spy collection while we copy the addresses.
-	 * While we copy addresses, any call to wireless_spy_update()
-	 * will NOP. This is OK, as anyway the addresses are changing. */
-	spydata->spy_number = 0;
-
-	/* We want to operate without locking, because wireless_spy_update()
-	 * most likely will happen in the interrupt handler, and therefore
-	 * have its own locking constraints and needs performance.
-	 * The rtnl_lock() make sure we don't race with the other iw_handlers.
-	 * This make sure wireless_spy_update() "see" that the spy list
-	 * is temporarily disabled. */
-	smp_wmb();
-
-	/* Are there are addresses to copy? */
-	if(wrqu->data.length > 0) {
-		int i;
-
-		/* Copy addresses */
-		for(i = 0; i < wrqu->data.length; i++)
-			memcpy(spydata->spy_address[i], address[i].sa_data,
-			       ETH_ALEN);
-		/* Reset stats */
-		memset(spydata->spy_stat, 0,
-		       sizeof(struct iw_quality) * IW_MAX_SPY);
-
-#ifdef WE_SPY_DEBUG
-		printk(KERN_DEBUG "iw_handler_set_spy() :  wireless_data %p, spydata %p, num %d\n", dev->wireless_data, spydata, wrqu->data.length);
-		for (i = 0; i < wrqu->data.length; i++)
-			printk(KERN_DEBUG
-			       "%02X:%02X:%02X:%02X:%02X:%02X \n",
-			       spydata->spy_address[i][0],
-			       spydata->spy_address[i][1],
-			       spydata->spy_address[i][2],
-			       spydata->spy_address[i][3],
-			       spydata->spy_address[i][4],
-			       spydata->spy_address[i][5]);
-#endif	/* WE_SPY_DEBUG */
-	}
-
-	/* Make sure above is updated before re-enabling */
-	smp_wmb();
-
-	/* Enable addresses */
-	spydata->spy_number = wrqu->data.length;
-
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : get Spy List
- */
-int iw_handler_get_spy(struct net_device *	dev,
-		       struct iw_request_info *	info,
-		       union iwreq_data *	wrqu,
-		       char *			extra)
-{
-	struct iw_spy_data *	spydata = get_spydata(dev);
-	struct sockaddr *	address = (struct sockaddr *) extra;
-	int			i;
-
-	/* Make sure driver is not buggy or using the old API */
-	if(!spydata)
-		return -EOPNOTSUPP;
-
-	wrqu->data.length = spydata->spy_number;
-
-	/* Copy addresses. */
-	for(i = 0; i < spydata->spy_number; i++) 	{
-		memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
-		address[i].sa_family = AF_UNIX;
-	}
-	/* Copy stats to the user buffer (just after). */
-	if(spydata->spy_number > 0)
-		memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
-		       spydata->spy_stat,
-		       sizeof(struct iw_quality) * spydata->spy_number);
-	/* Reset updated flags. */
-	for(i = 0; i < spydata->spy_number; i++)
-		spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : set spy threshold
- */
-int iw_handler_set_thrspy(struct net_device *	dev,
-			  struct iw_request_info *info,
-			  union iwreq_data *	wrqu,
-			  char *		extra)
-{
-	struct iw_spy_data *	spydata = get_spydata(dev);
-	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
-
-	/* Make sure driver is not buggy or using the old API */
-	if(!spydata)
-		return -EOPNOTSUPP;
-
-	/* Just do it */
-	memcpy(&(spydata->spy_thr_low), &(threshold->low),
-	       2 * sizeof(struct iw_quality));
-
-	/* Clear flag */
-	memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
-
-#ifdef WE_SPY_DEBUG
-	printk(KERN_DEBUG "iw_handler_set_thrspy() :  low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level);
-#endif	/* WE_SPY_DEBUG */
-
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : get spy threshold
- */
-int iw_handler_get_thrspy(struct net_device *	dev,
-			  struct iw_request_info *info,
-			  union iwreq_data *	wrqu,
-			  char *		extra)
-{
-	struct iw_spy_data *	spydata = get_spydata(dev);
-	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
-
-	/* Make sure driver is not buggy or using the old API */
-	if(!spydata)
-		return -EOPNOTSUPP;
-
-	/* Just do it */
-	memcpy(&(threshold->low), &(spydata->spy_thr_low),
-	       2 * sizeof(struct iw_quality));
-
-	return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Prepare and send a Spy Threshold event
- */
-static void iw_send_thrspy_event(struct net_device *	dev,
-				 struct iw_spy_data *	spydata,
-				 unsigned char *	address,
-				 struct iw_quality *	wstats)
-{
-	union iwreq_data	wrqu;
-	struct iw_thrspy	threshold;
-
-	/* Init */
-	wrqu.data.length = 1;
-	wrqu.data.flags = 0;
-	/* Copy address */
-	memcpy(threshold.addr.sa_data, address, ETH_ALEN);
-	threshold.addr.sa_family = ARPHRD_ETHER;
-	/* Copy stats */
-	memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
-	/* Copy also thresholds */
-	memcpy(&(threshold.low), &(spydata->spy_thr_low),
-	       2 * sizeof(struct iw_quality));
-
-#ifdef WE_SPY_DEBUG
-	printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n",
-	       threshold.addr.sa_data[0],
-	       threshold.addr.sa_data[1],
-	       threshold.addr.sa_data[2],
-	       threshold.addr.sa_data[3],
-	       threshold.addr.sa_data[4],
-	       threshold.addr.sa_data[5], threshold.qual.level);
-#endif	/* WE_SPY_DEBUG */
-
-	/* Send event to user space */
-	wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Call for the driver to update the spy data.
- * For now, the spy data is a simple array. As the size of the array is
- * small, this is good enough. If we wanted to support larger number of
- * spy addresses, we should use something more efficient...
- */
-void wireless_spy_update(struct net_device *	dev,
-			 unsigned char *	address,
-			 struct iw_quality *	wstats)
-{
-	struct iw_spy_data *	spydata = get_spydata(dev);
-	int			i;
-	int			match = -1;
-
-	/* Make sure driver is not buggy or using the old API */
-	if(!spydata)
-		return;
-
-#ifdef WE_SPY_DEBUG
-	printk(KERN_DEBUG "wireless_spy_update() :  wireless_data %p, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_data, spydata, address[0], address[1], address[2], address[3], address[4], address[5]);
-#endif	/* WE_SPY_DEBUG */
-
-	/* Update all records that match */
-	for(i = 0; i < spydata->spy_number; i++)
-		if(!compare_ether_addr(address, spydata->spy_address[i])) {
-			memcpy(&(spydata->spy_stat[i]), wstats,
-			       sizeof(struct iw_quality));
-			match = i;
-		}
-
-	/* Generate an event if we cross the spy threshold.
-	 * To avoid event storms, we have a simple hysteresis : we generate
-	 * event only when we go under the low threshold or above the
-	 * high threshold. */
-	if(match >= 0) {
-		if(spydata->spy_thr_under[match]) {
-			if(wstats->level > spydata->spy_thr_high.level) {
-				spydata->spy_thr_under[match] = 0;
-				iw_send_thrspy_event(dev, spydata,
-						     address, wstats);
-			}
-		} else {
-			if(wstats->level < spydata->spy_thr_low.level) {
-				spydata->spy_thr_under[match] = 1;
-				iw_send_thrspy_event(dev, spydata,
-						     address, wstats);
-			}
-		}
-	}
-}
-
-EXPORT_SYMBOL(iw_handler_get_spy);
-EXPORT_SYMBOL(iw_handler_get_thrspy);
-EXPORT_SYMBOL(iw_handler_set_spy);
-EXPORT_SYMBOL(iw_handler_set_thrspy);
-EXPORT_SYMBOL(wireless_send_event);
-EXPORT_SYMBOL(wireless_spy_update);
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index c030b12..b039edd 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -2,3 +2,5 @@ obj-$(CONFIG_CFG80211) += cfg80211.o
 
 cfg80211-objs := \
 	core.o nl80211.o
+
+obj-$(CONFIG_WIRELESS_EXT) += wext-old.o
diff --git a/net/wireless/wext-old.c b/net/wireless/wext-old.c
new file mode 100644
index 0000000..f69ab7b
--- /dev/null
+++ b/net/wireless/wext-old.c
@@ -0,0 +1,2353 @@
+/*
+ * This file implement the Wireless Extensions APIs.
+ *
+ * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved.
+ *
+ * (As all part of the Linux kernel, this file is GPL)
+ */
+
+/************************** DOCUMENTATION **************************/
+/*
+ * API definition :
+ * --------------
+ * See <linux/wireless.h> for details of the APIs and the rest.
+ *
+ * History :
+ * -------
+ *
+ * v1 - 5.12.01 - Jean II
+ *	o Created this file.
+ *
+ * v2 - 13.12.01 - Jean II
+ *	o Move /proc/net/wireless stuff from net/core/dev.c to here
+ *	o Make Wireless Extension IOCTLs go through here
+ *	o Added iw_handler handling ;-)
+ *	o Added standard ioctl description
+ *	o Initial dumb commit strategy based on orinoco.c
+ *
+ * v3 - 19.12.01 - Jean II
+ *	o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call
+ *	o Add event dispatcher function
+ *	o Add event description
+ *	o Propagate events as rtnetlink IFLA_WIRELESS option
+ *	o Generate event on selected SET requests
+ *
+ * v4 - 18.04.02 - Jean II
+ *	o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1
+ *
+ * v5 - 21.06.02 - Jean II
+ *	o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup)
+ *	o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
+ *	o Add IWEVCUSTOM for driver specific event/scanning token
+ *	o Turn on WE_STRICT_WRITE by default + kernel warning
+ *	o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num)
+ *	o Fix off-by-one in test (extra_size <= IFNAMSIZ)
+ *
+ * v6 - 9.01.03 - Jean II
+ *	o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
+ *	o Add enhanced spy support : iw_handler_set_thrspy() and event.
+ *	o Add WIRELESS_EXT version display in /proc/net/wireless
+ *
+ * v6 - 18.06.04 - Jean II
+ *	o Change get_spydata() method for added safety
+ *	o Remove spy #ifdef, they are always on -> cleaner code
+ *	o Allow any size GET request if user specifies length > max
+ *		and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV
+ *	o Start migrating get_wireless_stats to struct iw_handler_def
+ *	o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus
+ * Based on patch from Pavel Roskin <proski@gnu.org> :
+ *	o Fix kernel data leak to user space in private handler handling
+ *
+ * v7 - 18.3.05 - Jean II
+ *	o Remove (struct iw_point *)->pointer from events and streams
+ *	o Remove spy_offset from struct iw_handler_def
+ *	o Start deprecating dev->get_wireless_stats, output a warning
+ *	o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
+ *	o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
+ *
+ * v8 - 17.02.06 - Jean II
+ *	o RtNetlink requests support (SET/GET)
+ *
+ * v8b - 03.08.06 - Herbert Xu
+ *	o Fix Wireless Event locking issues.
+ *
+ * v9 - 14.3.06 - Jean II
+ *	o Change length in ESSID and NICK to strlen() instead of strlen()+1
+ *	o Make standard_ioctl_num and standard_event_num unsigned
+ *	o Remove (struct net_device *)->get_wireless_stats()
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/module.h>
+#include <linux/types.h>		/* off_t */
+#include <linux/netdevice.h>		/* struct ifreq, dev_get_by_name() */
+#include <linux/proc_fs.h>
+#include <linux/rtnetlink.h>		/* rtnetlink stuff */
+#include <linux/seq_file.h>
+#include <linux/init.h>			/* for __init */
+#include <linux/if_arp.h>		/* ARPHRD_ETHER */
+#include <linux/etherdevice.h>		/* compare_ether_addr */
+#include <linux/interrupt.h>
+
+#include <linux/wireless.h>		/* Pretty obvious */
+#include <net/iw_handler.h>		/* New driver API */
+#include <net/netlink.h>
+
+#include <asm/uaccess.h>		/* copy_to_user() */
+
+/**************************** CONSTANTS ****************************/
+
+/* Debugging stuff */
+#undef WE_IOCTL_DEBUG		/* Debug IOCTL API */
+#undef WE_RTNETLINK_DEBUG	/* Debug RtNetlink API */
+#undef WE_EVENT_DEBUG		/* Debug Event dispatcher */
+#undef WE_SPY_DEBUG		/* Debug enhanced spy support */
+
+/* Options */
+//CONFIG_NET_WIRELESS_RTNETLINK	/* Wireless requests over RtNetlink */
+#define WE_EVENT_RTNETLINK	/* Propagate events using RtNetlink */
+#define WE_SET_EVENT		/* Generate an event on some set commands */
+
+/************************* GLOBAL VARIABLES *************************/
+/*
+ * You should not use global variables, because of re-entrancy.
+ * On our case, it's only const, so it's OK...
+ */
+/*
+ * Meta-data about all the standard Wireless Extension request we
+ * know about.
+ */
+static const struct iw_ioctl_description standard_ioctl[] = {
+	[SIOCSIWCOMMIT	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWNAME	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_CHAR,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWNWID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWNWID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWFREQ	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_FREQ,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWFREQ	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_FREQ,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWMODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_UINT,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWMODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_UINT,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWSENS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWSENS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWRANGE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWRANGE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_range),
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWPRIV	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWPRIV	- SIOCIWFIRST] = { /* (handled directly by us) */
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct iw_priv_args),
+		.max_tokens	= 16,
+		.flags		= IW_DESCR_FLAG_NOMAX,
+	},
+	[SIOCSIWSTATS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWSTATS	- SIOCIWFIRST] = { /* (handled directly by us) */
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_statistics),
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct sockaddr),
+		.max_tokens	= IW_MAX_SPY,
+	},
+	[SIOCGIWSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct sockaddr) +
+				  sizeof(struct iw_quality),
+		.max_tokens	= IW_MAX_SPY,
+	},
+	[SIOCSIWTHRSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct iw_thrspy),
+		.min_tokens	= 1,
+		.max_tokens	= 1,
+	},
+	[SIOCGIWTHRSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct iw_thrspy),
+		.min_tokens	= 1,
+		.max_tokens	= 1,
+	},
+	[SIOCSIWAP	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[SIOCGIWAP	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWMLME	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_mlme),
+		.max_tokens	= sizeof(struct iw_mlme),
+	},
+	[SIOCGIWAPLIST	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct sockaddr) +
+				  sizeof(struct iw_quality),
+		.max_tokens	= IW_MAX_AP,
+		.flags		= IW_DESCR_FLAG_NOMAX,
+	},
+	[SIOCSIWSCAN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= 0,
+		.max_tokens	= sizeof(struct iw_scan_req),
+	},
+	[SIOCGIWSCAN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_SCAN_MAX_DATA,
+		.flags		= IW_DESCR_FLAG_NOMAX,
+	},
+	[SIOCSIWESSID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWESSID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWNICKN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+	},
+	[SIOCGIWNICKN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+	},
+	[SIOCSIWRATE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWRATE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWRTS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWRTS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWFRAG	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWFRAG	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWTXPOW	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWTXPOW	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWRETRY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWRETRY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWENCODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ENCODING_TOKEN_MAX,
+		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
+	},
+	[SIOCGIWENCODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ENCODING_TOKEN_MAX,
+		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
+	},
+	[SIOCSIWPOWER	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWPOWER	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWGENIE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[SIOCGIWGENIE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[SIOCSIWAUTH	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWAUTH	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWENCODEEXT - SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_encode_ext),
+		.max_tokens	= sizeof(struct iw_encode_ext) +
+				  IW_ENCODING_TOKEN_MAX,
+	},
+	[SIOCGIWENCODEEXT - SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_encode_ext),
+		.max_tokens	= sizeof(struct iw_encode_ext) +
+				  IW_ENCODING_TOKEN_MAX,
+	},
+	[SIOCSIWPMKSA - SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_pmksa),
+		.max_tokens	= sizeof(struct iw_pmksa),
+	},
+};
+static const unsigned standard_ioctl_num = (sizeof(standard_ioctl) /
+					    sizeof(struct iw_ioctl_description));
+
+/*
+ * Meta-data about all the additional standard Wireless Extension events
+ * we know about.
+ */
+static const struct iw_ioctl_description standard_event[] = {
+	[IWEVTXDROP	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[IWEVQUAL	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_QUAL,
+	},
+	[IWEVCUSTOM	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_CUSTOM_MAX,
+	},
+	[IWEVREGISTERED	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[IWEVEXPIRED	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR, 
+	},
+	[IWEVGENIE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[IWEVMICHAELMICFAILURE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT, 
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_michaelmicfailure),
+	},
+	[IWEVASSOCREQIE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[IWEVASSOCRESPIE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[IWEVPMKIDCAND	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_pmkid_cand),
+	},
+};
+static const unsigned standard_event_num = (sizeof(standard_event) /
+					    sizeof(struct iw_ioctl_description));
+
+/* Size (in bytes) of the various private data types */
+static const char iw_priv_type_size[] = {
+	0,				/* IW_PRIV_TYPE_NONE */
+	1,				/* IW_PRIV_TYPE_BYTE */
+	1,				/* IW_PRIV_TYPE_CHAR */
+	0,				/* Not defined */
+	sizeof(__u32),			/* IW_PRIV_TYPE_INT */
+	sizeof(struct iw_freq),		/* IW_PRIV_TYPE_FLOAT */
+	sizeof(struct sockaddr),	/* IW_PRIV_TYPE_ADDR */
+	0,				/* Not defined */
+};
+
+/* Size (in bytes) of various events */
+static const int event_type_size[] = {
+	IW_EV_LCP_LEN,			/* IW_HEADER_TYPE_NULL */
+	0,
+	IW_EV_CHAR_LEN,			/* IW_HEADER_TYPE_CHAR */
+	0,
+	IW_EV_UINT_LEN,			/* IW_HEADER_TYPE_UINT */
+	IW_EV_FREQ_LEN,			/* IW_HEADER_TYPE_FREQ */
+	IW_EV_ADDR_LEN,			/* IW_HEADER_TYPE_ADDR */
+	0,
+	IW_EV_POINT_LEN,		/* Without variable payload */
+	IW_EV_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */
+	IW_EV_QUAL_LEN,			/* IW_HEADER_TYPE_QUAL */
+};
+
+/************************ COMMON SUBROUTINES ************************/
+/*
+ * Stuff that may be used in various place or doesn't fit in one
+ * of the section below.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Return the driver handler associated with a specific Wireless Extension.
+ * Called from various place, so make sure it remains efficient.
+ */
+static inline iw_handler get_handler(struct net_device *dev,
+				     unsigned int cmd)
+{
+	/* Don't "optimise" the following variable, it will crash */
+	unsigned int	index;		/* *MUST* be unsigned */
+
+	/* Check if we have some wireless handlers defined */
+	if(dev->wireless_handlers == NULL)
+		return NULL;
+
+	/* Try as a standard command */
+	index = cmd - SIOCIWFIRST;
+	if(index < dev->wireless_handlers->num_standard)
+		return dev->wireless_handlers->standard[index];
+
+	/* Try as a private command */
+	index = cmd - SIOCIWFIRSTPRIV;
+	if(index < dev->wireless_handlers->num_private)
+		return dev->wireless_handlers->private[index];
+
+	/* Not found */
+	return NULL;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Get statistics out of the driver
+ */
+static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
+{
+	/* New location */
+	if((dev->wireless_handlers != NULL) &&
+	   (dev->wireless_handlers->get_wireless_stats != NULL))
+		return dev->wireless_handlers->get_wireless_stats(dev);
+
+	/* Not found */
+	return (struct iw_statistics *) NULL;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Call the commit handler in the driver
+ * (if exist and if conditions are right)
+ *
+ * Note : our current commit strategy is currently pretty dumb,
+ * but we will be able to improve on that...
+ * The goal is to try to agreagate as many changes as possible
+ * before doing the commit. Drivers that will define a commit handler
+ * are usually those that need a reset after changing parameters, so
+ * we want to minimise the number of reset.
+ * A cool idea is to use a timer : at each "set" command, we re-set the
+ * timer, when the timer eventually fires, we call the driver.
+ * Hopefully, more on that later.
+ *
+ * Also, I'm waiting to see how many people will complain about the
+ * netif_running(dev) test. I'm open on that one...
+ * Hopefully, the driver will remember to do a commit in "open()" ;-)
+ */
+static inline int call_commit_handler(struct net_device *	dev)
+{
+	if((netif_running(dev)) &&
+	   (dev->wireless_handlers->standard[0] != NULL)) {
+		/* Call the commit handler on the driver */
+		return dev->wireless_handlers->standard[0](dev, NULL,
+							   NULL, NULL);
+	} else
+		return 0;		/* Command completed successfully */
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Calculate size of private arguments
+ */
+static inline int get_priv_size(__u16	args)
+{
+	int	num = args & IW_PRIV_SIZE_MASK;
+	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+	return num * iw_priv_type_size[type];
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Re-calculate the size of private arguments
+ */
+static inline int adjust_priv_size(__u16		args,
+				   union iwreq_data *	wrqu)
+{
+	int	num = wrqu->data.length;
+	int	max = args & IW_PRIV_SIZE_MASK;
+	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+	/* Make sure the driver doesn't goof up */
+	if (max < num)
+		num = max;
+
+	return num * iw_priv_type_size[type];
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Standard Wireless Handler : get wireless stats
+ *	Allow programatic access to /proc/net/wireless even if /proc
+ *	doesn't exist... Also more efficient...
+ */
+static int iw_handler_get_iwstats(struct net_device *		dev,
+				  struct iw_request_info *	info,
+				  union iwreq_data *		wrqu,
+				  char *			extra)
+{
+	/* Get stats from the driver */
+	struct iw_statistics *stats;
+
+	stats = get_wireless_stats(dev);
+	if (stats != (struct iw_statistics *) NULL) {
+
+		/* Copy statistics to extra */
+		memcpy(extra, stats, sizeof(struct iw_statistics));
+		wrqu->data.length = sizeof(struct iw_statistics);
+
+		/* Check if we need to clear the updated flag */
+		if(wrqu->data.flags != 0)
+			stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+		return 0;
+	} else
+		return -EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Standard Wireless Handler : get iwpriv definitions
+ * Export the driver private handler definition
+ * They will be picked up by tools like iwpriv...
+ */
+static int iw_handler_get_private(struct net_device *		dev,
+				  struct iw_request_info *	info,
+				  union iwreq_data *		wrqu,
+				  char *			extra)
+{
+	/* Check if the driver has something to export */
+	if((dev->wireless_handlers->num_private_args == 0) ||
+	   (dev->wireless_handlers->private_args == NULL))
+		return -EOPNOTSUPP;
+
+	/* Check if there is enough buffer up there */
+	if(wrqu->data.length < dev->wireless_handlers->num_private_args) {
+		/* User space can't know in advance how large the buffer
+		 * needs to be. Give it a hint, so that we can support
+		 * any size buffer we want somewhat efficiently... */
+		wrqu->data.length = dev->wireless_handlers->num_private_args;
+		return -E2BIG;
+	}
+
+	/* Set the number of available ioctls. */
+	wrqu->data.length = dev->wireless_handlers->num_private_args;
+
+	/* Copy structure to the user buffer. */
+	memcpy(extra, dev->wireless_handlers->private_args,
+	       sizeof(struct iw_priv_args) * wrqu->data.length);
+
+	return 0;
+}
+
+
+/******************** /proc/net/wireless SUPPORT ********************/
+/*
+ * The /proc/net/wireless file is a human readable user-space interface
+ * exporting various wireless specific statistics from the wireless devices.
+ * This is the most popular part of the Wireless Extensions ;-)
+ *
+ * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
+ * The content of the file is basically the content of "struct iw_statistics".
+ */
+
+#ifdef CONFIG_PROC_FS
+
+/* ---------------------------------------------------------------- */
+/*
+ * Print one entry (line) of /proc/net/wireless
+ */
+static __inline__ void wireless_seq_printf_stats(struct seq_file *seq,
+						 struct net_device *dev)
+{
+	/* Get stats from the driver */
+	struct iw_statistics *stats = get_wireless_stats(dev);
+
+	if (stats) {
+		seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
+				"%6d %6d   %6d\n",
+			   dev->name, stats->status, stats->qual.qual,
+			   stats->qual.updated & IW_QUAL_QUAL_UPDATED
+			   ? '.' : ' ',
+			   ((__s32) stats->qual.level) - 
+			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+			   stats->qual.updated & IW_QUAL_LEVEL_UPDATED
+			   ? '.' : ' ',
+			   ((__s32) stats->qual.noise) - 
+			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+			   stats->qual.updated & IW_QUAL_NOISE_UPDATED
+			   ? '.' : ' ',
+			   stats->discard.nwid, stats->discard.code,
+			   stats->discard.fragment, stats->discard.retries,
+			   stats->discard.misc, stats->miss.beacon);
+		stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+	}
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Print info for /proc/net/wireless (print all entries)
+ */
+static int wireless_seq_show(struct seq_file *seq, void *v)
+{
+	if (v == SEQ_START_TOKEN)
+		seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
+				"packets               | Missed | WE\n"
+				" face | tus | link level noise |  nwid  "
+				"crypt   frag  retry   misc | beacon | %d\n",
+			   WIRELESS_EXT);
+	else
+		wireless_seq_printf_stats(seq, v);
+	return 0;
+}
+
+static struct seq_operations wireless_seq_ops = {
+	.start = dev_seq_start,
+	.next  = dev_seq_next,
+	.stop  = dev_seq_stop,
+	.show  = wireless_seq_show,
+};
+
+static int wireless_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &wireless_seq_ops);
+}
+
+static struct file_operations wireless_seq_fops = {
+	.owner	 = THIS_MODULE,
+	.open    = wireless_seq_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+int __init wireless_proc_init(void)
+{
+	/* Create /proc/net/wireless entry */
+	if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
+		return -ENOMEM;
+
+	return 0;
+}
+#endif	/* CONFIG_PROC_FS */
+
+/************************** IOCTL SUPPORT **************************/
+/*
+ * The original user space API to configure all those Wireless Extensions
+ * is through IOCTLs.
+ * In there, we check if we need to call the new driver API (iw_handler)
+ * or just call the driver ioctl handler.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a standard Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ */
+static int ioctl_standard_call(struct net_device *	dev,
+			       struct ifreq *		ifr,
+			       unsigned int		cmd,
+			       iw_handler		handler)
+{
+	struct iwreq *				iwr = (struct iwreq *) ifr;
+	const struct iw_ioctl_description *	descr;
+	struct iw_request_info			info;
+	int					ret = -EINVAL;
+
+	/* Get the description of the IOCTL */
+	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+		return -EOPNOTSUPP;
+	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+#ifdef WE_IOCTL_DEBUG
+	printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n",
+	       ifr->ifr_name, cmd);
+	printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
+#endif	/* WE_IOCTL_DEBUG */
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have a pointer to user space data or not */
+	if(descr->header_type != IW_HEADER_TYPE_POINT) {
+
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, &(iwr->u), NULL);
+
+#ifdef WE_SET_EVENT
+		/* Generate an event to notify listeners of the change */
+		if((descr->flags & IW_DESCR_FLAG_EVENT) &&
+		   ((ret == 0) || (ret == -EIWCOMMIT)))
+			wireless_send_event(dev, cmd, &(iwr->u), NULL);
+#endif	/* WE_SET_EVENT */
+	} else {
+		char *	extra;
+		int	extra_size;
+		int	user_length = 0;
+		int	err;
+		int	essid_compat = 0;
+
+		/* Calculate space needed by arguments. Always allocate
+		 * for max space. Easier, and won't last long... */
+		extra_size = descr->max_tokens * descr->token_size;
+
+		/* Check need for ESSID compatibility for WE < 21 */
+		switch (cmd) {
+		case SIOCSIWESSID:
+		case SIOCGIWESSID:
+		case SIOCSIWNICKN:
+		case SIOCGIWNICKN:
+			if (iwr->u.data.length == descr->max_tokens + 1)
+				essid_compat = 1;
+			else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+				char essid[IW_ESSID_MAX_SIZE + 1];
+
+				err = copy_from_user(essid, iwr->u.data.pointer,
+						     iwr->u.data.length *
+						     descr->token_size);
+				if (err)
+					return -EFAULT;
+
+				if (essid[iwr->u.data.length - 1] == '\0')
+					essid_compat = 1;
+			}
+			break;
+		default:
+			break;
+		}
+
+		iwr->u.data.length -= essid_compat;
+
+		/* Check what user space is giving us */
+		if(IW_IS_SET(cmd)) {
+			/* Check NULL pointer */
+			if((iwr->u.data.pointer == NULL) &&
+			   (iwr->u.data.length != 0))
+				return -EFAULT;
+			/* Check if number of token fits within bounds */
+			if(iwr->u.data.length > descr->max_tokens)
+				return -E2BIG;
+			if(iwr->u.data.length < descr->min_tokens)
+				return -EINVAL;
+		} else {
+			/* Check NULL pointer */
+			if(iwr->u.data.pointer == NULL)
+				return -EFAULT;
+			/* Save user space buffer size for checking */
+			user_length = iwr->u.data.length;
+
+			/* Don't check if user_length > max to allow forward
+			 * compatibility. The test user_length < min is
+			 * implied by the test at the end. */
+
+			/* Support for very large requests */
+			if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+			   (user_length > descr->max_tokens)) {
+				/* Allow userspace to GET more than max so
+				 * we can support any size GET requests.
+				 * There is still a limit : -ENOMEM. */
+				extra_size = user_length * descr->token_size;
+				/* Note : user_length is originally a __u16,
+				 * and token_size is controlled by us,
+				 * so extra_size won't get negative and
+				 * won't overflow... */
+			}
+		}
+
+#ifdef WE_IOCTL_DEBUG
+		printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
+		       dev->name, extra_size);
+#endif	/* WE_IOCTL_DEBUG */
+
+		/* Create the kernel buffer */
+		/*    kzalloc ensures NULL-termination for essid_compat */
+		extra = kzalloc(extra_size, GFP_KERNEL);
+		if (extra == NULL) {
+			return -ENOMEM;
+		}
+
+		/* If it is a SET, get all the extra data in here */
+		if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+			err = copy_from_user(extra, iwr->u.data.pointer,
+					     iwr->u.data.length *
+					     descr->token_size);
+			if (err) {
+				kfree(extra);
+				return -EFAULT;
+			}
+#ifdef WE_IOCTL_DEBUG
+			printk(KERN_DEBUG "%s (WE) : Got %d bytes\n",
+			       dev->name,
+			       iwr->u.data.length * descr->token_size);
+#endif	/* WE_IOCTL_DEBUG */
+		}
+
+		/* Call the handler */
+		ret = handler(dev, &info, &(iwr->u), extra);
+
+		iwr->u.data.length += essid_compat;
+
+		/* If we have something to return to the user */
+		if (!ret && IW_IS_GET(cmd)) {
+			/* Check if there is enough buffer up there */
+			if(user_length < iwr->u.data.length) {
+				kfree(extra);
+				return -E2BIG;
+			}
+
+			err = copy_to_user(iwr->u.data.pointer, extra,
+					   iwr->u.data.length *
+					   descr->token_size);
+			if (err)
+				ret =  -EFAULT;				   
+#ifdef WE_IOCTL_DEBUG
+			printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n",
+			       dev->name,
+			       iwr->u.data.length * descr->token_size);
+#endif	/* WE_IOCTL_DEBUG */
+		}
+
+#ifdef WE_SET_EVENT
+		/* Generate an event to notify listeners of the change */
+		if((descr->flags & IW_DESCR_FLAG_EVENT) &&
+		   ((ret == 0) || (ret == -EIWCOMMIT))) {
+			if(descr->flags & IW_DESCR_FLAG_RESTRICT)
+				/* If the event is restricted, don't
+				 * export the payload */
+				wireless_send_event(dev, cmd, &(iwr->u), NULL);
+			else
+				wireless_send_event(dev, cmd, &(iwr->u),
+						    extra);
+		}
+#endif	/* WE_SET_EVENT */
+
+		/* Cleanup - I told you it wasn't that long ;-) */
+		kfree(extra);
+	}
+
+	/* Call commit handler if needed and defined */
+	if(ret == -EIWCOMMIT)
+		ret = call_commit_handler(dev);
+
+	/* Here, we will generate the appropriate event if needed */
+
+	return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a private Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static inline int ioctl_private_call(struct net_device *	dev,
+				     struct ifreq *		ifr,
+				     unsigned int		cmd,
+				     iw_handler		handler)
+{
+	struct iwreq *			iwr = (struct iwreq *) ifr;
+	const struct iw_priv_args *	descr = NULL;
+	struct iw_request_info		info;
+	int				extra_size = 0;
+	int				i;
+	int				ret = -EINVAL;
+
+	/* Get the description of the IOCTL */
+	for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
+		if(cmd == dev->wireless_handlers->private_args[i].cmd) {
+			descr = &(dev->wireless_handlers->private_args[i]);
+			break;
+		}
+
+#ifdef WE_IOCTL_DEBUG
+	printk(KERN_DEBUG "%s (WE) : Found private handler for 0x%04X\n",
+	       ifr->ifr_name, cmd);
+	if(descr) {
+		printk(KERN_DEBUG "%s (WE) : Name %s, set %X, get %X\n",
+		       dev->name, descr->name,
+		       descr->set_args, descr->get_args);
+	}
+#endif	/* WE_IOCTL_DEBUG */
+
+	/* Compute the size of the set/get arguments */
+	if(descr != NULL) {
+		if(IW_IS_SET(cmd)) {
+			int	offset = 0;	/* For sub-ioctls */
+			/* Check for sub-ioctl handler */
+			if(descr->name[0] == '\0')
+				/* Reserve one int for sub-ioctl index */
+				offset = sizeof(__u32);
+
+			/* Size of set arguments */
+			extra_size = get_priv_size(descr->set_args);
+
+			/* Does it fits in iwr ? */
+			if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
+			   ((extra_size + offset) <= IFNAMSIZ))
+				extra_size = 0;
+		} else {
+			/* Size of get arguments */
+			extra_size = get_priv_size(descr->get_args);
+
+			/* Does it fits in iwr ? */
+			if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
+			   (extra_size <= IFNAMSIZ))
+				extra_size = 0;
+		}
+	}
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have a pointer to user space data or not. */
+	if(extra_size == 0) {
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
+	} else {
+		char *	extra;
+		int	err;
+
+		/* Check what user space is giving us */
+		if(IW_IS_SET(cmd)) {
+			/* Check NULL pointer */
+			if((iwr->u.data.pointer == NULL) &&
+			   (iwr->u.data.length != 0))
+				return -EFAULT;
+
+			/* Does it fits within bounds ? */
+			if(iwr->u.data.length > (descr->set_args &
+						 IW_PRIV_SIZE_MASK))
+				return -E2BIG;
+		} else {
+			/* Check NULL pointer */
+			if(iwr->u.data.pointer == NULL)
+				return -EFAULT;
+		}
+
+#ifdef WE_IOCTL_DEBUG
+		printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
+		       dev->name, extra_size);
+#endif	/* WE_IOCTL_DEBUG */
+
+		/* Always allocate for max space. Easier, and won't last
+		 * long... */
+		extra = kmalloc(extra_size, GFP_KERNEL);
+		if (extra == NULL) {
+			return -ENOMEM;
+		}
+
+		/* If it is a SET, get all the extra data in here */
+		if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+			err = copy_from_user(extra, iwr->u.data.pointer,
+					     extra_size);
+			if (err) {
+				kfree(extra);
+				return -EFAULT;
+			}
+#ifdef WE_IOCTL_DEBUG
+			printk(KERN_DEBUG "%s (WE) : Got %d elem\n",
+			       dev->name, iwr->u.data.length);
+#endif	/* WE_IOCTL_DEBUG */
+		}
+
+		/* Call the handler */
+		ret = handler(dev, &info, &(iwr->u), extra);
+
+		/* If we have something to return to the user */
+		if (!ret && IW_IS_GET(cmd)) {
+
+			/* Adjust for the actual length if it's variable,
+			 * avoid leaking kernel bits outside. */
+			if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
+				extra_size = adjust_priv_size(descr->get_args,
+							      &(iwr->u));
+			}
+
+			err = copy_to_user(iwr->u.data.pointer, extra,
+					   extra_size);
+			if (err)
+				ret =  -EFAULT;				   
+#ifdef WE_IOCTL_DEBUG
+			printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n",
+			       dev->name, iwr->u.data.length);
+#endif	/* WE_IOCTL_DEBUG */
+		}
+
+		/* Cleanup - I told you it wasn't that long ;-) */
+		kfree(extra);
+	}
+
+
+	/* Call commit handler if needed and defined */
+	if(ret == -EIWCOMMIT)
+		ret = call_commit_handler(dev);
+
+	return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main IOCTl dispatcher. Called from the main networking code
+ * (dev_ioctl() in net/core/dev.c).
+ * Check the type of IOCTL and call the appropriate wrapper...
+ */
+int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
+{
+	struct net_device *dev;
+	iw_handler	handler;
+
+	/* Permissions are already checked in dev_ioctl() before calling us.
+	 * The copy_to/from_user() of ifr is also dealt with in there */
+
+	/* Make sure the device exist */
+	if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
+		return -ENODEV;
+
+	/* A bunch of special cases, then the generic case...
+	 * Note that 'cmd' is already filtered in dev_ioctl() with
+	 * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
+	switch(cmd) 
+	{
+		case SIOCGIWSTATS:
+			/* Get Wireless Stats */
+			return ioctl_standard_call(dev,
+						   ifr,
+						   cmd,
+						   &iw_handler_get_iwstats);
+
+		case SIOCGIWPRIV:
+			/* Check if we have some wireless handlers defined */
+			if(dev->wireless_handlers != NULL) {
+				/* We export to user space the definition of
+				 * the private handler ourselves */
+				return ioctl_standard_call(dev,
+							   ifr,
+							   cmd,
+							   &iw_handler_get_private);
+			}
+			// ## Fall-through for old API ##
+		default:
+			/* Generic IOCTL */
+			/* Basic check */
+			if (!netif_device_present(dev))
+				return -ENODEV;
+			/* New driver API : try to find the handler */
+			handler = get_handler(dev, cmd);
+			if(handler != NULL) {
+				/* Standard and private are not the same */
+				if(cmd < SIOCIWFIRSTPRIV)
+					return ioctl_standard_call(dev,
+								   ifr,
+								   cmd,
+								   handler);
+				else
+					return ioctl_private_call(dev,
+								  ifr,
+								  cmd,
+								  handler);
+			}
+			/* Old driver API : call driver ioctl handler */
+			if (dev->do_ioctl) {
+				return dev->do_ioctl(dev, ifr, cmd);
+			}
+			return -EOPNOTSUPP;
+	}
+	/* Not reached */
+	return -EINVAL;
+}
+
+/********************** RTNETLINK REQUEST API **********************/
+/*
+ * The alternate user space API to configure all those Wireless Extensions
+ * is through RtNetlink.
+ * This API support only the new driver API (iw_handler).
+ *
+ * This RtNetlink API use the same query/reply model as the ioctl API.
+ * Maximum effort has been done to fit in the RtNetlink model, and
+ * we support both RtNetlink Set and RtNelink Get operations.
+ * On the other hand, we don't offer Dump operations because of the
+ * following reasons :
+ *	o Large number of parameters, most optional
+ *	o Large size of some parameters (> 100 bytes)
+ *	o Each parameters need to be extracted from hardware
+ *	o Scan requests can take seconds and disable network activity.
+ * Because of this high cost/overhead, we want to return only the
+ * parameters the user application is really interested in.
+ * We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag.
+ *
+ * The API uses the standard RtNetlink socket. When the RtNetlink code
+ * find a IFLA_WIRELESS field in a RtNetlink SET_LINK request,
+ * it calls here.
+ */
+
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a standard Wireless Extension GET handler.
+ * We do various checks and call the handler with the proper args.
+ */
+static int rtnetlink_standard_get(struct net_device *	dev,
+				  struct iw_event *	request,
+				  int			request_len,
+				  iw_handler		handler,
+				  char **		p_buf,
+				  int *			p_len)
+{
+	const struct iw_ioctl_description *	descr = NULL;
+	unsigned int				cmd;
+	union iwreq_data *			wrqu;
+	int					hdr_len;
+	struct iw_request_info			info;
+	char *					buffer = NULL;
+	int					buffer_size = 0;
+	int					ret = -EINVAL;
+
+	/* Get the description of the Request */
+	cmd = request->cmd;
+	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+		return -EOPNOTSUPP;
+	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+#ifdef WE_RTNETLINK_DEBUG
+	printk(KERN_DEBUG "%s (WE.r) : Found standard handler for 0x%04X\n",
+	       dev->name, cmd);
+	printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
+#endif	/* WE_RTNETLINK_DEBUG */
+
+	/* Check if wrqu is complete */
+	hdr_len = event_type_size[descr->header_type];
+	if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+		printk(KERN_DEBUG
+		       "%s (WE.r) : Wireless request too short (%d)\n",
+		       dev->name, request_len);
+#endif	/* WE_RTNETLINK_DEBUG */
+		return -EINVAL;
+	}
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have extra data in the reply or not */
+	if(descr->header_type != IW_HEADER_TYPE_POINT) {
+
+		/* Create the kernel buffer that we will return.
+		 * It's at an offset to match the TYPE_POINT case... */
+		buffer_size = request_len + IW_EV_POINT_OFF;
+		buffer = kmalloc(buffer_size, GFP_KERNEL);
+		if (buffer == NULL) {
+			return -ENOMEM;
+		}
+		/* Copy event data */
+		memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
+		/* Use our own copy of wrqu */
+		wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
+					     + IW_EV_LCP_LEN);
+
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, wrqu, NULL);
+
+	} else {
+		union iwreq_data	wrqu_point;
+		char *			extra = NULL;
+		int			extra_size = 0;
+
+		/* Get a temp copy of wrqu (skip pointer) */
+		memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
+		       ((char *) request) + IW_EV_LCP_LEN,
+		       IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+
+		/* Calculate space needed by arguments. Always allocate
+		 * for max space. Easier, and won't last long... */
+		extra_size = descr->max_tokens * descr->token_size;
+		/* Support for very large requests */
+		if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+		   (wrqu_point.data.length > descr->max_tokens))
+			extra_size = (wrqu_point.data.length
+				      * descr->token_size);
+		buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
+#ifdef WE_RTNETLINK_DEBUG
+		printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
+		       dev->name, extra_size, buffer_size);
+#endif	/* WE_RTNETLINK_DEBUG */
+
+		/* Create the kernel buffer that we will return */
+		buffer = kmalloc(buffer_size, GFP_KERNEL);
+		if (buffer == NULL) {
+			return -ENOMEM;
+		}
+
+		/* Put wrqu in the right place (just before extra).
+		 * Leave space for IWE header and dummy pointer...
+		 * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
+		 */
+		memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
+		       ((char *) &wrqu_point) + IW_EV_POINT_OFF,
+		       IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+		wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
+
+		/* Extra comes logically after that. Offset +12 bytes. */
+		extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
+
+		/* Call the handler */
+		ret = handler(dev, &info, wrqu, extra);
+
+		/* Calculate real returned length */
+		extra_size = (wrqu->data.length * descr->token_size);
+		/* Re-adjust reply size */
+		request->len = extra_size + IW_EV_POINT_LEN;
+
+		/* Put the iwe header where it should, i.e. scrap the
+		 * dummy pointer. */
+		memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
+
+#ifdef WE_RTNETLINK_DEBUG
+		printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
+#endif	/* WE_RTNETLINK_DEBUG */
+
+		/* Check if there is enough buffer up there */
+		if(wrqu_point.data.length < wrqu->data.length)
+			ret = -E2BIG;
+	}
+
+	/* Return the buffer to the caller */
+	if (!ret) {
+		*p_buf = buffer;
+		*p_len = request->len;
+	} else {
+		/* Cleanup */
+		if(buffer)
+			kfree(buffer);
+	}
+
+	return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a standard Wireless Extension SET handler.
+ * We do various checks and call the handler with the proper args.
+ */
+static inline int rtnetlink_standard_set(struct net_device *	dev,
+					 struct iw_event *	request,
+					 int			request_len,
+					 iw_handler		handler)
+{
+	const struct iw_ioctl_description *	descr = NULL;
+	unsigned int				cmd;
+	union iwreq_data *			wrqu;
+	union iwreq_data			wrqu_point;
+	int					hdr_len;
+	char *					extra = NULL;
+	int					extra_size = 0;
+	struct iw_request_info			info;
+	int					ret = -EINVAL;
+
+	/* Get the description of the Request */
+	cmd = request->cmd;
+	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+		return -EOPNOTSUPP;
+	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+#ifdef WE_RTNETLINK_DEBUG
+	printk(KERN_DEBUG "%s (WE.r) : Found standard SET handler for 0x%04X\n",
+	       dev->name, cmd);
+	printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
+#endif	/* WE_RTNETLINK_DEBUG */
+
+	/* Extract fixed header from request. This is properly aligned. */
+	wrqu = &request->u;
+
+	/* Check if wrqu is complete */
+	hdr_len = event_type_size[descr->header_type];
+	if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+		printk(KERN_DEBUG
+		       "%s (WE.r) : Wireless request too short (%d)\n",
+		       dev->name, request_len);
+#endif	/* WE_RTNETLINK_DEBUG */
+		return -EINVAL;
+	}
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have extra data in the request or not */
+	if(descr->header_type != IW_HEADER_TYPE_POINT) {
+
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, wrqu, NULL);
+
+	} else {
+		int	extra_len;
+
+		/* Put wrqu in the right place (skip pointer) */
+		memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
+		       wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+		/* Don't forget about the event code... */
+		wrqu = &wrqu_point;
+
+		/* Check if number of token fits within bounds */
+		if(wrqu_point.data.length > descr->max_tokens)
+			return -E2BIG;
+		if(wrqu_point.data.length < descr->min_tokens)
+			return -EINVAL;
+
+		/* Real length of payload */
+		extra_len = wrqu_point.data.length * descr->token_size;
+
+		/* Check if request is self consistent */
+		if((request_len - hdr_len) < extra_len) {
+#ifdef WE_RTNETLINK_DEBUG
+			printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
+			       dev->name, extra_size);
+#endif	/* WE_RTNETLINK_DEBUG */
+			return -EINVAL;
+		}
+
+#ifdef WE_RTNETLINK_DEBUG
+		printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
+		       dev->name, extra_size);
+#endif	/* WE_RTNETLINK_DEBUG */
+
+		/* Always allocate for max space. Easier, and won't last
+		 * long... */
+		extra_size = descr->max_tokens * descr->token_size;
+		extra = kmalloc(extra_size, GFP_KERNEL);
+		if (extra == NULL)
+			return -ENOMEM;
+
+		/* Copy extra in aligned buffer */
+		memcpy(extra, ((char *) request) + hdr_len, extra_len);
+
+		/* Call the handler */
+		ret = handler(dev, &info, &wrqu_point, extra);
+	}
+
+#ifdef WE_SET_EVENT
+	/* Generate an event to notify listeners of the change */
+	if((descr->flags & IW_DESCR_FLAG_EVENT) &&
+	   ((ret == 0) || (ret == -EIWCOMMIT))) {
+		if(descr->flags & IW_DESCR_FLAG_RESTRICT)
+			/* If the event is restricted, don't
+			 * export the payload */
+			wireless_send_event(dev, cmd, wrqu, NULL);
+		else
+			wireless_send_event(dev, cmd, wrqu, extra);
+	}
+#endif	/* WE_SET_EVENT */
+
+	/* Cleanup - I told you it wasn't that long ;-) */
+	if(extra)
+		kfree(extra);
+
+	/* Call commit handler if needed and defined */
+	if(ret == -EIWCOMMIT)
+		ret = call_commit_handler(dev);
+
+	return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a private Wireless Extension GET handler.
+ * Same as above...
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static inline int rtnetlink_private_get(struct net_device *	dev,
+					struct iw_event *	request,
+					int			request_len,
+					iw_handler		handler,
+					char **			p_buf,
+					int *			p_len)
+{
+	const struct iw_priv_args *	descr = NULL;
+	unsigned int			cmd;
+	union iwreq_data *		wrqu;
+	int				hdr_len;
+	struct iw_request_info		info;
+	int				extra_size = 0;
+	int				i;
+	char *				buffer = NULL;
+	int				buffer_size = 0;
+	int				ret = -EINVAL;
+
+	/* Get the description of the Request */
+	cmd = request->cmd;
+	for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
+		if(cmd == dev->wireless_handlers->private_args[i].cmd) {
+			descr = &(dev->wireless_handlers->private_args[i]);
+			break;
+		}
+	if(descr == NULL)
+		return -EOPNOTSUPP;
+
+#ifdef WE_RTNETLINK_DEBUG
+	printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
+	       dev->name, cmd);
+	printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
+	       dev->name, descr->name, descr->set_args, descr->get_args);
+#endif	/* WE_RTNETLINK_DEBUG */
+
+	/* Compute the max size of the get arguments */
+	extra_size = get_priv_size(descr->get_args);
+
+	/* Does it fits in wrqu ? */
+	if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
+	   (extra_size <= IFNAMSIZ)) {
+		hdr_len = extra_size;
+		extra_size = 0;
+	} else {
+		hdr_len = IW_EV_POINT_LEN;
+	}
+
+	/* Check if wrqu is complete */
+	if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+		printk(KERN_DEBUG
+		       "%s (WE.r) : Wireless request too short (%d)\n",
+		       dev->name, request_len);
+#endif	/* WE_RTNETLINK_DEBUG */
+		return -EINVAL;
+	}
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have a pointer to user space data or not. */
+	if(extra_size == 0) {
+
+		/* Create the kernel buffer that we will return.
+		 * It's at an offset to match the TYPE_POINT case... */
+		buffer_size = request_len + IW_EV_POINT_OFF;
+		buffer = kmalloc(buffer_size, GFP_KERNEL);
+		if (buffer == NULL) {
+			return -ENOMEM;
+		}
+		/* Copy event data */
+		memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
+		/* Use our own copy of wrqu */
+		wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
+					     + IW_EV_LCP_LEN);
+
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, wrqu, (char *) wrqu);
+
+	} else {
+		char *	extra;
+
+		/* Buffer for full reply */
+		buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
+
+#ifdef WE_RTNETLINK_DEBUG
+		printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
+		       dev->name, extra_size, buffer_size);
+#endif	/* WE_RTNETLINK_DEBUG */
+
+		/* Create the kernel buffer that we will return */
+		buffer = kmalloc(buffer_size, GFP_KERNEL);
+		if (buffer == NULL) {
+			return -ENOMEM;
+		}
+
+		/* Put wrqu in the right place (just before extra).
+		 * Leave space for IWE header and dummy pointer...
+		 * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
+		 */
+		memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
+		       ((char *) request) + IW_EV_LCP_LEN,
+		       IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+		wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
+
+		/* Extra comes logically after that. Offset +12 bytes. */
+		extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
+
+		/* Call the handler */
+		ret = handler(dev, &info, wrqu, extra);
+
+		/* Adjust for the actual length if it's variable,
+		 * avoid leaking kernel bits outside. */
+		if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
+			extra_size = adjust_priv_size(descr->get_args, wrqu);
+		/* Re-adjust reply size */
+		request->len = extra_size + IW_EV_POINT_LEN;
+
+		/* Put the iwe header where it should, i.e. scrap the
+		 * dummy pointer. */
+		memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
+
+#ifdef WE_RTNETLINK_DEBUG
+		printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
+#endif	/* WE_RTNETLINK_DEBUG */
+	}
+
+	/* Return the buffer to the caller */
+	if (!ret) {
+		*p_buf = buffer;
+		*p_len = request->len;
+	} else {
+		/* Cleanup */
+		if(buffer)
+			kfree(buffer);
+	}
+
+	return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a private Wireless Extension SET handler.
+ * Same as above...
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static inline int rtnetlink_private_set(struct net_device *	dev,
+					struct iw_event *	request,
+					int			request_len,
+					iw_handler		handler)
+{
+	const struct iw_priv_args *	descr = NULL;
+	unsigned int			cmd;
+	union iwreq_data *		wrqu;
+	union iwreq_data		wrqu_point;
+	int				hdr_len;
+	char *				extra = NULL;
+	int				extra_size = 0;
+	int				offset = 0;	/* For sub-ioctls */
+	struct iw_request_info		info;
+	int				i;
+	int				ret = -EINVAL;
+
+	/* Get the description of the Request */
+	cmd = request->cmd;
+	for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
+		if(cmd == dev->wireless_handlers->private_args[i].cmd) {
+			descr = &(dev->wireless_handlers->private_args[i]);
+			break;
+		}
+	if(descr == NULL)
+		return -EOPNOTSUPP;
+
+#ifdef WE_RTNETLINK_DEBUG
+	printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
+	       ifr->ifr_name, cmd);
+	printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
+	       dev->name, descr->name, descr->set_args, descr->get_args);
+#endif	/* WE_RTNETLINK_DEBUG */
+
+	/* Compute the size of the set arguments */
+	/* Check for sub-ioctl handler */
+	if(descr->name[0] == '\0')
+		/* Reserve one int for sub-ioctl index */
+		offset = sizeof(__u32);
+
+	/* Size of set arguments */
+	extra_size = get_priv_size(descr->set_args);
+
+	/* Does it fits in wrqu ? */
+	if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
+	   (extra_size <= IFNAMSIZ)) {
+		hdr_len = IW_EV_LCP_LEN + extra_size;
+		extra_size = 0;
+	} else {
+		hdr_len = IW_EV_POINT_LEN;
+	}
+
+	/* Extract fixed header from request. This is properly aligned. */
+	wrqu = &request->u;
+
+	/* Check if wrqu is complete */
+	if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+		printk(KERN_DEBUG
+		       "%s (WE.r) : Wireless request too short (%d)\n",
+		       dev->name, request_len);
+#endif	/* WE_RTNETLINK_DEBUG */
+		return -EINVAL;
+	}
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have a pointer to user space data or not. */
+	if(extra_size == 0) {
+
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, wrqu, (char *) wrqu);
+
+	} else {
+		int	extra_len;
+
+		/* Put wrqu in the right place (skip pointer) */
+		memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
+		       wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+
+		/* Does it fits within bounds ? */
+		if(wrqu_point.data.length > (descr->set_args &
+					     IW_PRIV_SIZE_MASK))
+			return -E2BIG;
+
+		/* Real length of payload */
+		extra_len = adjust_priv_size(descr->set_args, &wrqu_point);
+
+		/* Check if request is self consistent */
+		if((request_len - hdr_len) < extra_len) {
+#ifdef WE_RTNETLINK_DEBUG
+			printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
+			       dev->name, extra_size);
+#endif	/* WE_RTNETLINK_DEBUG */
+			return -EINVAL;
+		}
+
+#ifdef WE_RTNETLINK_DEBUG
+		printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
+		       dev->name, extra_size);
+#endif	/* WE_RTNETLINK_DEBUG */
+
+		/* Always allocate for max space. Easier, and won't last
+		 * long... */
+		extra = kmalloc(extra_size, GFP_KERNEL);
+		if (extra == NULL)
+			return -ENOMEM;
+
+		/* Copy extra in aligned buffer */
+		memcpy(extra, ((char *) request) + hdr_len, extra_len);
+
+		/* Call the handler */
+		ret = handler(dev, &info, &wrqu_point, extra);
+
+		/* Cleanup - I told you it wasn't that long ;-) */
+		kfree(extra);
+	}
+
+	/* Call commit handler if needed and defined */
+	if(ret == -EIWCOMMIT)
+		ret = call_commit_handler(dev);
+
+	return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main RtNetlink dispatcher. Called from the main networking code
+ * (do_getlink() in net/core/rtnetlink.c).
+ * Check the type of Request and call the appropriate wrapper...
+ */
+int wireless_rtnetlink_get(struct net_device *	dev,
+			   char *		data,
+			   int			len,
+			   char **		p_buf,
+			   int *		p_len)
+{
+	struct iw_event *	request = (struct iw_event *) data;
+	iw_handler		handler;
+
+	/* Check length */
+	if(len < IW_EV_LCP_LEN) {
+		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
+		       dev->name, len);
+		return -EINVAL;
+	}
+
+	/* ReCheck length (len may have padding) */
+	if(request->len > len) {
+		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
+		       dev->name, request->len, len);
+		return -EINVAL;
+	}
+
+	/* Only accept GET requests in here */
+	if(!IW_IS_GET(request->cmd))
+		return -EOPNOTSUPP;
+
+	/* If command is `get the encoding parameters', check if
+	 * the user has the right to do it */
+	if (request->cmd == SIOCGIWENCODE ||
+	    request->cmd == SIOCGIWENCODEEXT) {
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+	}
+
+	/* Special cases */
+	if(request->cmd == SIOCGIWSTATS)
+		/* Get Wireless Stats */
+		return rtnetlink_standard_get(dev,
+					      request,
+					      request->len,
+					      &iw_handler_get_iwstats,
+					      p_buf, p_len);
+	if(request->cmd == SIOCGIWPRIV) {
+		/* Check if we have some wireless handlers defined */
+		if(dev->wireless_handlers == NULL)
+			return -EOPNOTSUPP;
+		/* Get Wireless Stats */
+		return rtnetlink_standard_get(dev,
+					      request,
+					      request->len,
+					      &iw_handler_get_private,
+					      p_buf, p_len);
+	}
+
+	/* Basic check */
+	if (!netif_device_present(dev))
+		return -ENODEV;
+
+	/* Try to find the handler */
+	handler = get_handler(dev, request->cmd);
+	if(handler != NULL) {
+		/* Standard and private are not the same */
+		if(request->cmd < SIOCIWFIRSTPRIV)
+			return rtnetlink_standard_get(dev,
+						      request,
+						      request->len,
+						      handler,
+						      p_buf, p_len);
+		else
+			return rtnetlink_private_get(dev,
+						     request,
+						     request->len,
+						     handler,
+						     p_buf, p_len);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main RtNetlink dispatcher. Called from the main networking code
+ * (do_setlink() in net/core/rtnetlink.c).
+ * Check the type of Request and call the appropriate wrapper...
+ */
+int wireless_rtnetlink_set(struct net_device *	dev,
+			   char *		data,
+			   int			len)
+{
+	struct iw_event *	request = (struct iw_event *) data;
+	iw_handler		handler;
+
+	/* Check length */
+	if(len < IW_EV_LCP_LEN) {
+		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
+		       dev->name, len);
+		return -EINVAL;
+	}
+
+	/* ReCheck length (len may have padding) */
+	if(request->len > len) {
+		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
+		       dev->name, request->len, len);
+		return -EINVAL;
+	}
+
+	/* Only accept SET requests in here */
+	if(!IW_IS_SET(request->cmd))
+		return -EOPNOTSUPP;
+
+	/* Basic check */
+	if (!netif_device_present(dev))
+		return -ENODEV;
+
+	/* New driver API : try to find the handler */
+	handler = get_handler(dev, request->cmd);
+	if(handler != NULL) {
+		/* Standard and private are not the same */
+		if(request->cmd < SIOCIWFIRSTPRIV)
+			return rtnetlink_standard_set(dev,
+						      request,
+						      request->len,
+						      handler);
+		else
+			return rtnetlink_private_set(dev,
+						     request,
+						     request->len,
+						     handler);
+	}
+
+	return -EOPNOTSUPP;
+}
+#endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
+
+
+/************************* EVENT PROCESSING *************************/
+/*
+ * Process events generated by the wireless layer or the driver.
+ * Most often, the event will be propagated through rtnetlink
+ */
+
+#ifdef WE_EVENT_RTNETLINK
+/* ---------------------------------------------------------------- */
+/*
+ * Locking...
+ * ----------
+ *
+ * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
+ * the locking issue in here and implementing this code !
+ *
+ * The issue : wireless_send_event() is often called in interrupt context,
+ * while the Netlink layer can never be called in interrupt context.
+ * The fully formed RtNetlink events are queued, and then a tasklet is run
+ * to feed those to Netlink.
+ * The skb_queue is interrupt safe, and its lock is not held while calling
+ * Netlink, so there is no possibility of dealock.
+ * Jean II
+ */
+
+static struct sk_buff_head wireless_nlevent_queue;
+
+static int __init wireless_nlevent_init(void)
+{
+	skb_queue_head_init(&wireless_nlevent_queue);
+	return 0;
+}
+
+subsys_initcall(wireless_nlevent_init);
+
+static void wireless_nlevent_process(unsigned long data)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&wireless_nlevent_queue)))
+		rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
+}
+
+static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
+
+/* ---------------------------------------------------------------- */
+/*
+ * Fill a rtnetlink message with our event data.
+ * Note that we propage only the specified event and don't dump the
+ * current wireless config. Dumping the wireless config is far too
+ * expensive (for each parameter, the driver need to query the hardware).
+ */
+static inline int rtnetlink_fill_iwinfo(struct sk_buff *	skb,
+					struct net_device *	dev,
+					int			type,
+					char *			event,
+					int			event_len)
+{
+	struct ifinfomsg *r;
+	struct nlmsghdr  *nlh;
+	unsigned char	 *b = skb->tail;
+
+	nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
+	r = NLMSG_DATA(nlh);
+	r->ifi_family = AF_UNSPEC;
+	r->__ifi_pad = 0;
+	r->ifi_type = dev->type;
+	r->ifi_index = dev->ifindex;
+	r->ifi_flags = dev_get_flags(dev);
+	r->ifi_change = 0;	/* Wireless changes don't affect those flags */
+
+	/* Add the wireless events in the netlink packet */
+	RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
+
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+	skb_trim(skb, b - skb->data);
+	return -1;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Create and broadcast and send it on the standard rtnetlink socket
+ * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c
+ * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
+ * within a RTM_NEWLINK event.
+ */
+static inline void rtmsg_iwinfo(struct net_device *	dev,
+				char *			event,
+				int			event_len)
+{
+	struct sk_buff *skb;
+	int size = NLMSG_GOODSIZE;
+
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (!skb)
+		return;
+
+	if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
+				  event, event_len) < 0) {
+		kfree_skb(skb);
+		return;
+	}
+	NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
+	skb_queue_tail(&wireless_nlevent_queue, skb);
+	tasklet_schedule(&wireless_nlevent_tasklet);
+}
+
+#endif	/* WE_EVENT_RTNETLINK */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main event dispatcher. Called from other parts and drivers.
+ * Send the event on the appropriate channels.
+ * May be called from interrupt context.
+ */
+void wireless_send_event(struct net_device *	dev,
+			 unsigned int		cmd,
+			 union iwreq_data *	wrqu,
+			 char *			extra)
+{
+	const struct iw_ioctl_description *	descr = NULL;
+	int extra_len = 0;
+	struct iw_event  *event;		/* Mallocated whole event */
+	int event_len;				/* Its size */
+	int hdr_len;				/* Size of the event header */
+	int wrqu_off = 0;			/* Offset in wrqu */
+	/* Don't "optimise" the following variable, it will crash */
+	unsigned	cmd_index;		/* *MUST* be unsigned */
+
+	/* Get the description of the Event */
+	if(cmd <= SIOCIWLAST) {
+		cmd_index = cmd - SIOCIWFIRST;
+		if(cmd_index < standard_ioctl_num)
+			descr = &(standard_ioctl[cmd_index]);
+	} else {
+		cmd_index = cmd - IWEVFIRST;
+		if(cmd_index < standard_event_num)
+			descr = &(standard_event[cmd_index]);
+	}
+	/* Don't accept unknown events */
+	if(descr == NULL) {
+		/* Note : we don't return an error to the driver, because
+		 * the driver would not know what to do about it. It can't
+		 * return an error to the user, because the event is not
+		 * initiated by a user request.
+		 * The best the driver could do is to log an error message.
+		 * We will do it ourselves instead...
+		 */
+	  	printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
+		       dev->name, cmd);
+		return;
+	}
+#ifdef WE_EVENT_DEBUG
+	printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n",
+	       dev->name, cmd);
+	printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
+#endif	/* WE_EVENT_DEBUG */
+
+	/* Check extra parameters and set extra_len */
+	if(descr->header_type == IW_HEADER_TYPE_POINT) {
+		/* Check if number of token fits within bounds */
+		if(wrqu->data.length > descr->max_tokens) {
+		  	printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
+			return;
+		}
+		if(wrqu->data.length < descr->min_tokens) {
+		  	printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
+			return;
+		}
+		/* Calculate extra_len - extra is NULL for restricted events */
+		if(extra != NULL)
+			extra_len = wrqu->data.length * descr->token_size;
+		/* Always at an offset in wrqu */
+		wrqu_off = IW_EV_POINT_OFF;
+#ifdef WE_EVENT_DEBUG
+		printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len);
+#endif	/* WE_EVENT_DEBUG */
+	}
+
+	/* Total length of the event */
+	hdr_len = event_type_size[descr->header_type];
+	event_len = hdr_len + extra_len;
+
+#ifdef WE_EVENT_DEBUG
+	printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, wrqu_off %d, event_len %d\n", dev->name, cmd, hdr_len, wrqu_off, event_len);
+#endif	/* WE_EVENT_DEBUG */
+
+	/* Create temporary buffer to hold the event */
+	event = kmalloc(event_len, GFP_ATOMIC);
+	if(event == NULL)
+		return;
+
+	/* Fill event */
+	event->len = event_len;
+	event->cmd = cmd;
+	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
+	if(extra != NULL)
+		memcpy(((char *) event) + hdr_len, extra, extra_len);
+
+#ifdef WE_EVENT_RTNETLINK
+	/* Send via the RtNetlink event channel */
+	rtmsg_iwinfo(dev, (char *) event, event_len);
+#endif	/* WE_EVENT_RTNETLINK */
+
+	/* Cleanup */
+	kfree(event);
+
+	return;		/* Always success, I guess ;-) */
+}
+
+/********************** ENHANCED IWSPY SUPPORT **********************/
+/*
+ * In the old days, the driver was handling spy support all by itself.
+ * Now, the driver can delegate this task to Wireless Extensions.
+ * It needs to use those standard spy iw_handler in struct iw_handler_def,
+ * push data to us via wireless_spy_update() and include struct iw_spy_data
+ * in its private part (and export it in net_device->wireless_data->spy_data).
+ * One of the main advantage of centralising spy support here is that
+ * it becomes much easier to improve and extend it without having to touch
+ * the drivers. One example is the addition of the Spy-Threshold events.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Return the pointer to the spy data in the driver.
+ * Because this is called on the Rx path via wireless_spy_update(),
+ * we want it to be efficient...
+ */
+static inline struct iw_spy_data * get_spydata(struct net_device *dev)
+{
+	/* This is the new way */
+	if(dev->wireless_data)
+		return(dev->wireless_data->spy_data);
+	return NULL;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : set Spy List
+ */
+int iw_handler_set_spy(struct net_device *	dev,
+		       struct iw_request_info *	info,
+		       union iwreq_data *	wrqu,
+		       char *			extra)
+{
+	struct iw_spy_data *	spydata = get_spydata(dev);
+	struct sockaddr *	address = (struct sockaddr *) extra;
+
+	/* Make sure driver is not buggy or using the old API */
+	if(!spydata)
+		return -EOPNOTSUPP;
+
+	/* Disable spy collection while we copy the addresses.
+	 * While we copy addresses, any call to wireless_spy_update()
+	 * will NOP. This is OK, as anyway the addresses are changing. */
+	spydata->spy_number = 0;
+
+	/* We want to operate without locking, because wireless_spy_update()
+	 * most likely will happen in the interrupt handler, and therefore
+	 * have its own locking constraints and needs performance.
+	 * The rtnl_lock() make sure we don't race with the other iw_handlers.
+	 * This make sure wireless_spy_update() "see" that the spy list
+	 * is temporarily disabled. */
+	smp_wmb();
+
+	/* Are there are addresses to copy? */
+	if(wrqu->data.length > 0) {
+		int i;
+
+		/* Copy addresses */
+		for(i = 0; i < wrqu->data.length; i++)
+			memcpy(spydata->spy_address[i], address[i].sa_data,
+			       ETH_ALEN);
+		/* Reset stats */
+		memset(spydata->spy_stat, 0,
+		       sizeof(struct iw_quality) * IW_MAX_SPY);
+
+#ifdef WE_SPY_DEBUG
+		printk(KERN_DEBUG "iw_handler_set_spy() :  wireless_data %p, spydata %p, num %d\n", dev->wireless_data, spydata, wrqu->data.length);
+		for (i = 0; i < wrqu->data.length; i++)
+			printk(KERN_DEBUG
+			       "%02X:%02X:%02X:%02X:%02X:%02X \n",
+			       spydata->spy_address[i][0],
+			       spydata->spy_address[i][1],
+			       spydata->spy_address[i][2],
+			       spydata->spy_address[i][3],
+			       spydata->spy_address[i][4],
+			       spydata->spy_address[i][5]);
+#endif	/* WE_SPY_DEBUG */
+	}
+
+	/* Make sure above is updated before re-enabling */
+	smp_wmb();
+
+	/* Enable addresses */
+	spydata->spy_number = wrqu->data.length;
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : get Spy List
+ */
+int iw_handler_get_spy(struct net_device *	dev,
+		       struct iw_request_info *	info,
+		       union iwreq_data *	wrqu,
+		       char *			extra)
+{
+	struct iw_spy_data *	spydata = get_spydata(dev);
+	struct sockaddr *	address = (struct sockaddr *) extra;
+	int			i;
+
+	/* Make sure driver is not buggy or using the old API */
+	if(!spydata)
+		return -EOPNOTSUPP;
+
+	wrqu->data.length = spydata->spy_number;
+
+	/* Copy addresses. */
+	for(i = 0; i < spydata->spy_number; i++) 	{
+		memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
+		address[i].sa_family = AF_UNIX;
+	}
+	/* Copy stats to the user buffer (just after). */
+	if(spydata->spy_number > 0)
+		memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
+		       spydata->spy_stat,
+		       sizeof(struct iw_quality) * spydata->spy_number);
+	/* Reset updated flags. */
+	for(i = 0; i < spydata->spy_number; i++)
+		spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : set spy threshold
+ */
+int iw_handler_set_thrspy(struct net_device *	dev,
+			  struct iw_request_info *info,
+			  union iwreq_data *	wrqu,
+			  char *		extra)
+{
+	struct iw_spy_data *	spydata = get_spydata(dev);
+	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
+
+	/* Make sure driver is not buggy or using the old API */
+	if(!spydata)
+		return -EOPNOTSUPP;
+
+	/* Just do it */
+	memcpy(&(spydata->spy_thr_low), &(threshold->low),
+	       2 * sizeof(struct iw_quality));
+
+	/* Clear flag */
+	memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
+
+#ifdef WE_SPY_DEBUG
+	printk(KERN_DEBUG "iw_handler_set_thrspy() :  low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level);
+#endif	/* WE_SPY_DEBUG */
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : get spy threshold
+ */
+int iw_handler_get_thrspy(struct net_device *	dev,
+			  struct iw_request_info *info,
+			  union iwreq_data *	wrqu,
+			  char *		extra)
+{
+	struct iw_spy_data *	spydata = get_spydata(dev);
+	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
+
+	/* Make sure driver is not buggy or using the old API */
+	if(!spydata)
+		return -EOPNOTSUPP;
+
+	/* Just do it */
+	memcpy(&(threshold->low), &(spydata->spy_thr_low),
+	       2 * sizeof(struct iw_quality));
+
+	return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Prepare and send a Spy Threshold event
+ */
+static void iw_send_thrspy_event(struct net_device *	dev,
+				 struct iw_spy_data *	spydata,
+				 unsigned char *	address,
+				 struct iw_quality *	wstats)
+{
+	union iwreq_data	wrqu;
+	struct iw_thrspy	threshold;
+
+	/* Init */
+	wrqu.data.length = 1;
+	wrqu.data.flags = 0;
+	/* Copy address */
+	memcpy(threshold.addr.sa_data, address, ETH_ALEN);
+	threshold.addr.sa_family = ARPHRD_ETHER;
+	/* Copy stats */
+	memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
+	/* Copy also thresholds */
+	memcpy(&(threshold.low), &(spydata->spy_thr_low),
+	       2 * sizeof(struct iw_quality));
+
+#ifdef WE_SPY_DEBUG
+	printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n",
+	       threshold.addr.sa_data[0],
+	       threshold.addr.sa_data[1],
+	       threshold.addr.sa_data[2],
+	       threshold.addr.sa_data[3],
+	       threshold.addr.sa_data[4],
+	       threshold.addr.sa_data[5], threshold.qual.level);
+#endif	/* WE_SPY_DEBUG */
+
+	/* Send event to user space */
+	wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Call for the driver to update the spy data.
+ * For now, the spy data is a simple array. As the size of the array is
+ * small, this is good enough. If we wanted to support larger number of
+ * spy addresses, we should use something more efficient...
+ */
+void wireless_spy_update(struct net_device *	dev,
+			 unsigned char *	address,
+			 struct iw_quality *	wstats)
+{
+	struct iw_spy_data *	spydata = get_spydata(dev);
+	int			i;
+	int			match = -1;
+
+	/* Make sure driver is not buggy or using the old API */
+	if(!spydata)
+		return;
+
+#ifdef WE_SPY_DEBUG
+	printk(KERN_DEBUG "wireless_spy_update() :  wireless_data %p, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_data, spydata, address[0], address[1], address[2], address[3], address[4], address[5]);
+#endif	/* WE_SPY_DEBUG */
+
+	/* Update all records that match */
+	for(i = 0; i < spydata->spy_number; i++)
+		if(!compare_ether_addr(address, spydata->spy_address[i])) {
+			memcpy(&(spydata->spy_stat[i]), wstats,
+			       sizeof(struct iw_quality));
+			match = i;
+		}
+
+	/* Generate an event if we cross the spy threshold.
+	 * To avoid event storms, we have a simple hysteresis : we generate
+	 * event only when we go under the low threshold or above the
+	 * high threshold. */
+	if(match >= 0) {
+		if(spydata->spy_thr_under[match]) {
+			if(wstats->level > spydata->spy_thr_high.level) {
+				spydata->spy_thr_under[match] = 0;
+				iw_send_thrspy_event(dev, spydata,
+						     address, wstats);
+			}
+		} else {
+			if(wstats->level < spydata->spy_thr_low.level) {
+				spydata->spy_thr_under[match] = 1;
+				iw_send_thrspy_event(dev, spydata,
+						     address, wstats);
+			}
+		}
+	}
+}
+
+EXPORT_SYMBOL(iw_handler_get_spy);
+EXPORT_SYMBOL(iw_handler_get_thrspy);
+EXPORT_SYMBOL(iw_handler_set_spy);
+EXPORT_SYMBOL(iw_handler_set_thrspy);
+EXPORT_SYMBOL(wireless_send_event);
+EXPORT_SYMBOL(wireless_spy_update);
-- 
1.4.4.2

-- 
John W. Linville
linville@tuxdriver.com
_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RFC PATCH 3/3] cfg80211: add wext-compatible client
  2007-01-31  1:39   ` [RFC PATCH 2/3] wireless: move wext to net/wireless/ John W. Linville
@ 2007-01-31  1:41     ` John W. Linville
  2007-01-31 14:40     ` [RFC PATCH 2/3] wireless: move wext to net/wireless/ Christoph Hellwig
  1 sibling, 0 replies; 52+ messages in thread
From: John W. Linville @ 2007-01-31  1:41 UTC (permalink / raw)
  To: wireless

From: Johannes Berg <johannes@sipsolutions.net>

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Jiri Benc <jbenc@suse.cz>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
 include/linux/netdevice.h  |    7 +-
 include/net/cfg80211.h     |   34 +-
 net/Kconfig                |   28 +
 net/core/dev.c             |   38 +-
 net/core/net-sysfs.c       |    4 +-
 net/core/rtnetlink.c       |   42 ++-
 net/wireless/Makefile      |   10 +
 net/wireless/core.c        |   16 +-
 net/wireless/core.h        |   21 +
 net/wireless/wext-common.c |  610 ++++++++++++++++
 net/wireless/wext-compat.c | 1646 +++++++++++++++++++++++++++++++++++++++++++-
 net/wireless/wext-old.c    |  629 +-----------------
 net/wireless/wext.h        |   13 +
 13 files changed, 2433 insertions(+), 665 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index c1e9962..6a9b4c8 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -348,12 +348,17 @@ struct net_device
 
 	struct net_device_stats* (*get_stats)(struct net_device *dev);
 
+#ifdef CONFIG_WIRELESS_EXT
 	/* List of functions to handle Wireless Extensions (instead of ioctl).
 	 * See <net/iw_handler.h> for details. Jean II */
 	const struct iw_handler_def *	wireless_handlers;
 	/* Instance data managed by the core of Wireless Extensions. */
 	struct iw_public_data *	wireless_data;
-
+#endif
+#ifdef CONFIG_CFG80211_WEXT_COMPAT
+	/* pending config used by cfg80211/wext compat code only */
+	void *cfg80211_wext_pending_config;
+#endif
 	const struct ethtool_ops *ethtool_ops;
 
 	/*
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index d83c47f..2a78a6d 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -6,6 +6,7 @@
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <net/genetlink.h>
+#include <linux/wireless.h>
 
 /*
  * 802.11 configuration in-kernel interface
@@ -17,21 +18,12 @@
  * struct cfg80211_config - description of a configuration (request)
  */
 struct cfg80211_config {
-	/* first fields with 'internal' validity */
+	/* see below */
+	u32 valid;
 
-	/* SSID to use, valid if not NULL, ssid_len then
-	 * contains the length. change forces reassociation */
 	s8 ssid_len;
 	u8 *ssid;
 
-	/* now fields with explicit validity */
-#define CFG80211_CFG_VALID_NWID			(1<<0)
-#define CFG80211_CFG_VALID_RX_SENSITIVITY	(1<<1)
-#define CFG80211_CFG_VALID_TRANSMIT_POWER	(1<<2)
-#define CFG80211_CFG_VALID_FRAG_THRESHOLD	(1<<3)
-#define CFG80211_CFG_VALID_CHANNEL		(1<<4)
-	unsigned int valid;
-
 	u16 network_id;
 	s32 rx_sensitivity;
 	u32 transmit_power;
@@ -39,6 +31,13 @@ struct cfg80211_config {
 	u32 channel;
 };
 
+#define CFG80211_CFG_VALID_SSID			(1<<0)
+#define CFG80211_CFG_VALID_NWID			(1<<1)
+#define CFG80211_CFG_VALID_RX_SENSITIVITY	(1<<2)
+#define CFG80211_CFG_VALID_TRANSMIT_POWER	(1<<3)
+#define CFG80211_CFG_VALID_FRAG_THRESHOLD	(1<<4)
+#define CFG80211_CFG_VALID_CHANNEL		(1<<5)
+
 struct scan_channel {
 	u32 channel;
 	int active;
@@ -87,6 +86,9 @@ struct scan_params {
  *
  * @get_config: fill the given config structure with the current configuration
  *
+ * @get_config_valid: return a bitmask of CFG80211_CFG_VALID_* indicating
+ *		      which parameters can be set.
+ *
  * @reassociate: reassociate with current settings (SSID, BSSID if
  *		 userspace roaming is enabled)
  *
@@ -133,6 +135,7 @@ struct cfg80211_ops {
 			     struct cfg80211_config *cfg);
 	void	(*get_config)(void *priv, struct net_device *dev,
 			      struct cfg80211_config *cfg);
+	u32	(*get_config_valid)(void *priv, struct net_device *dev);
 
 
 	int	(*reassociate)(void *priv, struct net_device *dev);
@@ -190,4 +193,13 @@ extern void *nl80211hdr_put(struct sk_buff *skb, u32 pid,
 extern void *nl80211msg_new(struct sk_buff **skb, u32 pid,
 			    u32 seq, int flags, u8 cmd);
 
+#ifdef CONFIG_CFG80211_WEXT_COMPAT
+extern int cfg80211_wext_ioctl(struct ifreq *ifr, unsigned int cmd);
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+int cfg80211_wext_nl_set(struct net_device *dev, char *data, int len);
+int cfg80211_wext_nl_get(struct net_device *dev, char *data, int len,
+			 char **p_buf, int *p_len);
+#endif
+#endif
+
 #endif /* __NET_CFG80211_H */
diff --git a/net/Kconfig b/net/Kconfig
index 8d121a5..fade3fd 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -229,6 +229,34 @@ config FIB_RULES
 config CFG80211
 	tristate "Improved wireless configuration API"
 
+config CFG80211_WEXT_COMPAT
+	bool "cfg80211 Wireless Extensions compatibility"
+	depends CFG80211
+	default y
+	---help---
+	This option allows using devices whose drivers have been
+	converted to use the new cfg80211 with wireless extensions,
+	providing WE-20 compatibility. Note that cfg80211's "native"
+	interface is nl80211 using generic netlink. The wireless
+	extensions are being deprecated, but userspace tools may still
+	be using them.
+
+	If unsure, say Y.
+
+config CFG80211_WEXTNL_COMPAT
+	bool "cfg80211 WE-netlink compatibility"
+	depends CFG80211 && CFG80211_WEXT_COMPAT
+	---help---
+	This option allows using devices whose drivers have been
+	converted to use the new cfg80211 with wireless extensions
+	over rtnetlink, providing WE-20 compatibility. Note that
+	cfg80211's "native" interface is nl80211 using generic netlink.
+	The wireless extensions are being deprecated and the netlink
+	based API for WE was never configured by default, nor do any
+	userspace tools use this feature.
+
+	This option exists only to make Jean happy. Say N.
+
 endif   # if NET
 endmenu # Networking
 
diff --git a/net/core/dev.c b/net/core/dev.c
index c8822aa..8584046 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -116,6 +116,7 @@
 #include <linux/dmaengine.h>
 #include <linux/err.h>
 #include <linux/ctype.h>
+#include <net/cfg80211.h>
 
 /*
  *	The list of packet types we will receive (as opposed to discard)
@@ -2228,7 +2229,7 @@ static struct file_operations softnet_seq_fops = {
 	.release = seq_release,
 };
 
-#ifdef CONFIG_WIRELESS_EXT
+#if defined(CONFIG_WIRELESS_EXT) || defined(CFG80211_WEXT_COMPAT)
 extern int wireless_proc_init(void);
 #else
 #define wireless_proc_init() 0
@@ -2798,6 +2799,39 @@ int dev_ioctl(unsigned int cmd, void __user *arg)
 					ret = -EFAULT;
 				return ret;
 			}
+#ifdef CONFIG_CFG80211_WEXT_COMPAT
+			/* Take care of cfg80211 WE compatibility */
+			if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
+				/* If command is `set a parameter', or
+				 * `get the encoding parameters', check if
+				 * the user has the right to do it */
+				if (IW_IS_SET(cmd) || cmd == SIOCGIWENCODE
+				    || cmd == SIOCGIWENCODEEXT) {
+					if (!capable(CAP_NET_ADMIN))
+						return -EPERM;
+				}
+				dev_load(ifr.ifr_name);
+				rtnl_lock();
+				/* Follow me in net/wireless/wext-compat.c */
+				ret = cfg80211_wext_ioctl(&ifr, cmd);
+				rtnl_unlock();
+				if (ret == 0 && IW_IS_GET(cmd) &&
+				    copy_to_user(arg, &ifr,
+					    	 sizeof(struct ifreq)))
+					ret = -EFAULT;
+				/* haha, I cheat here by allowing a driver or
+				 * stack to have both WE or CFG80211-WE for
+				 * a little while during conversion... hope that
+				 * ENOSYS is only used to indicate not implemented
+				 *
+				 * if wireless extensions are not configured
+				 * then this is the last thing here so that
+				 * if we fall through we return -EINVAL
+				 */
+				if (ret != -ENOSYS)
+					return ret;
+			}
+#endif
 #ifdef CONFIG_WIRELESS_EXT
 			/* Take care of Wireless Extensions */
 			if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
@@ -2814,7 +2848,7 @@ int dev_ioctl(unsigned int cmd, void __user *arg)
 				/* Follow me in net/wireless/wext-old.c */
 				ret = wireless_process_ioctl(&ifr, cmd);
 				rtnl_unlock();
-				if (IW_IS_GET(cmd) &&
+				if (ret == 0 && IW_IS_GET(cmd) &&
 				    copy_to_user(arg, &ifr,
 					    	 sizeof(struct ifreq)))
 					ret = -EFAULT;
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index f47f319..44e69a2 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -329,7 +329,7 @@ static struct attribute_group netstat_group = {
 	.attrs  = netstat_attrs,
 };
 
-#ifdef WIRELESS_EXT
+#ifdef CONFIG_WIRELESS_EXT
 /* helper function that does all the locking etc for wireless stats */
 static ssize_t wireless_show(struct class_device *cd, char *buf,
 			     ssize_t (*format)(const struct iw_statistics *,
@@ -462,7 +462,7 @@ int netdev_register_sysfs(struct net_device *net)
 	if (net->get_stats)
 		*groups++ = &netstat_group;
 
-#ifdef WIRELESS_EXT
+#ifdef CONFIG_WIRELESS_EXT
 	if (net->wireless_handlers && net->wireless_handlers->get_wireless_stats)
 		*groups++ = &wireless_group;
 #endif
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index e76539a..45c3d39 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -56,6 +56,9 @@
 #include <linux/wireless.h>
 #include <net/iw_handler.h>
 #endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+#include <net/cfg80211.h>
+#endif
 
 static DEFINE_MUTEX(rtnl_mutex);
 static struct sock *rtnl;
@@ -536,6 +539,20 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 		modified = 1;
 	}
 
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+	if (tb[IFLA_WIRELESS]) {
+		/* Call cfg80211 WE backward compat code.
+		 * Various stuff checked in there... */
+		err = cfg80211_wext_nl_set(dev, nla_data(tb[IFLA_WIRELESS]),
+					   nla_len(tb[IFLA_WIRELESS]));
+		if (err < 0 && err != -ENOSYS)
+			goto errout_dev;
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+		if (err == 0)
+			goto skip_old_wext_nl;
+#endif
+	}
+#endif
 #ifdef CONFIG_NET_WIRELESS_RTNETLINK
 	if (tb[IFLA_WIRELESS]) {
 		/* Call Wireless Extensions.
@@ -545,8 +562,10 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 		if (err < 0)
 			goto errout_dev;
 	}
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+ skip_old_wext_nl:
+#endif
 #endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
-
 	if (tb[IFLA_BROADCAST]) {
 		nla_memcpy(dev->broadcast, tb[IFLA_BROADCAST], dev->addr_len);
 		send_addr_notify = 1;
@@ -611,6 +630,24 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 		return -EINVAL;
 
 
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+	if (tb[IFLA_WIRELESS]) {
+		/* Call Wireless Extensions. We need to know the size before
+		 * we can alloc. Various stuff checked in there... */
+		err = cfg80211_wext_nl_get(dev, nla_data(tb[IFLA_WIRELESS]),
+					   nla_len(tb[IFLA_WIRELESS]),
+					   &iw_buf, &iw_buf_len);
+		if (err < 0 && err != -ENOSYS)
+			goto errout;
+
+		iw += IW_EV_POINT_OFF;
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+		if (err == 0)
+			goto skip_old_wext_nl;
+		iw -= IW_EV_POINT_OFF;
+#endif
+	}
+#endif
 #ifdef CONFIG_NET_WIRELESS_RTNETLINK
 	if (tb[IFLA_WIRELESS]) {
 		/* Call Wireless Extensions. We need to know the size before
@@ -623,6 +660,9 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 
 		iw += IW_EV_POINT_OFF;
 	}
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+ skip_old_wext_nl:
+#endif
 #endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
 
 	nskb = nlmsg_new(if_nlmsg_size(iw_buf_len), GFP_KERNEL);
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index b039edd..f285440 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -4,3 +4,13 @@ cfg80211-objs := \
 	core.o nl80211.o
 
 obj-$(CONFIG_WIRELESS_EXT) += wext-old.o
+
+obj-nn :=
+obj-yy :=
+obj-yn :=
+obj-ny :=
+
+# this needs to be compiled in...
+obj-$(CONFIG_CFG80211_WEXT_COMPAT) += wext-compat.o
+obj-$(CONFIG_CFG80211_WEXT_COMPAT)$(CONFIG_NET_WIRELESS) += wext-common.o
+obj-y += $(obj-yy) $(obj-yn) $(obj-ny)
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 4a10ec2..ebf312a 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -131,7 +131,7 @@ cfg80211_get_drv_from_ifindex(int ifindex)
 	if (drv)
 		mutex_lock(&drv->mtx);
 	else
-		drv = ERR_PTR(-ENODEV);
+		drv = ERR_PTR(-ENOSYS);
 	dev_put(dev);
  out:
 	mutex_unlock(&cfg80211_drv_mutex);
@@ -221,13 +221,23 @@ EXPORT_SYMBOL_GPL(cfg80211_unregister);
 
 static int cfg80211_init(void)
 {
-	/* possibly need to do more later */
+#ifdef CONFIG_CFG80211_WEXT_COMPAT
+	cfg80211_core_ops.get_drv_from_ifidx = cfg80211_get_drv_from_ifindex;
+	cfg80211_core_ops.put_drv = cfg80211_put_drv;
+	cfg80211_core_ops.module = THIS_MODULE;
+	cfg80211_core_ops.loaded = 1;
+#endif
 	return nl80211_init();
 }
 
 static void cfg80211_exit(void)
 {
-	/* possibly need to do more later */
+#ifdef CONFIG_CFG80211_WEXT_COMPAT
+	cfg80211_core_ops.loaded = 0;
+	cfg80211_core_ops.module = NULL;
+	cfg80211_core_ops.get_drv_from_ifidx = NULL;
+	cfg80211_core_ops.put_drv = NULL;
+#endif
 	nl80211_exit();
 }
 
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 562c476..595f184 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -9,6 +9,7 @@
 #include <linux/mutex.h>
 #include <linux/list.h>
 #include <net/genetlink.h>
+#include <linux/module.h>
 
 struct cfg80211_registered_driver {
 	struct cfg80211_ops *ops;
@@ -25,6 +26,26 @@ struct cfg80211_registered_driver {
 extern struct mutex cfg80211_drv_mutex;
 extern struct list_head cfg80211_drv_list;
 
+#ifdef CONFIG_CFG80211_WEXT_COMPAT
+/* wext compatibility must be compiled in...
+ * this extern is in wext-compat.c */
+struct cfg80211_core_ops {
+	/* flag to see if cfg80211 is there.
+	 * FIXME: isn't that racy? */
+	int loaded;
+
+	/* used to make sure the module isn't going away
+	 * can't really happen, except if no driver has cfg80211
+	 * in use, but in that case  */
+	struct module *module;
+
+	/* and finally these are used to do work */
+	struct cfg80211_registered_driver *(*get_drv_from_ifidx)(int ifidx);
+	void (*put_drv)(struct cfg80211_registered_driver *drv);
+};
+extern struct cfg80211_core_ops cfg80211_core_ops;
+#endif
+
 /*
  * This function returns a pointer to the driver
  * that the genl_info item that is passed refers to.
diff --git a/net/wireless/wext-common.c b/net/wireless/wext-common.c
new file mode 100644
index 0000000..d9786db
--- /dev/null
+++ b/net/wireless/wext-common.c
@@ -0,0 +1,610 @@
+/* common wext support routines, proc interface and events */
+
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/wireless.h>
+#include <linux/types.h>
+#include <net/iw_handler.h>
+#include <linux/seq_file.h>
+#include <net/netlink.h>
+#include <linux/rtnetlink.h>
+#include "wext.h"
+
+/* common data */
+/*
+ * Meta-data about all the standard Wireless Extension request we
+ * know about.
+ */
+const struct iw_ioctl_description standard_ioctl[] = {
+	[SIOCSIWCOMMIT	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWNAME	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_CHAR,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWNWID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWNWID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWFREQ	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_FREQ,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWFREQ	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_FREQ,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWMODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_UINT,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWMODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_UINT,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWSENS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWSENS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWRANGE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWRANGE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_range),
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWPRIV	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWPRIV	- SIOCIWFIRST] = { /* (handled directly by us) */
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct iw_priv_args),
+		.max_tokens	= 16,
+		.flags		= IW_DESCR_FLAG_NOMAX,
+	},
+	[SIOCSIWSTATS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWSTATS	- SIOCIWFIRST] = { /* (handled directly by us) */
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_statistics),
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct sockaddr),
+		.max_tokens	= IW_MAX_SPY,
+	},
+	[SIOCGIWSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct sockaddr) +
+				  sizeof(struct iw_quality),
+		.max_tokens	= IW_MAX_SPY,
+	},
+	[SIOCSIWTHRSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct iw_thrspy),
+		.min_tokens	= 1,
+		.max_tokens	= 1,
+	},
+	[SIOCGIWTHRSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct iw_thrspy),
+		.min_tokens	= 1,
+		.max_tokens	= 1,
+	},
+	[SIOCSIWAP	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[SIOCGIWAP	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWMLME	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_mlme),
+		.max_tokens	= sizeof(struct iw_mlme),
+	},
+	[SIOCGIWAPLIST	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct sockaddr) +
+				  sizeof(struct iw_quality),
+		.max_tokens	= IW_MAX_AP,
+		.flags		= IW_DESCR_FLAG_NOMAX,
+	},
+	[SIOCSIWSCAN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= 0,
+		.max_tokens	= sizeof(struct iw_scan_req),
+	},
+	[SIOCGIWSCAN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_SCAN_MAX_DATA,
+		.flags		= IW_DESCR_FLAG_NOMAX,
+	},
+	[SIOCSIWESSID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWESSID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWNICKN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+	},
+	[SIOCGIWNICKN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+	},
+	[SIOCSIWRATE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWRATE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWRTS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWRTS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWFRAG	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWFRAG	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWTXPOW	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWTXPOW	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWRETRY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWRETRY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWENCODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ENCODING_TOKEN_MAX,
+		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
+	},
+	[SIOCGIWENCODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ENCODING_TOKEN_MAX,
+		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
+	},
+	[SIOCSIWPOWER	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWPOWER	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWGENIE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[SIOCGIWGENIE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[SIOCSIWAUTH	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWAUTH	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWENCODEEXT - SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_encode_ext),
+		.max_tokens	= sizeof(struct iw_encode_ext) +
+				  IW_ENCODING_TOKEN_MAX,
+	},
+	[SIOCGIWENCODEEXT - SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_encode_ext),
+		.max_tokens	= sizeof(struct iw_encode_ext) +
+				  IW_ENCODING_TOKEN_MAX,
+	},
+	[SIOCSIWPMKSA - SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_pmksa),
+		.max_tokens	= sizeof(struct iw_pmksa),
+	},
+};
+const unsigned standard_ioctl_num = (sizeof(standard_ioctl) /
+					sizeof(struct iw_ioctl_description));
+
+/*
+ * Meta-data about all the additional standard Wireless Extension events
+ * we know about.
+ */
+const struct iw_ioctl_description standard_event[] = {
+	[IWEVTXDROP	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[IWEVQUAL	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_QUAL,
+	},
+	[IWEVCUSTOM	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_CUSTOM_MAX,
+	},
+	[IWEVREGISTERED	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[IWEVEXPIRED	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[IWEVGENIE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[IWEVMICHAELMICFAILURE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_michaelmicfailure),
+	},
+	[IWEVASSOCREQIE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[IWEVASSOCRESPIE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[IWEVPMKIDCAND	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_pmkid_cand),
+	},
+};
+unsigned standard_event_num = (sizeof(standard_event) /
+					sizeof(struct iw_ioctl_description));
+
+/* Size (in bytes) of various events */
+const int event_type_size[] = {
+	IW_EV_LCP_LEN,			/* IW_HEADER_TYPE_NULL */
+	0,
+	IW_EV_CHAR_LEN,			/* IW_HEADER_TYPE_CHAR */
+	0,
+	IW_EV_UINT_LEN,			/* IW_HEADER_TYPE_UINT */
+	IW_EV_FREQ_LEN,			/* IW_HEADER_TYPE_FREQ */
+	IW_EV_ADDR_LEN,			/* IW_HEADER_TYPE_ADDR */
+	0,
+	IW_EV_POINT_LEN,		/* Without variable payload */
+	IW_EV_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */
+	IW_EV_QUAL_LEN,			/* IW_HEADER_TYPE_QUAL */
+};
+
+
+struct iw_statistics *get_wireless_stats(struct net_device *dev,
+					 struct iw_statistics *out)
+{
+#ifdef CONFIG_CFG80211
+	if (dev->ieee80211_ptr && out) {
+		/* bah, just fake some stuff for now */
+		memset(out, 0, sizeof(*out));
+		return out;
+	}
+#endif
+#ifdef CONFIG_WIRELESS_EXT
+	if ((dev->wireless_handlers != NULL) &&
+	    (dev->wireless_handlers->get_wireless_stats != NULL))
+		return dev->wireless_handlers->get_wireless_stats(dev);
+#endif
+	return NULL;
+}
+
+/*
+ * The /proc/net/wireless file is a human readable user-space interface
+ * exporting various wireless specific statistics from the wireless devices.
+ * This is the most popular part of the Wireless Extensions ;-)
+ *
+ * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
+ * The content of the file is basically the content of "struct iw_statistics".
+ */
+
+#ifdef CONFIG_PROC_FS
+
+/*
+ * Print one entry (line) of /proc/net/wireless
+ */
+static void wireless_seq_printf_stats(struct seq_file *seq,
+				      struct net_device *dev)
+{
+	/* Get stats from the driver */
+	struct iw_statistics stats_buf;
+	struct iw_statistics *stats = get_wireless_stats(dev, &stats_buf);
+
+	if (stats) {
+		seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
+				"%6d %6d   %6d\n",
+			   dev->name, stats->status, stats->qual.qual,
+			   stats->qual.updated & IW_QUAL_QUAL_UPDATED
+			   ? '.' : ' ',
+			   ((__s32) stats->qual.level) -
+			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+			   stats->qual.updated & IW_QUAL_LEVEL_UPDATED
+			   ? '.' : ' ',
+			   ((__s32) stats->qual.noise) -
+			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+			   stats->qual.updated & IW_QUAL_NOISE_UPDATED
+			   ? '.' : ' ',
+			   stats->discard.nwid, stats->discard.code,
+			   stats->discard.fragment, stats->discard.retries,
+			   stats->discard.misc, stats->miss.beacon);
+		stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+	}
+}
+
+/*
+ * Print info for /proc/net/wireless (print all entries)
+ */
+static int wireless_seq_show(struct seq_file *seq, void *v)
+{
+	if (v == SEQ_START_TOKEN)
+		seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
+				"packets               | Missed | WE\n"
+				" face | tus | link level noise |  nwid  "
+				"crypt   frag  retry   misc | beacon | %d\n",
+			   WIRELESS_EXT);
+	else
+		wireless_seq_printf_stats(seq, v);
+	return 0;
+}
+
+static struct seq_operations wireless_seq_ops = {
+	.start = dev_seq_start,
+	.next  = dev_seq_next,
+	.stop  = dev_seq_stop,
+	.show  = wireless_seq_show,
+};
+
+static int wireless_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &wireless_seq_ops);
+}
+
+static struct file_operations wireless_seq_fops = {
+	.owner	 = THIS_MODULE,
+	.open    = wireless_seq_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+int __init wireless_proc_init(void)
+{
+	/* Create /proc/net/wireless entry */
+	if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
+		return -ENOMEM;
+
+	return 0;
+}
+#endif	/* CONFIG_PROC_FS */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Locking...
+ * ----------
+ *
+ * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
+ * the locking issue in here and implementing this code !
+ *
+ * The issue : wireless_send_event() is often called in interrupt context,
+ * while the Netlink layer can never be called in interrupt context.
+ * The fully formed RtNetlink events are queued, and then a tasklet is run
+ * to feed those to Netlink.
+ * The skb_queue is interrupt safe, and its lock is not held while calling
+ * Netlink, so there is no possibility of dealock.
+ * Jean II
+ */
+
+static struct sk_buff_head wireless_nlevent_queue;
+
+static int __init wireless_nlevent_init(void)
+{
+	skb_queue_head_init(&wireless_nlevent_queue);
+	return 0;
+}
+
+subsys_initcall(wireless_nlevent_init);
+
+static void wireless_nlevent_process(unsigned long data)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&wireless_nlevent_queue)))
+		rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
+}
+
+static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
+
+/* ---------------------------------------------------------------- */
+/*
+ * Fill a rtnetlink message with our event data.
+ * Note that we propage only the specified event and don't dump the
+ * current wireless config. Dumping the wireless config is far too
+ * expensive (for each parameter, the driver need to query the hardware).
+ */
+static inline int rtnetlink_fill_iwinfo(struct sk_buff *	skb,
+					struct net_device *	dev,
+					int			type,
+					char *			event,
+					int			event_len)
+{
+	struct ifinfomsg *r;
+	struct nlmsghdr  *nlh;
+	unsigned char	 *b = skb->tail;
+
+	nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
+	r = NLMSG_DATA(nlh);
+	r->ifi_family = AF_UNSPEC;
+	r->__ifi_pad = 0;
+	r->ifi_type = dev->type;
+	r->ifi_index = dev->ifindex;
+	r->ifi_flags = dev_get_flags(dev);
+	r->ifi_change = 0;	/* Wireless changes don't affect those flags */
+
+	/* Add the wireless events in the netlink packet */
+	RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
+
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+	skb_trim(skb, b - skb->data);
+	return -1;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Create and broadcast and send it on the standard rtnetlink socket
+ * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c
+ * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
+ * within a RTM_NEWLINK event.
+ */
+static inline void rtmsg_iwinfo(struct net_device *	dev,
+				char *			event,
+				int			event_len)
+{
+	struct sk_buff *skb;
+	int size = NLMSG_GOODSIZE;
+
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (!skb)
+		return;
+
+	if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
+				  event, event_len) < 0) {
+		kfree_skb(skb);
+		return;
+	}
+	NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
+	skb_queue_tail(&wireless_nlevent_queue, skb);
+	tasklet_schedule(&wireless_nlevent_tasklet);
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main event dispatcher. Called from other parts and drivers.
+ * Send the event on the appropriate channels.
+ * May be called from interrupt context.
+ */
+void wireless_send_event(struct net_device *	dev,
+			 unsigned int		cmd,
+			 union iwreq_data *	wrqu,
+			 char *			extra)
+{
+	const struct iw_ioctl_description *	descr = NULL;
+	int extra_len = 0;
+	struct iw_event  *event;		/* Mallocated whole event */
+	int event_len;				/* Its size */
+	int hdr_len;				/* Size of the event header */
+	int wrqu_off = 0;			/* Offset in wrqu */
+	/* Don't "optimise" the following variable, it will crash */
+	unsigned	cmd_index;		/* *MUST* be unsigned */
+
+	/* Get the description of the Event */
+	if(cmd <= SIOCIWLAST) {
+		cmd_index = cmd - SIOCIWFIRST;
+		if(cmd_index < standard_ioctl_num)
+			descr = &(standard_ioctl[cmd_index]);
+	} else {
+		cmd_index = cmd - IWEVFIRST;
+		if(cmd_index < standard_event_num)
+			descr = &(standard_event[cmd_index]);
+	}
+	/* Don't accept unknown events */
+	if(descr == NULL) {
+		/* Note : we don't return an error to the driver, because
+		 * the driver would not know what to do about it. It can't
+		 * return an error to the user, because the event is not
+		 * initiated by a user request.
+		 * The best the driver could do is to log an error message.
+		 * We will do it ourselves instead...
+		 */
+	  	printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
+		       dev->name, cmd);
+		return;
+	}
+
+	/* Check extra parameters and set extra_len */
+	if(descr->header_type == IW_HEADER_TYPE_POINT) {
+		/* Check if number of token fits within bounds */
+		if(wrqu->data.length > descr->max_tokens) {
+		  	printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
+			return;
+		}
+		if(wrqu->data.length < descr->min_tokens) {
+		  	printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
+			return;
+		}
+		/* Calculate extra_len - extra is NULL for restricted events */
+		if(extra != NULL)
+			extra_len = wrqu->data.length * descr->token_size;
+		/* Always at an offset in wrqu */
+		wrqu_off = IW_EV_POINT_OFF;
+	}
+
+	/* Total length of the event */
+	hdr_len = event_type_size[descr->header_type];
+	event_len = hdr_len + extra_len;
+
+	/* Create temporary buffer to hold the event */
+	event = kmalloc(event_len, GFP_ATOMIC);
+	if(event == NULL)
+		return;
+
+	/* Fill event */
+	event->len = event_len;
+	event->cmd = cmd;
+	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
+	if(extra != NULL)
+		memcpy(((char *) event) + hdr_len, extra, extra_len);
+
+	/* Send via the RtNetlink event channel */
+	rtmsg_iwinfo(dev, (char *) event, event_len);
+
+	/* Cleanup */
+	kfree(event);
+
+	return;		/* Always success, I guess ;-) */
+}
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 1c7c361..0cf6ce9 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -1,25 +1,1633 @@
-/* NOT YET */
+/*
+ * wireless extensions compatibility for cfg80211.
+ *
+ * Lots of code from the original wireless.c:
+ * Copyright 1997-2006	Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ * Copyright 2006	Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPLv2.
+ *
+ * Theory of operation, so to speak:
+ *
+ * To implement compatibility, I added a new field to struct net_device
+ * that contains the pending configuration structure. This is dynamically
+ * allocated when needed and freed when committed.
+ *
+ * Commit is done some time after the last parameter was changed
+ * (with each parameter change simply (re-)schedule a timer) or
+ * if explicitly asked for. This is probably not what most people
+ * would expect, but perfectly fine in the WE API.
+ *
+ * NB: we leak memory if the user
+ *      - changes some settings
+ *      - quickly rmmod's the module so that the net device is destroyed
+ * Since only root can do it and I don't see a way to hook into
+ * the net device's destruction... tough.
+ *
+ * NB2: Note that each of the wrappers should check if the cfg80211
+ * user provides the command, and for configure() it must also check
+ * if that parameter can be set or not via get_config_valid()
+ *
+ * NB3: It's really bad that we can't report an error from the timer-
+ * based commit... Hopefully get_config_valid() can catch everything?
+ *
+ * see set_essid for an example
+ *
+ * another question I just thought about.. does wext expect to see
+ * the new config even if it wasn't committed... if so, we need to
+ * look at the pending config in various _get_ calls...
+ */
 
-To implement compatibility, we add a new field to struct net_device
-that contains the pending configuration structure. This is dynamically
-allocated when needed and freed when committed.
-In a way it replaces the wireless_handlers field in there which is now
-done by dynamic lookup. No worries. No one is going to have thousands
-of wireless devices, and if that changes we can still trivially change
-this assumption :)
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <net/netlink.h>
+#include <asm/uaccess.h>
+#include <net/cfg80211.h>
 
-Commit is done some time after the last parameter was changed
-(with each parameter change simply (re-)schedule a timer) or
-if explicitly asked for. This is probably not what most people
-would expect, but perfectly fine in the WE API.
+#include "core.h"
+#include "wext.h"
 
-compatibility mappings:
+/* The cfg80211 driver assigns callbacks in this
+ * if it is loaded. If not, then we can't config
+ * anything anyway... */
+struct cfg80211_core_ops cfg80211_core_ops;
+EXPORT_SYMBOL_GPL(cfg80211_core_ops);
 
-SIOCSIWAP
-  -> if bssid is all-ones: set roaming to kernel, reassociate
-  -> if bssid is all-zeroes: set roaming to kernel
-  -> otherwise: set roaming to userspace, set bssid
+static struct cfg80211_registered_driver *cfg80211_wx_setup(int ifindex)
+{
+	if (!cfg80211_core_ops.loaded)
+		return ERR_PTR(-ENOSYS);
+	if (!try_module_get(cfg80211_core_ops.module))
+		return ERR_PTR(-ENOSYS);
 
-SIOCGIWAP
-  -> get association parameters and fill return bssid appropriately
+	return cfg80211_core_ops.get_drv_from_ifidx(ifindex);
+}
 
+static void cfg80211_wx_teardown(struct cfg80211_registered_driver *drv)
+{
+	if (!IS_ERR(drv))
+		cfg80211_core_ops.put_drv(drv);
+	module_put(cfg80211_core_ops.module);
+}
+
+/* internal API: use this function when changing
+ * some parameter that needs to be committed */
+static void cfg80211_wx_start_commit_timer(int ifindex)
+{
+	/* TODO:
+	 * start a timer associate with this interface
+	 * and then when it expires commit the pending
+	 * data...
+	 * This function must be callable when the timer
+	 * is already running, and the timer must
+	 * be able to deal with an unassigned
+	 * dev->cfg80211_wext_pending_config pointer
+	 * as well as taking the rtnl lock (due to wext)! */
+}
+
+static void cfg80211_ensure_netdev_pending_cfg(struct net_device *dev)
+{
+	struct cfg80211_config *cfg = dev->cfg80211_wext_pending_config;
+	if (!cfg) {
+		cfg = kmalloc(sizeof(*cfg)+32, GFP_KERNEL);
+		cfg->ssid = (char*)cfg + sizeof(*cfg);
+		dev->cfg80211_wext_pending_config = cfg;
+	}
+}
+
+/* operations we implement. whew, I machine-generated these */
+static int cfg80211_wx_set_commit(struct net_device *net_dev,
+				  struct iw_request_info *info,
+				  union iwreq_data *data,
+				  char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	if (!net_dev->cfg80211_wext_pending_config) {
+		err = 0;
+		goto out;
+	}
+
+	err = drv->ops->configure(drv->priv, net_dev,
+				  net_dev->cfg80211_wext_pending_config);
+
+	kfree(net_dev->cfg80211_wext_pending_config);
+	net_dev->cfg80211_wext_pending_config = NULL;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_name(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_nwid(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_nwid(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_freq(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_freq(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_mode(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_mode(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_sens(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_sens(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_range(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_range(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_ap(struct net_device *net_dev,
+			      struct iw_request_info *info,
+			      union iwreq_data *data,
+			      char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	/* SIOCSIWAP
+	 *   -> if bssid is all-ones: set roaming to kernel, reassociate
+	 *   -> if bssid is all-zeroes: set roaming to kernel
+	 *   -> otherwise: set roaming to userspace, set bssid
+	 */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_ap(struct net_device *net_dev,
+			      struct iw_request_info *info,
+			      union iwreq_data *data,
+			      char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	/* SIOCGIWAP
+	 *   -> get association parameters and fill return bssid appropriately
+	 */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_mlme(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_waplist(struct net_device *net_dev,
+				   struct iw_request_info *info,
+				   union iwreq_data *data,
+				   char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_scan(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_scan(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_essid(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct cfg80211_config *cfg;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	err = -ENOSYS;
+	if (!drv->ops->configure || !drv->ops->get_config_valid)
+		goto out;
+	if (!(drv->ops->get_config_valid(drv->priv, net_dev)
+			& CFG80211_CFG_VALID_SSID))
+		goto out;
+
+	cfg80211_ensure_netdev_pending_cfg(net_dev);
+	cfg = net_dev->cfg80211_wext_pending_config;
+	if (!cfg) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	memcpy(cfg->ssid, extra, data->essid.length);
+	cfg->ssid_len = data->essid.length;
+	cfg->valid |= CFG80211_CFG_VALID_SSID;
+
+	cfg80211_wx_start_commit_timer(net_dev->ifindex);
+	err = 0;
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_essid(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_rate(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_rate(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_rts(struct net_device *net_dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *data,
+			       char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_rts(struct net_device *net_dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *data,
+			       char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_frag(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_frag(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_txpow(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_txpow(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_retry(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_retry(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_encode(struct net_device *net_dev,
+				  struct iw_request_info *info,
+				  union iwreq_data *data,
+				  char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_encode(struct net_device *net_dev,
+				  struct iw_request_info *info,
+				  union iwreq_data *data,
+				  char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_power(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_power(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_genie(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_genie(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_auth(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_auth(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_encodeext(struct net_device *net_dev,
+				     struct iw_request_info *info,
+				     union iwreq_data *data,
+				     char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_encodeext(struct net_device *net_dev,
+				     struct iw_request_info *info,
+				     union iwreq_data *data,
+				     char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_wpmksa(struct net_device *net_dev,
+				  struct iw_request_info *info,
+				  union iwreq_data *data,
+				  char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+
+
+/* operations array */
+#ifdef WX
+# undef WX
+#endif
+#define WX(ioctl)  [(ioctl) - SIOCIWFIRST]
+static const iw_handler cfg80211_wx_handlers[] = {
+	WX(SIOCSIWCOMMIT)	= cfg80211_wx_set_commit,
+	WX(SIOCGIWNAME)		= cfg80211_wx_get_name,
+	WX(SIOCSIWNWID)		= cfg80211_wx_set_nwid,
+	WX(SIOCGIWNWID)		= cfg80211_wx_get_nwid,
+	WX(SIOCSIWFREQ)		= cfg80211_wx_set_freq,
+	WX(SIOCGIWFREQ)		= cfg80211_wx_get_freq,
+	WX(SIOCSIWMODE)		= cfg80211_wx_set_mode,
+	WX(SIOCGIWMODE)		= cfg80211_wx_get_mode,
+	WX(SIOCSIWSENS)		= cfg80211_wx_set_sens,
+	WX(SIOCGIWSENS)		= cfg80211_wx_get_sens,
+	WX(SIOCSIWRANGE)	= cfg80211_wx_set_range,
+	WX(SIOCGIWRANGE)	= cfg80211_wx_get_range,
+	WX(SIOCSIWAP)		= cfg80211_wx_set_ap,
+	WX(SIOCGIWAP)		= cfg80211_wx_get_ap,
+	WX(SIOCSIWMLME)		= cfg80211_wx_set_mlme,
+	WX(SIOCGIWAPLIST)	= cfg80211_wx_get_waplist,
+	WX(SIOCSIWSCAN)		= cfg80211_wx_set_scan,
+	WX(SIOCGIWSCAN)		= cfg80211_wx_get_scan,
+	WX(SIOCSIWESSID)	= cfg80211_wx_set_essid,
+	WX(SIOCGIWESSID)	= cfg80211_wx_get_essid,
+	WX(SIOCSIWRATE)		= cfg80211_wx_set_rate,
+	WX(SIOCGIWRATE)		= cfg80211_wx_get_rate,
+	WX(SIOCSIWRTS)		= cfg80211_wx_set_rts,
+	WX(SIOCGIWRTS)		= cfg80211_wx_get_rts,
+	WX(SIOCSIWFRAG)		= cfg80211_wx_set_frag,
+	WX(SIOCGIWFRAG)		= cfg80211_wx_get_frag,
+	WX(SIOCSIWTXPOW)	= cfg80211_wx_set_txpow,
+	WX(SIOCGIWTXPOW)	= cfg80211_wx_get_txpow,
+	WX(SIOCSIWRETRY)	= cfg80211_wx_set_retry,
+	WX(SIOCGIWRETRY)	= cfg80211_wx_get_retry,
+	WX(SIOCSIWENCODE)	= cfg80211_wx_set_encode,
+	WX(SIOCGIWENCODE)	= cfg80211_wx_get_encode,
+	WX(SIOCSIWPOWER)	= cfg80211_wx_set_power,
+	WX(SIOCGIWPOWER)	= cfg80211_wx_get_power,
+	WX(SIOCSIWGENIE)	= cfg80211_wx_set_genie,
+	WX(SIOCGIWGENIE)	= cfg80211_wx_get_genie,
+	WX(SIOCSIWAUTH)		= cfg80211_wx_set_auth,
+	WX(SIOCGIWAUTH)		= cfg80211_wx_get_auth,
+	WX(SIOCSIWENCODEEXT)	= cfg80211_wx_set_encodeext,
+	WX(SIOCGIWENCODEEXT)	= cfg80211_wx_get_encodeext,
+	WX(SIOCSIWPMKSA)	= cfg80211_wx_set_wpmksa,
+};
+
+/* dummy so I didn't have to change that much code... */
+static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
+{
+	int idx = cmd - SIOCIWFIRST;
+	if (idx < ARRAY_SIZE(cfg80211_wx_handlers))
+		return cfg80211_wx_handlers[idx];
+	return NULL;
+}
+
+/*
+ * this is sort of backwards and wouldn't need to call
+ * get_wireless_stats, but it was easier to just copy the code...
+ */
+static int iw_handler_get_iwstats(struct net_device *		dev,
+				  struct iw_request_info *	info,
+				  union iwreq_data *		wrqu,
+				  char *			extra)
+{
+	/* Get stats from the driver */
+	struct iw_statistics stats_buf;
+	struct iw_statistics *stats;
+
+	stats = get_wireless_stats(dev, &stats_buf);
+	if (stats != (struct iw_statistics *) NULL) {
+
+		/* Copy statistics to extra */
+		memcpy(extra, stats, sizeof(struct iw_statistics));
+		wrqu->data.length = sizeof(struct iw_statistics);
+
+		/* Check if we need to clear the updated flag */
+		if(wrqu->data.flags != 0)
+			stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+		return 0;
+	} else
+		return -EOPNOTSUPP;
+}
+
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+/*
+ * Wrapper to call a standard Wireless Extension GET handler.
+ * We do various checks and call the handler with the proper args.
+ */
+static int rtnetlink_standard_get(struct net_device *	dev,
+				  struct iw_event *	request,
+				  int			request_len,
+				  iw_handler		handler,
+				  char **		p_buf,
+				  int *			p_len)
+{
+	const struct iw_ioctl_description *	descr = NULL;
+	unsigned int				cmd;
+	union iwreq_data *			wrqu;
+	int					hdr_len;
+	struct iw_request_info			info;
+	char *					buffer = NULL;
+	int					buffer_size = 0;
+	int					ret = -EINVAL;
+
+	/* Get the description of the Request */
+	cmd = request->cmd;
+	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+		return -EOPNOTSUPP;
+	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+	/* Check if wrqu is complete */
+	hdr_len = event_type_size[descr->header_type];
+	if(request_len < hdr_len)
+		return -EINVAL;
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have extra data in the reply or not */
+	if(descr->header_type != IW_HEADER_TYPE_POINT) {
+
+		/* Create the kernel buffer that we will return.
+		 * It's at an offset to match the TYPE_POINT case... */
+		buffer_size = request_len + IW_EV_POINT_OFF;
+		buffer = kmalloc(buffer_size, GFP_KERNEL);
+		if (buffer == NULL) {
+			return -ENOMEM;
+		}
+		/* Copy event data */
+		memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
+		/* Use our own copy of wrqu */
+		wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
+					     + IW_EV_LCP_LEN);
+
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, wrqu, NULL);
+
+	} else {
+		union iwreq_data	wrqu_point;
+		char *			extra = NULL;
+		int			extra_size = 0;
+
+		/* Get a temp copy of wrqu (skip pointer) */
+		memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
+		       ((char *) request) + IW_EV_LCP_LEN,
+		       IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+
+		/* Calculate space needed by arguments. Always allocate
+		 * for max space. Easier, and won't last long... */
+		extra_size = descr->max_tokens * descr->token_size;
+		/* Support for very large requests */
+		if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+		   (wrqu_point.data.length > descr->max_tokens))
+			extra_size = (wrqu_point.data.length
+				      * descr->token_size);
+		buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
+
+		/* Create the kernel buffer that we will return */
+		buffer = kmalloc(buffer_size, GFP_KERNEL);
+		if (buffer == NULL) {
+			return -ENOMEM;
+		}
+
+		/* Put wrqu in the right place (just before extra).
+		 * Leave space for IWE header and dummy pointer...
+		 * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
+		 */
+		memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
+		       ((char *) &wrqu_point) + IW_EV_POINT_OFF,
+		       IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+		wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
+
+		/* Extra comes logically after that. Offset +12 bytes. */
+		extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
+
+		/* Call the handler */
+		ret = handler(dev, &info, wrqu, extra);
+
+		/* Calculate real returned length */
+		extra_size = (wrqu->data.length * descr->token_size);
+		/* Re-adjust reply size */
+		request->len = extra_size + IW_EV_POINT_LEN;
+
+		/* Put the iwe header where it should, i.e. scrap the
+		 * dummy pointer. */
+		memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
+
+		/* Check if there is enough buffer up there */
+		if(wrqu_point.data.length < wrqu->data.length)
+			ret = -E2BIG;
+	}
+
+	/* Return the buffer to the caller */
+	if (!ret) {
+		*p_buf = buffer;
+		*p_len = request->len;
+	} else {
+		/* Cleanup */
+		if(buffer)
+			kfree(buffer);
+	}
+
+	return ret;
+}
+
+/*
+ * Wrapper to call a standard Wireless Extension SET handler.
+ * We do various checks and call the handler with the proper args.
+ */
+static inline int rtnetlink_standard_set(struct net_device *	dev,
+					 struct iw_event *	request,
+					 int			request_len,
+					 iw_handler		handler)
+{
+	const struct iw_ioctl_description *	descr = NULL;
+	unsigned int				cmd;
+	union iwreq_data *			wrqu;
+	union iwreq_data			wrqu_point;
+	int					hdr_len;
+	char *					extra = NULL;
+	int					extra_size = 0;
+	struct iw_request_info			info;
+	int					ret = -EINVAL;
+
+	/* Get the description of the Request */
+	cmd = request->cmd;
+	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+		return -EOPNOTSUPP;
+	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+	/* Extract fixed header from request. This is properly aligned. */
+	wrqu = &request->u;
+
+	/* Check if wrqu is complete */
+	hdr_len = event_type_size[descr->header_type];
+	if(request_len < hdr_len)
+		return -EINVAL;
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have extra data in the request or not */
+	if(descr->header_type != IW_HEADER_TYPE_POINT) {
+
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, wrqu, NULL);
+
+	} else {
+		int	extra_len;
+
+		/* Put wrqu in the right place (skip pointer) */
+		memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
+		       wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+		/* Don't forget about the event code... */
+		wrqu = &wrqu_point;
+
+		/* Check if number of token fits within bounds */
+		if(wrqu_point.data.length > descr->max_tokens)
+			return -E2BIG;
+		if(wrqu_point.data.length < descr->min_tokens)
+			return -EINVAL;
+
+		/* Real length of payload */
+		extra_len = wrqu_point.data.length * descr->token_size;
+
+		/* Check if request is self consistent */
+		if((request_len - hdr_len) < extra_len)
+			return -EINVAL;
+
+		/* Always allocate for max space. Easier, and won't last
+		 * long... */
+		extra_size = descr->max_tokens * descr->token_size;
+		extra = kmalloc(extra_size, GFP_KERNEL);
+		if (extra == NULL)
+			return -ENOMEM;
+
+		/* Copy extra in aligned buffer */
+		memcpy(extra, ((char *) request) + hdr_len, extra_len);
+
+		/* Call the handler */
+		ret = handler(dev, &info, &wrqu_point, extra);
+	}
+
+        /* Generate an event to notify listeners of the change */
+	if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
+	    ((ret == 0) || (ret == -EIWCOMMIT))) {
+		if(descr->flags & IW_DESCR_FLAG_RESTRICT)
+			/* If the event is restricted, don't
+			 * export the payload */
+			wireless_send_event(dev, cmd, wrqu, NULL);
+		else
+			wireless_send_event(dev, cmd, wrqu, extra);
+	}
+
+	/* Cleanup - I told you it wasn't that long ;-) */
+	if(extra)
+		kfree(extra);
+
+	return ret;
+}
+
+/*
+ * Main RtNetlink dispatcher. Called from the main networking code
+ * (do_getlink() in net/core/rtnetlink.c).
+ * Check the type of Request and call the appropriate wrapper...
+ */
+int cfg80211_wext_nl_get(struct net_device *	dev,
+			 char *			data,
+			 int			len,
+			 char **		p_buf,
+			 int *			p_len)
+{
+	struct iw_event *	request = (struct iw_event *) data;
+	iw_handler		handler;
+
+	/* Check length */
+	if(len < IW_EV_LCP_LEN) {
+		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
+		       dev->name, len);
+		return -EINVAL;
+	}
+
+	/* ReCheck length (len may have padding) */
+	if(request->len > len) {
+		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
+		       dev->name, request->len, len);
+		return -EINVAL;
+	}
+
+	/* Only accept GET requests in here */
+	if(!IW_IS_GET(request->cmd))
+		return -EOPNOTSUPP;
+
+	/* If command is `get the encoding parameters', check if
+	 * the user has the right to do it */
+	if (request->cmd == SIOCGIWENCODE ||
+	    request->cmd == SIOCGIWENCODEEXT) {
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+	}
+
+	/* Special cases */
+	if(request->cmd == SIOCGIWSTATS)
+		/* Get Wireless Stats */
+		return rtnetlink_standard_get(dev,
+					      request,
+					      request->len,
+					      &iw_handler_get_iwstats,
+					      p_buf, p_len);
+	if(request->cmd == SIOCGIWPRIV)
+		return -EOPNOTSUPP;
+
+	/* Basic check */
+	if (!netif_device_present(dev))
+		return -ENODEV;
+
+	/* Try to find the handler */
+	handler = get_handler(dev, request->cmd);
+	if (handler != NULL && request->cmd < SIOCIWFIRSTPRIV)
+		return rtnetlink_standard_get(dev,
+					      request,
+					      request->len,
+					      handler,
+					      p_buf, p_len);
+
+	return -EOPNOTSUPP;
+}
+
+/*
+ * Main RtNetlink dispatcher. Called from the main networking code
+ * (do_setlink() in net/core/rtnetlink.c).
+ * Check the type of Request and call the appropriate wrapper...
+ */
+int cfg80211_wext_nl_set(struct net_device *	dev,
+			 char *			data,
+			 int			len)
+{
+	struct iw_event *	request = (struct iw_event *) data;
+	iw_handler		handler;
+
+	/* Check length */
+	if(len < IW_EV_LCP_LEN) {
+		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
+		       dev->name, len);
+		return -EINVAL;
+	}
+
+	/* ReCheck length (len may have padding) */
+	if(request->len > len) {
+		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
+		       dev->name, request->len, len);
+		return -EINVAL;
+	}
+
+	/* Only accept SET requests in here */
+	if(!IW_IS_SET(request->cmd))
+		return -EOPNOTSUPP;
+
+	/* Basic check */
+	if (!netif_device_present(dev))
+		return -ENODEV;
+
+	/* New driver API : try to find the handler */
+	handler = get_handler(dev, request->cmd);
+	if(handler != NULL && request->cmd < SIOCIWFIRSTPRIV)
+		return rtnetlink_standard_set(dev,
+					      request,
+					      request->len,
+					      handler);
+
+	return -EOPNOTSUPP;
+}
+#endif
+
+/*
+ * Wrapper to call a standard Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ */
+static int ioctl_standard_call(struct net_device *	dev,
+			       struct ifreq *		ifr,
+			       unsigned int		cmd,
+			       iw_handler		handler)
+{
+	struct iwreq *				iwr = (struct iwreq *) ifr;
+	const struct iw_ioctl_description *	descr;
+	struct iw_request_info			info;
+	int					ret = -EINVAL;
+
+	/* Get the description of the IOCTL */
+	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+		return -EOPNOTSUPP;
+	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have a pointer to user space data or not */
+	if(descr->header_type != IW_HEADER_TYPE_POINT) {
+
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, &(iwr->u), NULL);
+
+		/* Generate an event to notify listeners of the change */
+		if((descr->flags & IW_DESCR_FLAG_EVENT) &&
+		   ((ret == 0) || (ret == -EIWCOMMIT)))
+			wireless_send_event(dev, cmd, &(iwr->u), NULL);
+	} else {
+		char *	extra;
+		int	extra_size;
+		int	user_length = 0;
+		int	err;
+
+		/* Calculate space needed by arguments. Always allocate
+		 * for max space. Easier, and won't last long... */
+		extra_size = descr->max_tokens * descr->token_size;
+
+		/* Check what user space is giving us */
+		if(IW_IS_SET(cmd)) {
+			/* Check NULL pointer */
+			if((iwr->u.data.pointer == NULL) &&
+			   (iwr->u.data.length != 0))
+				return -EFAULT;
+			/* Check if number of token fits within bounds */
+			if(iwr->u.data.length > descr->max_tokens)
+				return -E2BIG;
+			if(iwr->u.data.length < descr->min_tokens)
+				return -EINVAL;
+		} else {
+			/* Check NULL pointer */
+			if(iwr->u.data.pointer == NULL)
+				return -EFAULT;
+			/* Save user space buffer size for checking */
+			user_length = iwr->u.data.length;
+
+			/* Don't check if user_length > max to allow forward
+			 * compatibility. The test user_length < min is
+			 * implied by the test at the end. */
+
+			/* Support for very large requests */
+			if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+			   (user_length > descr->max_tokens)) {
+				/* Allow userspace to GET more than max so
+				 * we can support any size GET requests.
+				 * There is still a limit : -ENOMEM. */
+				extra_size = user_length * descr->token_size;
+				/* Note : user_length is originally a __u16,
+				 * and token_size is controlled by us,
+				 * so extra_size won't get negative and
+				 * won't overflow... */
+			}
+		}
+
+		/* Create the kernel buffer */
+		extra = kmalloc(extra_size, GFP_KERNEL);
+		if (extra == NULL) {
+			return -ENOMEM;
+		}
+
+		/* If it is a SET, get all the extra data in here */
+		if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+			err = copy_from_user(extra, iwr->u.data.pointer,
+					     iwr->u.data.length *
+					     descr->token_size);
+			if (err) {
+				kfree(extra);
+				return -EFAULT;
+			}
+		}
+
+		/* Call the handler */
+		ret = handler(dev, &info, &(iwr->u), extra);
+
+		/* If we have something to return to the user */
+		if (!ret && IW_IS_GET(cmd)) {
+			/* Check if there is enough buffer up there */
+			if(user_length < iwr->u.data.length) {
+				kfree(extra);
+				return -E2BIG;
+			}
+
+			err = copy_to_user(iwr->u.data.pointer, extra,
+					   iwr->u.data.length *
+					   descr->token_size);
+			if (err)
+				ret =  -EFAULT;
+		}
+
+		/* Generate an event to notify listeners of the change */
+		if((descr->flags & IW_DESCR_FLAG_EVENT) &&
+		   ((ret == 0) || (ret == -EIWCOMMIT))) {
+			if(descr->flags & IW_DESCR_FLAG_RESTRICT)
+				/* If the event is restricted, don't
+				 * export the payload */
+				wireless_send_event(dev, cmd, &(iwr->u), NULL);
+			else
+				wireless_send_event(dev, cmd, &(iwr->u),
+						    extra);
+		}
+
+		/* Cleanup - I told you it wasn't that long ;-) */
+		kfree(extra);
+	}
+
+	return ret;
+}
+
+/* and finally the ioctl wrapper */
+int cfg80211_wext_ioctl(struct ifreq *ifr, unsigned int cmd)
+{
+	struct net_device *dev;
+	iw_handler	handler;
+
+	/* Permissions are already checked in dev_ioctl() before calling us.
+	 * The copy_to/from_user() of ifr is also dealt with in there */
+
+	/* Make sure the device exist */
+	if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
+		return -ENODEV;
+
+	/* A bunch of special cases, then the generic case...
+	 * Note that 'cmd' is already filtered in dev_ioctl() with
+	 * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
+	switch(cmd) {
+		case SIOCGIWSTATS:
+			/* Get Wireless Stats */
+			return ioctl_standard_call(dev,
+						   ifr,
+						   cmd,
+						   &iw_handler_get_iwstats);
+
+		case SIOCGIWPRIV:
+			return -EOPNOTSUPP;
+		default:
+			/* Basic check */
+			if (!netif_device_present(dev))
+				return -ENODEV;
+			handler = get_handler(dev, cmd);
+			if(cmd < SIOCIWFIRSTPRIV && handler != NULL)
+				return ioctl_standard_call(dev, ifr, cmd,
+							   handler);
+			return -EOPNOTSUPP;
+	}
+	return -EINVAL;
+}
diff --git a/net/wireless/wext-old.c b/net/wireless/wext-old.c
index f69ab7b..9892396 100644
--- a/net/wireless/wext-old.c
+++ b/net/wireless/wext-old.c
@@ -83,9 +83,7 @@
 #include <linux/module.h>
 #include <linux/types.h>		/* off_t */
 #include <linux/netdevice.h>		/* struct ifreq, dev_get_by_name() */
-#include <linux/proc_fs.h>
 #include <linux/rtnetlink.h>		/* rtnetlink stuff */
-#include <linux/seq_file.h>
 #include <linux/init.h>			/* for __init */
 #include <linux/if_arp.h>		/* ARPHRD_ETHER */
 #include <linux/etherdevice.h>		/* compare_ether_addr */
@@ -97,6 +95,8 @@
 
 #include <asm/uaccess.h>		/* copy_to_user() */
 
+#include "wext.h"
+
 /**************************** CONSTANTS ****************************/
 
 /* Debugging stuff */
@@ -111,294 +111,6 @@
 #define WE_SET_EVENT		/* Generate an event on some set commands */
 
 /************************* GLOBAL VARIABLES *************************/
-/*
- * You should not use global variables, because of re-entrancy.
- * On our case, it's only const, so it's OK...
- */
-/*
- * Meta-data about all the standard Wireless Extension request we
- * know about.
- */
-static const struct iw_ioctl_description standard_ioctl[] = {
-	[SIOCSIWCOMMIT	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWNAME	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_CHAR,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWNWID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWNWID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWFREQ	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_FREQ,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWFREQ	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_FREQ,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWMODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_UINT,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWMODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_UINT,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWSENS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWSENS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWRANGE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWRANGE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_range),
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWPRIV	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWPRIV	- SIOCIWFIRST] = { /* (handled directly by us) */
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct iw_priv_args),
-		.max_tokens	= 16,
-		.flags		= IW_DESCR_FLAG_NOMAX,
-	},
-	[SIOCSIWSTATS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWSTATS	- SIOCIWFIRST] = { /* (handled directly by us) */
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_statistics),
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct sockaddr),
-		.max_tokens	= IW_MAX_SPY,
-	},
-	[SIOCGIWSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct sockaddr) +
-				  sizeof(struct iw_quality),
-		.max_tokens	= IW_MAX_SPY,
-	},
-	[SIOCSIWTHRSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct iw_thrspy),
-		.min_tokens	= 1,
-		.max_tokens	= 1,
-	},
-	[SIOCGIWTHRSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct iw_thrspy),
-		.min_tokens	= 1,
-		.max_tokens	= 1,
-	},
-	[SIOCSIWAP	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-	},
-	[SIOCGIWAP	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWMLME	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_mlme),
-		.max_tokens	= sizeof(struct iw_mlme),
-	},
-	[SIOCGIWAPLIST	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct sockaddr) +
-				  sizeof(struct iw_quality),
-		.max_tokens	= IW_MAX_AP,
-		.flags		= IW_DESCR_FLAG_NOMAX,
-	},
-	[SIOCSIWSCAN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= 0,
-		.max_tokens	= sizeof(struct iw_scan_req),
-	},
-	[SIOCGIWSCAN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_SCAN_MAX_DATA,
-		.flags		= IW_DESCR_FLAG_NOMAX,
-	},
-	[SIOCSIWESSID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWESSID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWNICKN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-	},
-	[SIOCGIWNICKN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-	},
-	[SIOCSIWRATE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWRATE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWRTS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWRTS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWFRAG	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWFRAG	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWTXPOW	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWTXPOW	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWRETRY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWRETRY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWENCODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ENCODING_TOKEN_MAX,
-		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
-	},
-	[SIOCGIWENCODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ENCODING_TOKEN_MAX,
-		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
-	},
-	[SIOCSIWPOWER	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWPOWER	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWGENIE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[SIOCGIWGENIE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[SIOCSIWAUTH	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWAUTH	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWENCODEEXT - SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_encode_ext),
-		.max_tokens	= sizeof(struct iw_encode_ext) +
-				  IW_ENCODING_TOKEN_MAX,
-	},
-	[SIOCGIWENCODEEXT - SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_encode_ext),
-		.max_tokens	= sizeof(struct iw_encode_ext) +
-				  IW_ENCODING_TOKEN_MAX,
-	},
-	[SIOCSIWPMKSA - SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_pmksa),
-		.max_tokens	= sizeof(struct iw_pmksa),
-	},
-};
-static const unsigned standard_ioctl_num = (sizeof(standard_ioctl) /
-					    sizeof(struct iw_ioctl_description));
-
-/*
- * Meta-data about all the additional standard Wireless Extension events
- * we know about.
- */
-static const struct iw_ioctl_description standard_event[] = {
-	[IWEVTXDROP	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-	},
-	[IWEVQUAL	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_QUAL,
-	},
-	[IWEVCUSTOM	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_CUSTOM_MAX,
-	},
-	[IWEVREGISTERED	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-	},
-	[IWEVEXPIRED	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR, 
-	},
-	[IWEVGENIE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[IWEVMICHAELMICFAILURE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT, 
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_michaelmicfailure),
-	},
-	[IWEVASSOCREQIE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[IWEVASSOCRESPIE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[IWEVPMKIDCAND	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_pmkid_cand),
-	},
-};
-static const unsigned standard_event_num = (sizeof(standard_event) /
-					    sizeof(struct iw_ioctl_description));
 
 /* Size (in bytes) of the various private data types */
 static const char iw_priv_type_size[] = {
@@ -412,21 +124,6 @@ static const char iw_priv_type_size[] = {
 	0,				/* Not defined */
 };
 
-/* Size (in bytes) of various events */
-static const int event_type_size[] = {
-	IW_EV_LCP_LEN,			/* IW_HEADER_TYPE_NULL */
-	0,
-	IW_EV_CHAR_LEN,			/* IW_HEADER_TYPE_CHAR */
-	0,
-	IW_EV_UINT_LEN,			/* IW_HEADER_TYPE_UINT */
-	IW_EV_FREQ_LEN,			/* IW_HEADER_TYPE_FREQ */
-	IW_EV_ADDR_LEN,			/* IW_HEADER_TYPE_ADDR */
-	0,
-	IW_EV_POINT_LEN,		/* Without variable payload */
-	IW_EV_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */
-	IW_EV_QUAL_LEN,			/* IW_HEADER_TYPE_QUAL */
-};
-
 /************************ COMMON SUBROUTINES ************************/
 /*
  * Stuff that may be used in various place or doesn't fit in one
@@ -464,21 +161,6 @@ static inline iw_handler get_handler(struct net_device *dev,
 
 /* ---------------------------------------------------------------- */
 /*
- * Get statistics out of the driver
- */
-static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
-{
-	/* New location */
-	if((dev->wireless_handlers != NULL) &&
-	   (dev->wireless_handlers->get_wireless_stats != NULL))
-		return dev->wireless_handlers->get_wireless_stats(dev);
-
-	/* Not found */
-	return (struct iw_statistics *) NULL;
-}
-
-/* ---------------------------------------------------------------- */
-/*
  * Call the commit handler in the driver
  * (if exist and if conditions are right)
  *
@@ -551,7 +233,7 @@ static int iw_handler_get_iwstats(struct net_device *		dev,
 	/* Get stats from the driver */
 	struct iw_statistics *stats;
 
-	stats = get_wireless_stats(dev);
+	stats = get_wireless_stats(dev, NULL);
 	if (stats != (struct iw_statistics *) NULL) {
 
 		/* Copy statistics to extra */
@@ -601,97 +283,6 @@ static int iw_handler_get_private(struct net_device *		dev,
 	return 0;
 }
 
-
-/******************** /proc/net/wireless SUPPORT ********************/
-/*
- * The /proc/net/wireless file is a human readable user-space interface
- * exporting various wireless specific statistics from the wireless devices.
- * This is the most popular part of the Wireless Extensions ;-)
- *
- * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
- * The content of the file is basically the content of "struct iw_statistics".
- */
-
-#ifdef CONFIG_PROC_FS
-
-/* ---------------------------------------------------------------- */
-/*
- * Print one entry (line) of /proc/net/wireless
- */
-static __inline__ void wireless_seq_printf_stats(struct seq_file *seq,
-						 struct net_device *dev)
-{
-	/* Get stats from the driver */
-	struct iw_statistics *stats = get_wireless_stats(dev);
-
-	if (stats) {
-		seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
-				"%6d %6d   %6d\n",
-			   dev->name, stats->status, stats->qual.qual,
-			   stats->qual.updated & IW_QUAL_QUAL_UPDATED
-			   ? '.' : ' ',
-			   ((__s32) stats->qual.level) - 
-			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
-			   stats->qual.updated & IW_QUAL_LEVEL_UPDATED
-			   ? '.' : ' ',
-			   ((__s32) stats->qual.noise) - 
-			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
-			   stats->qual.updated & IW_QUAL_NOISE_UPDATED
-			   ? '.' : ' ',
-			   stats->discard.nwid, stats->discard.code,
-			   stats->discard.fragment, stats->discard.retries,
-			   stats->discard.misc, stats->miss.beacon);
-		stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
-	}
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Print info for /proc/net/wireless (print all entries)
- */
-static int wireless_seq_show(struct seq_file *seq, void *v)
-{
-	if (v == SEQ_START_TOKEN)
-		seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
-				"packets               | Missed | WE\n"
-				" face | tus | link level noise |  nwid  "
-				"crypt   frag  retry   misc | beacon | %d\n",
-			   WIRELESS_EXT);
-	else
-		wireless_seq_printf_stats(seq, v);
-	return 0;
-}
-
-static struct seq_operations wireless_seq_ops = {
-	.start = dev_seq_start,
-	.next  = dev_seq_next,
-	.stop  = dev_seq_stop,
-	.show  = wireless_seq_show,
-};
-
-static int wireless_seq_open(struct inode *inode, struct file *file)
-{
-	return seq_open(file, &wireless_seq_ops);
-}
-
-static struct file_operations wireless_seq_fops = {
-	.owner	 = THIS_MODULE,
-	.open    = wireless_seq_open,
-	.read    = seq_read,
-	.llseek  = seq_lseek,
-	.release = seq_release,
-};
-
-int __init wireless_proc_init(void)
-{
-	/* Create /proc/net/wireless entry */
-	if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
-		return -ENOMEM;
-
-	return 0;
-}
-#endif	/* CONFIG_PROC_FS */
-
 /************************** IOCTL SUPPORT **************************/
 /*
  * The original user space API to configure all those Wireless Extensions
@@ -1863,220 +1454,6 @@ int wireless_rtnetlink_set(struct net_device *	dev,
 }
 #endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
 
-
-/************************* EVENT PROCESSING *************************/
-/*
- * Process events generated by the wireless layer or the driver.
- * Most often, the event will be propagated through rtnetlink
- */
-
-#ifdef WE_EVENT_RTNETLINK
-/* ---------------------------------------------------------------- */
-/*
- * Locking...
- * ----------
- *
- * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
- * the locking issue in here and implementing this code !
- *
- * The issue : wireless_send_event() is often called in interrupt context,
- * while the Netlink layer can never be called in interrupt context.
- * The fully formed RtNetlink events are queued, and then a tasklet is run
- * to feed those to Netlink.
- * The skb_queue is interrupt safe, and its lock is not held while calling
- * Netlink, so there is no possibility of dealock.
- * Jean II
- */
-
-static struct sk_buff_head wireless_nlevent_queue;
-
-static int __init wireless_nlevent_init(void)
-{
-	skb_queue_head_init(&wireless_nlevent_queue);
-	return 0;
-}
-
-subsys_initcall(wireless_nlevent_init);
-
-static void wireless_nlevent_process(unsigned long data)
-{
-	struct sk_buff *skb;
-
-	while ((skb = skb_dequeue(&wireless_nlevent_queue)))
-		rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
-}
-
-static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
-
-/* ---------------------------------------------------------------- */
-/*
- * Fill a rtnetlink message with our event data.
- * Note that we propage only the specified event and don't dump the
- * current wireless config. Dumping the wireless config is far too
- * expensive (for each parameter, the driver need to query the hardware).
- */
-static inline int rtnetlink_fill_iwinfo(struct sk_buff *	skb,
-					struct net_device *	dev,
-					int			type,
-					char *			event,
-					int			event_len)
-{
-	struct ifinfomsg *r;
-	struct nlmsghdr  *nlh;
-	unsigned char	 *b = skb->tail;
-
-	nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
-	r = NLMSG_DATA(nlh);
-	r->ifi_family = AF_UNSPEC;
-	r->__ifi_pad = 0;
-	r->ifi_type = dev->type;
-	r->ifi_index = dev->ifindex;
-	r->ifi_flags = dev_get_flags(dev);
-	r->ifi_change = 0;	/* Wireless changes don't affect those flags */
-
-	/* Add the wireless events in the netlink packet */
-	RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
-
-	nlh->nlmsg_len = skb->tail - b;
-	return skb->len;
-
-nlmsg_failure:
-rtattr_failure:
-	skb_trim(skb, b - skb->data);
-	return -1;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Create and broadcast and send it on the standard rtnetlink socket
- * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c
- * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
- * within a RTM_NEWLINK event.
- */
-static inline void rtmsg_iwinfo(struct net_device *	dev,
-				char *			event,
-				int			event_len)
-{
-	struct sk_buff *skb;
-	int size = NLMSG_GOODSIZE;
-
-	skb = alloc_skb(size, GFP_ATOMIC);
-	if (!skb)
-		return;
-
-	if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
-				  event, event_len) < 0) {
-		kfree_skb(skb);
-		return;
-	}
-	NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
-	skb_queue_tail(&wireless_nlevent_queue, skb);
-	tasklet_schedule(&wireless_nlevent_tasklet);
-}
-
-#endif	/* WE_EVENT_RTNETLINK */
-
-/* ---------------------------------------------------------------- */
-/*
- * Main event dispatcher. Called from other parts and drivers.
- * Send the event on the appropriate channels.
- * May be called from interrupt context.
- */
-void wireless_send_event(struct net_device *	dev,
-			 unsigned int		cmd,
-			 union iwreq_data *	wrqu,
-			 char *			extra)
-{
-	const struct iw_ioctl_description *	descr = NULL;
-	int extra_len = 0;
-	struct iw_event  *event;		/* Mallocated whole event */
-	int event_len;				/* Its size */
-	int hdr_len;				/* Size of the event header */
-	int wrqu_off = 0;			/* Offset in wrqu */
-	/* Don't "optimise" the following variable, it will crash */
-	unsigned	cmd_index;		/* *MUST* be unsigned */
-
-	/* Get the description of the Event */
-	if(cmd <= SIOCIWLAST) {
-		cmd_index = cmd - SIOCIWFIRST;
-		if(cmd_index < standard_ioctl_num)
-			descr = &(standard_ioctl[cmd_index]);
-	} else {
-		cmd_index = cmd - IWEVFIRST;
-		if(cmd_index < standard_event_num)
-			descr = &(standard_event[cmd_index]);
-	}
-	/* Don't accept unknown events */
-	if(descr == NULL) {
-		/* Note : we don't return an error to the driver, because
-		 * the driver would not know what to do about it. It can't
-		 * return an error to the user, because the event is not
-		 * initiated by a user request.
-		 * The best the driver could do is to log an error message.
-		 * We will do it ourselves instead...
-		 */
-	  	printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
-		       dev->name, cmd);
-		return;
-	}
-#ifdef WE_EVENT_DEBUG
-	printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n",
-	       dev->name, cmd);
-	printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif	/* WE_EVENT_DEBUG */
-
-	/* Check extra parameters and set extra_len */
-	if(descr->header_type == IW_HEADER_TYPE_POINT) {
-		/* Check if number of token fits within bounds */
-		if(wrqu->data.length > descr->max_tokens) {
-		  	printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
-			return;
-		}
-		if(wrqu->data.length < descr->min_tokens) {
-		  	printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
-			return;
-		}
-		/* Calculate extra_len - extra is NULL for restricted events */
-		if(extra != NULL)
-			extra_len = wrqu->data.length * descr->token_size;
-		/* Always at an offset in wrqu */
-		wrqu_off = IW_EV_POINT_OFF;
-#ifdef WE_EVENT_DEBUG
-		printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len);
-#endif	/* WE_EVENT_DEBUG */
-	}
-
-	/* Total length of the event */
-	hdr_len = event_type_size[descr->header_type];
-	event_len = hdr_len + extra_len;
-
-#ifdef WE_EVENT_DEBUG
-	printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, wrqu_off %d, event_len %d\n", dev->name, cmd, hdr_len, wrqu_off, event_len);
-#endif	/* WE_EVENT_DEBUG */
-
-	/* Create temporary buffer to hold the event */
-	event = kmalloc(event_len, GFP_ATOMIC);
-	if(event == NULL)
-		return;
-
-	/* Fill event */
-	event->len = event_len;
-	event->cmd = cmd;
-	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
-	if(extra != NULL)
-		memcpy(((char *) event) + hdr_len, extra, extra_len);
-
-#ifdef WE_EVENT_RTNETLINK
-	/* Send via the RtNetlink event channel */
-	rtmsg_iwinfo(dev, (char *) event, event_len);
-#endif	/* WE_EVENT_RTNETLINK */
-
-	/* Cleanup */
-	kfree(event);
-
-	return;		/* Always success, I guess ;-) */
-}
-
 /********************** ENHANCED IWSPY SUPPORT **********************/
 /*
  * In the old days, the driver was handling spy support all by itself.
diff --git a/net/wireless/wext.h b/net/wireless/wext.h
new file mode 100644
index 0000000..fcf1c5a
--- /dev/null
+++ b/net/wireless/wext.h
@@ -0,0 +1,13 @@
+/*
+ * some foo for wext compat/wext interoperability
+ */
+#ifndef _WEXT_H
+#define _WEXT_H
+#include <linux/wireless.h>
+extern struct iw_statistics *get_wireless_stats(struct net_device *dev,
+						struct iw_statistics *out);
+extern const struct iw_ioctl_description standard_ioctl[];
+extern const unsigned standard_ioctl_num;
+extern const struct iw_ioctl_description standard_event[];
+extern const int event_type_size[];
+#endif /* _WEXT_H */
-- 
1.4.4.2

-- 
John W. Linville
linville@tuxdriver.com
_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-01-31  1:38 ` [RFC PATCH 1/3] cfg80211 and nl80211 John W. Linville
  2007-01-31  1:39   ` [RFC PATCH 2/3] wireless: move wext to net/wireless/ John W. Linville
@ 2007-01-31  2:46   ` Michael Wu
  2007-01-31 17:37     ` Jiri Benc
  2007-02-01 10:18     ` Johannes Berg
  1 sibling, 2 replies; 52+ messages in thread
From: Michael Wu @ 2007-01-31  2:46 UTC (permalink / raw)
  To: wireless


[-- Attachment #1.1: Type: text/plain, Size: 2083 bytes --]

On Tuesday 30 January 2007 20:38, John W. Linville wrote:
> From: Johannes Berg <johannes@sipsolutions.net>
>
> +	/* Inject a frame using NL80211_ATTR_FLAGS and NL80211_ATTR_FRAME.
> +	 * If kernel sends this, it's a status notification for the injected
> +	 * frame. */
> +	NL80211_CMD_INJECT,
> +
I think drivers that currently support frame injection do it by allowing TX on 
a monitor interface w/ AVS or radiotap header. I would rather have this than 
requiring the use of NL80211 to inject frames since userspace won't have to 
do as much to continue supporting frame injection. It also makes more sense 
to me. Status notification can be done by a 802.11 ACK frame. Radiotap might 
need to be extended to support toggling the ACK frame reporting for TX and 
whatever else userspace wants to set.

[snip]
> +	/* configure device */
> +	NL80211_CMD_CONFIGURE,
> +
> +	/* request configuration */
> +	NL80211_CMD_GET_CONFIG,
> +
> +	/* configuration sent from kernel */
> +	NL80211_CMD_NEW_CONFIG,
> +
Why NEW?

[snip]
> +	/* currently set roaming BSSID */
> +	NL80211_CMD_FIXED_BSSID,
> +
Wasn't immediately obvious to me that this is from GET_FIXED_BSSID.

> +	/* get current association information, if not associated then
> +	 * the BSSID attribute is not present in response */
> +	NL80211_CMD_GET_ASSOCIATION,
> +
> +	/* association notification and response to GET_BSSID */
> +	NL80211_CMD_ASSOCIATION_CHANGED,
> +
I don't see a GET_BSSID. Did you mean GET_ASSOCIATION?

[snip]
> +	/* attributes used for configuration */
> +	/* network ID (pre 802.11 HW) */
> +	NL80211_ATTR_NETWORK_ID,
> +
We really want to support (and port) pre 802.11 drivers?

[snip]
> +	/* SSID of ESS to associate to */
BSS?

> +	NL80211_ATTR_SSID,
> +

[snip]
> + * @list_interfaces: for each interfaces belonging to the wiphy identified
> + *		     by the priv pointer, call the one() function with the
> + *		     given data and the ifindex. This callback is required.
> + *
Do we really have to have this?

Thanks,
-Michael Wu

[-- Attachment #1.2: Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 152 bytes --]

_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-01-31  1:37 [RFC] cfg80211 merge John W. Linville
  2007-01-31  1:38 ` [RFC PATCH 1/3] cfg80211 and nl80211 John W. Linville
@ 2007-01-31  2:48 ` Jouni Malinen
  2007-01-31 17:29   ` Jiri Benc
  2007-02-07  0:46 ` [RFC v2] " John W. Linville
  2 siblings, 1 reply; 52+ messages in thread
From: Jouni Malinen @ 2007-01-31  2:48 UTC (permalink / raw)
  To: John W. Linville; +Cc: wireless

On Tue, Jan 30, 2007 at 08:37:17PM -0500, John W. Linville wrote:

> In London we agreed that it would be good to go ahead and merge
> cfg80211.

> Let me know if I've left anything out, screwed anything up, or if
> you no longer think a cfg80211 merge is appropriate at this time.

Merge to the mainline kernel? Do we really believe that this is ready
for this now? I have to admit that I have not personally tested this,
but based on what I've heard, this does not seem to be complete yet and
there are some areas that may need to be changed or at least there has
been some comments on desire to change something in the design, e.g.,
for the MLME requests and recently for the SSID handling. In addition, I
have not heard of any user space programs that would actually be using
this interface.

What would happen if this gets merged in now and some major problems are
found when people start implementing programs using this interface?
Would we be stuck with maintaining this user space interface as-is for
years without having a chance of easily fixing something?

I'm all for getting this into the mainline kernel as soon as possible,
but I do not think that this should happen before there are user space
programs that demonstrate that cfg80211 can be used without problems and
that the kernel side implementation is complete enough to allow some
meaningful operations to be completed with cfg80211 (likely with WEXT
disabled, i.e., cfg80211 being feature complete for, say, WPA
authentication).

I have been waiting for cfg80211 to become bit more stable and
functional before implementing support for it as an alternative driver
interface in wpa_supplicant. Based on what I heard in London, this would
not work yet, but maybe now would be a suitable time to at least start
experimenting with this by making a copy of the WEXT interface code and
starting to change operations one by one from WEXT to cfg80211 based on
what actually is available in the kernel.

-- 
Jouni Malinen                                            PGP id EFC895FA
_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 2/3] wireless: move wext to net/wireless/
  2007-01-31  1:39   ` [RFC PATCH 2/3] wireless: move wext to net/wireless/ John W. Linville
  2007-01-31  1:41     ` [RFC PATCH 3/3] cfg80211: add wext-compatible client John W. Linville
@ 2007-01-31 14:40     ` Christoph Hellwig
  2007-01-31 19:00       ` Johannes Berg
  1 sibling, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2007-01-31 14:40 UTC (permalink / raw)
  To: John W. Linville; +Cc: wireless

On Tue, Jan 30, 2007 at 08:39:43PM -0500, John W. Linville wrote:
> From: Johannes Berg <johannes@sipsolutions.net>
> 
> This patch moves net/core/wireless.c to net/wireless/wext.c. I
> refrained from further cleanups though I was tempted. Hence this
> is a pure file move plus various build system adjustments.

Is this the right name?  Shouldn't it be wext-ioctl.c given that
we'll hopefull soon grow a wext implementation with a proper method
table interface for drivers.

_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-01-31  2:48 ` [RFC] cfg80211 merge Jouni Malinen
@ 2007-01-31 17:29   ` Jiri Benc
  2007-01-31 18:32     ` John W. Linville
  0 siblings, 1 reply; 52+ messages in thread
From: Jiri Benc @ 2007-01-31 17:29 UTC (permalink / raw)
  To: Jouni Malinen; +Cc: wireless

On Tue, 30 Jan 2007 18:48:07 -0800, Jouni Malinen wrote:
> I'm all for getting this into the mainline kernel as soon as possible,
> but I do not think that this should happen before there are user space
> programs that demonstrate that cfg80211 can be used without problems and
> that the kernel side implementation is complete enough to allow some
> meaningful operations to be completed with cfg80211 (likely with WEXT
> disabled, i.e., cfg80211 being feature complete for, say, WPA
> authentication).

I fully agree with Jouni.

 Jiri

-- 
Jiri Benc
SUSE Labs
_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-01-31  2:46   ` [RFC PATCH 1/3] cfg80211 and nl80211 Michael Wu
@ 2007-01-31 17:37     ` Jiri Benc
  2007-01-31 20:24       ` Michael Buesch
  2007-02-01 10:18     ` Johannes Berg
  1 sibling, 1 reply; 52+ messages in thread
From: Jiri Benc @ 2007-01-31 17:37 UTC (permalink / raw)
  To: Michael Wu; +Cc: wireless

On Tue, 30 Jan 2007 21:46:01 -0500, Michael Wu wrote:
> I think drivers that currently support frame injection do it by allowing TX on 
> a monitor interface w/ AVS or radiotap header. I would rather have this than 
> requiring the use of NL80211 to inject frames since userspace won't have to 
> do as much to continue supporting frame injection. It also makes more sense 
> to me. Status notification can be done by a 802.11 ACK frame. Radiotap might 
> need to be extended to support toggling the ACK frame reporting for TX and 
> whatever else userspace wants to set.

Please, don't do that. You need to get a success/failure report from
the driver/stack. Doing that by dumping fake frames through the network
interfaces isn't something I could consider as a right behaviour. Also,
you need to match the sent frame and the report from the driver. This
gets really tricky when you use network interfaces.

 Jiri

-- 
Jiri Benc
SUSE Labs
_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-01-31 17:29   ` Jiri Benc
@ 2007-01-31 18:32     ` John W. Linville
  2007-01-31 19:25       ` Jiri Benc
  0 siblings, 1 reply; 52+ messages in thread
From: John W. Linville @ 2007-01-31 18:32 UTC (permalink / raw)
  To: Jiri Benc; +Cc: wireless

On Wed, Jan 31, 2007 at 06:29:08PM +0100, Jiri Benc wrote:
> On Tue, 30 Jan 2007 18:48:07 -0800, Jouni Malinen wrote:
> > I'm all for getting this into the mainline kernel as soon as possible,
> > but I do not think that this should happen before there are user space
> > programs that demonstrate that cfg80211 can be used without problems and
> > that the kernel side implementation is complete enough to allow some
> > meaningful operations to be completed with cfg80211 (likely with WEXT
> > disabled, i.e., cfg80211 being feature complete for, say, WPA
> > authentication).
> 
> I fully agree with Jouni.

OK, where were you guys in London?  I don't recall any disagreement
there?

The WEXT compat code should be enough to drive the cfg80211 code.
And we would mark it CONFIG_EXPERIMENTAL (which I left out of the
patch) for now.  I suppose we could leave-out the nl80211 part to
avoid userland lock-in for now?

This also has the advantage of including the ieee80211_ptr in the
net_device structure, hopefully easing the task of packaging the rest
of the stack for external builds.

I suppose we could wait for at least one driver to be cfg80211-capable.
Would that satisfy everyone?

John
-- 
John W. Linville
linville@tuxdriver.com
_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 2/3] wireless: move wext to net/wireless/
  2007-01-31 14:40     ` [RFC PATCH 2/3] wireless: move wext to net/wireless/ Christoph Hellwig
@ 2007-01-31 19:00       ` Johannes Berg
  0 siblings, 0 replies; 52+ messages in thread
From: Johannes Berg @ 2007-01-31 19:00 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: wireless


[-- Attachment #1.1: Type: text/plain, Size: 464 bytes --]

On Wed, 2007-01-31 at 14:40 +0000, Christoph Hellwig wrote:

> Is this the right name?  Shouldn't it be wext-ioctl.c given that
> we'll hopefull soon grow a wext implementation with a proper method
> table interface for drivers.

No, wext is dead, the replacement is a mix of cfg80211 and nl80211.
Besides, wext isn't strictly ioctl-based, it does netlink too (but just
puts the ioctl data into netlink messages and has the same braindamage.)

johannes

[-- Attachment #1.2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

[-- Attachment #2: Type: text/plain, Size: 152 bytes --]

_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-01-31 18:32     ` John W. Linville
@ 2007-01-31 19:25       ` Jiri Benc
  2007-01-31 20:07         ` Christoph Hellwig
  0 siblings, 1 reply; 52+ messages in thread
From: Jiri Benc @ 2007-01-31 19:25 UTC (permalink / raw)
  To: John W. Linville; +Cc: wireless

On Wed, 31 Jan 2007 13:32:55 -0500, John W. Linville wrote:
> OK, where were you guys in London?  I don't recall any disagreement
> there?

Hm, I remember we agreed that merging of d80211 and cfg80211/nl80211
are two separate things. And a discussion about cfg80211/nl80211 not
being quite ready yet (if I remember correctly, Jouni participated in
that discussion). But well, it was just informal and maybe I missed
something else.

> The WEXT compat code should be enough to drive the cfg80211 code.
> And we would mark it CONFIG_EXPERIMENTAL (which I left out of the
> patch) for now.  I suppose we could leave-out the nl80211 part to
> avoid userland lock-in for now?

Yes, that could solve that I think.

> This also has the advantage of including the ieee80211_ptr in the
> net_device structure, hopefully easing the task of packaging the rest
> of the stack for external builds.

Definitely.

> I suppose we could wait for at least one driver to be cfg80211-capable.
> Would that satisfy everyone?

If cfg80211-capable means also WPA-capable and if nl80211 is left out
until user space counterpart exists, yes, I'm okay with that.

Thanks,

 Jiri

-- 
Jiri Benc
SUSE Labs
_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-01-31 19:25       ` Jiri Benc
@ 2007-01-31 20:07         ` Christoph Hellwig
  2007-01-31 20:44           ` John W. Linville
  0 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2007-01-31 20:07 UTC (permalink / raw)
  To: Jiri Benc; +Cc: wireless

On Wed, Jan 31, 2007 at 08:25:49PM +0100, Jiri Benc wrote:
> On Wed, 31 Jan 2007 13:32:55 -0500, John W. Linville wrote:
> > OK, where were you guys in London?  I don't recall any disagreement
> > there?
> 
> Hm, I remember we agreed that merging of d80211 and cfg80211/nl80211
> are two separate things. And a discussion about cfg80211/nl80211 not
> being quite ready yet (if I remember correctly, Jouni participated in
> that discussion). But well, it was just informal and maybe I missed
> something else.

Yes, cfg80211 should go in first so we can use it to avoid all the
boilerplate code in existing and new drivers.  Once we have netlink-capable
userspace nl80211 can be added ontop of that.  This also allows for
possibly needed adjustments of the nl interface when writing the
tools actually using it.

_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-01-31 17:37     ` Jiri Benc
@ 2007-01-31 20:24       ` Michael Buesch
  0 siblings, 0 replies; 52+ messages in thread
From: Michael Buesch @ 2007-01-31 20:24 UTC (permalink / raw)
  To: wireless

On Wednesday 31 January 2007 18:37, Jiri Benc wrote:
> On Tue, 30 Jan 2007 21:46:01 -0500, Michael Wu wrote:
> > I think drivers that currently support frame injection do it by allowing TX on 
> > a monitor interface w/ AVS or radiotap header. I would rather have this than 
> > requiring the use of NL80211 to inject frames since userspace won't have to 
> > do as much to continue supporting frame injection. It also makes more sense 
> > to me. Status notification can be done by a 802.11 ACK frame. Radiotap might 
> > need to be extended to support toggling the ACK frame reporting for TX and 
> > whatever else userspace wants to set.
> 
> Please, don't do that. You need to get a success/failure report from
> the driver/stack. Doing that by dumping fake frames through the network
> interfaces isn't something I could consider as a right behaviour. Also,
> you need to match the sent frame and the report from the driver. This
> gets really tricky when you use network interfaces.

TX control information also needs to be passed somehow.
So you would require some TXcontrol header or something
like that for the injected data. Not really scalable.

-- 
Greetings Michael.
_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-01-31 20:07         ` Christoph Hellwig
@ 2007-01-31 20:44           ` John W. Linville
  2007-01-31 21:06             ` Johannes Berg
  0 siblings, 1 reply; 52+ messages in thread
From: John W. Linville @ 2007-01-31 20:44 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: wireless

On Wed, Jan 31, 2007 at 08:07:41PM +0000, Christoph Hellwig wrote:
> On Wed, Jan 31, 2007 at 08:25:49PM +0100, Jiri Benc wrote:
> > On Wed, 31 Jan 2007 13:32:55 -0500, John W. Linville wrote:
> > > OK, where were you guys in London?  I don't recall any disagreement
> > > there?
> > 
> > Hm, I remember we agreed that merging of d80211 and cfg80211/nl80211
> > are two separate things. And a discussion about cfg80211/nl80211 not
> > being quite ready yet (if I remember correctly, Jouni participated in
> > that discussion). But well, it was just informal and maybe I missed
> > something else.
> 
> Yes, cfg80211 should go in first so we can use it to avoid all the
> boilerplate code in existing and new drivers.  Once we have netlink-capable
> userspace nl80211 can be added ontop of that.  This also allows for
> possibly needed adjustments of the nl interface when writing the
> tools actually using it.

Agreed.  I (and/or Johannes?) will rework the patches to remove the
nl bits.

Thanks,

John
-- 
John W. Linville
linville@tuxdriver.com
_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-01-31 20:44           ` John W. Linville
@ 2007-01-31 21:06             ` Johannes Berg
  2007-01-31 23:54               ` Tomas Winkler
  0 siblings, 1 reply; 52+ messages in thread
From: Johannes Berg @ 2007-01-31 21:06 UTC (permalink / raw)
  To: John W. Linville; +Cc: wireless


[-- Attachment #1.1: Type: text/plain, Size: 270 bytes --]

On Wed, 2007-01-31 at 15:44 -0500, John W. Linville wrote:

> Agreed.  I (and/or Johannes?) will rework the patches to remove the
> nl bits.

Starting tomorrow I can devote some time again.

Maybe we should resurrect those wiphy patches I had first?

johannes

[-- Attachment #1.2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

[-- Attachment #2: Type: text/plain, Size: 152 bytes --]

_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-01-31 21:06             ` Johannes Berg
@ 2007-01-31 23:54               ` Tomas Winkler
  2007-02-01 13:07                 ` Johannes Berg
  0 siblings, 1 reply; 52+ messages in thread
From: Tomas Winkler @ 2007-01-31 23:54 UTC (permalink / raw)
  To: Johannes Berg; +Cc: wireless


[-- Attachment #1.1: Type: text/plain, Size: 698 bytes --]

As far as I tested cfg80211 compatibility mode is incomplete. You cannot run
hostapd over it
If I recall correctly PRISM2_IOCTL_PRISM2_PARAM and friends are not passed
to d80211

Tomas.

On 1/31/07, Johannes Berg <johannes@sipsolutions.net> wrote:
>
> On Wed, 2007-01-31 at 15:44 -0500, John W. Linville wrote:
>
> > Agreed.  I (and/or Johannes?) will rework the patches to remove the
> > nl bits.
>
> Starting tomorrow I can devote some time again.
>
> Maybe we should resurrect those wiphy patches I had first?
>
> johannes
>
>
> _______________________________________________
> wireless mailing list
> wireless@lists.tuxdriver.org
> http://lists.tuxdriver.org/mailman/listinfo/wireless
>
>
>
>

[-- Attachment #1.2: Type: text/html, Size: 1150 bytes --]

[-- Attachment #2: Type: text/plain, Size: 152 bytes --]

_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-01-31  2:46   ` [RFC PATCH 1/3] cfg80211 and nl80211 Michael Wu
  2007-01-31 17:37     ` Jiri Benc
@ 2007-02-01 10:18     ` Johannes Berg
  2007-02-05 17:45       ` Michael Wu
  1 sibling, 1 reply; 52+ messages in thread
From: Johannes Berg @ 2007-02-01 10:18 UTC (permalink / raw)
  To: Michael Wu; +Cc: wireless


[-- Attachment #1.1: Type: text/plain, Size: 3242 bytes --]

On Tue, 2007-01-30 at 21:46 -0500, Michael Wu wrote:

> I think drivers that currently support frame injection do it by allowing TX on 
> a monitor interface w/ AVS or radiotap header. I would rather have this than 
> requiring the use of NL80211 to inject frames since userspace won't have to 
> do as much to continue supporting frame injection. It also makes more sense 
> to me. Status notification can be done by a 802.11 ACK frame. Radiotap might 
> need to be extended to support toggling the ACK frame reporting for TX and 
> whatever else userspace wants to set.

I disagree. Generic netlink is trivially extensible while radiotap
isn't, and I really don't want a radiotap parser in the kernel.
Secondly, a lot of notification items will only be given via netlink
anyway, like failed decryption or whatever, and we don't want to keep
all this in fake management frames.

> > +	/* configuration sent from kernel */
> > +	NL80211_CMD_NEW_CONFIG,
> > +
> Why NEW?

I think Thomas suggested the scheme GET/SET/NEW but I don't really care.

> [snip]
> > +	/* currently set roaming BSSID */
> > +	NL80211_CMD_FIXED_BSSID,
> > +
> Wasn't immediately obvious to me that this is from GET_FIXED_BSSID.

should get a NEW prefix as well then? :)

> > +	/* get current association information, if not associated then
> > +	 * the BSSID attribute is not present in response */
> > +	NL80211_CMD_GET_ASSOCIATION,
> > +
> > +	/* association notification and response to GET_BSSID */
> > +	NL80211_CMD_ASSOCIATION_CHANGED,
> > +
> I don't see a GET_BSSID. Did you mean GET_ASSOCIATION?

Yeah, I suppose I do.

> [snip]
> > +	/* attributes used for configuration */
> > +	/* network ID (pre 802.11 HW) */
> > +	NL80211_ATTR_NETWORK_ID,
> > +
> We really want to support (and port) pre 802.11 drivers?

Not really sure. Do we? This is probably the only thing required for
that... We can leave it out and if someone wants to port them later add
it in.

> [snip]
> > +	/* SSID of ESS to associate to */
> BSS?

Well, I think that comes down to what happens if we don't set a BSSID
but do set an SSID and then tell it to associate. In that case, it
should chose a BSS from the ESS with that SSID.

> > + * @list_interfaces: for each interfaces belonging to the wiphy identified
> > + *		     by the priv pointer, call the one() function with the
> > + *		     given data and the ifindex. This callback is required.
> > + *
> Do we really have to have this?

We can only not have this if we mandate that all add/remove interface
operations go through cfg80211. I was afraid that wouldn't be too
popular. If we do mandate that then we can keep the list in cfg80211
instead of having to query the driver/stack.
However, d80211 at least will need access to the list. So then we have
to add API for that. And all the legacy non-d80211 drivers (except for
ipw2200) will ever only return a single interface so it's trivial to
implement there. Hence, I stuck with this. I'm open for changes, of
course.

If I now misunderstood the question... Yes, we do need to know the list
of devices associated to a certain wiphy, if only to be able to have
userspace print it out :)

johannes

[-- Attachment #1.2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

[-- Attachment #2: Type: text/plain, Size: 152 bytes --]

_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-01-31 23:54               ` Tomas Winkler
@ 2007-02-01 13:07                 ` Johannes Berg
  2007-02-01 14:04                   ` Tomas Winkler
  0 siblings, 1 reply; 52+ messages in thread
From: Johannes Berg @ 2007-02-01 13:07 UTC (permalink / raw)
  To: Tomas Winkler; +Cc: wireless


[-- Attachment #1.1: Type: text/plain, Size: 376 bytes --]

On Thu, 2007-02-01 at 01:54 +0200, Tomas Winkler wrote:
> As far as I tested cfg80211 compatibility mode is incomplete. You
> cannot run hostapd over it
> If I recall correctly PRISM2_IOCTL_PRISM2_PARAM and friends are not
> passed to d80211 

It's a priv ioctl, it's not supposed to be passed on.

johannes (who is getting positively pissed off about such requests)

[-- Attachment #1.2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

[-- Attachment #2: Type: text/plain, Size: 152 bytes --]

_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-02-01 13:07                 ` Johannes Berg
@ 2007-02-01 14:04                   ` Tomas Winkler
  2007-02-01 14:11                     ` Johannes Berg
  2007-02-01 14:12                     ` Jiri Benc
  0 siblings, 2 replies; 52+ messages in thread
From: Tomas Winkler @ 2007-02-01 14:04 UTC (permalink / raw)
  To: Johannes Berg; +Cc: wireless


[-- Attachment #1.1: Type: text/plain, Size: 521 bytes --]

Can you elaborate shortly.  I'm not from time 0 on this list.
Thanks.

On 2/1/07, Johannes Berg <johannes@sipsolutions.net> wrote:
>
> On Thu, 2007-02-01 at 01:54 +0200, Tomas Winkler wrote:
> > As far as I tested cfg80211 compatibility mode is incomplete. You
> > cannot run hostapd over it
> > If I recall correctly PRISM2_IOCTL_PRISM2_PARAM and friends are not
> > passed to d80211
>
> It's a priv ioctl, it's not supposed to be passed on.
>
> johannes (who is getting positively pissed off about such requests)
>
>
>

[-- Attachment #1.2: Type: text/html, Size: 848 bytes --]

[-- Attachment #2: Type: text/plain, Size: 152 bytes --]

_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-02-01 14:04                   ` Tomas Winkler
@ 2007-02-01 14:11                     ` Johannes Berg
  2007-02-02 18:18                       ` Tomas Winkler
  2007-02-01 14:12                     ` Jiri Benc
  1 sibling, 1 reply; 52+ messages in thread
From: Johannes Berg @ 2007-02-01 14:11 UTC (permalink / raw)
  To: Tomas Winkler; +Cc: wireless


[-- Attachment #1.1: Type: text/plain, Size: 581 bytes --]

On Thu, 2007-02-01 at 16:04 +0200, Tomas Winkler wrote:
> Can you elaborate shortly.  I'm not from time 0 on this list.

Well. cfg80211 will replace the common wext ioctls. All the private wext
ioctls will not be handled there.

The rationale is that
 (1) it doesn't delete priv ioctls, they'll still work for the migration
     period
 (2) you can't rely on private ioctls having a certain meaning for each
     driver, ie. other drivers are free the reassign numbers of
     sub-ioctls
 (3) any behaviour that is generally useful should be standardised.

johannes

[-- Attachment #1.2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

[-- Attachment #2: Type: text/plain, Size: 152 bytes --]

_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-02-01 14:04                   ` Tomas Winkler
  2007-02-01 14:11                     ` Johannes Berg
@ 2007-02-01 14:12                     ` Jiri Benc
  1 sibling, 0 replies; 52+ messages in thread
From: Jiri Benc @ 2007-02-01 14:12 UTC (permalink / raw)
  To: Tomas Winkler; +Cc: wireless

On Thu, 1 Feb 2007 16:04:53 +0200, Tomas Winkler wrote:
> Can you elaborate shortly.  I'm not from time 0 on this list.
> Thanks.

Use Google and list archives, that's why their exist.

 Jiri

-- 
Jiri Benc
SUSE Labs
_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-02-01 14:11                     ` Johannes Berg
@ 2007-02-02 18:18                       ` Tomas Winkler
  2007-02-03 17:37                         ` Johannes Berg
  0 siblings, 1 reply; 52+ messages in thread
From: Tomas Winkler @ 2007-02-02 18:18 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless, wireless


[-- Attachment #1.1: Type: text/plain, Size: 948 bytes --]

Johannes
I don't know who is missing something here, but what I'm saying that (1) is
not correct. priv ioctls don't work with compatibility  set on.
In any case I'll check it again to be sure and I will apologize if I'm not
correct.
I definitely agree on two other  points.

Thanks
Tomas


On 2/1/07, Johannes Berg <johannes@sipsolutions.net> wrote:
>
> On Thu, 2007-02-01 at 16:04 +0200, Tomas Winkler wrote:
> > Can you elaborate shortly.  I'm not from time 0 on this list.
>
> Well. cfg80211 will replace the common wext ioctls. All the private wext
> ioctls will not be handled there.
>
> The rationale is that
> (1) it doesn't delete priv ioctls, they'll still work for the migration
>      period
> (2) you can't rely on private ioctls having a certain meaning for each
>      driver, ie. other drivers are free the reassign numbers of
>      sub-ioctls
> (3) any behaviour that is generally useful should be standardised.
>
> johannes
>
>
>

[-- Attachment #1.2: Type: text/html, Size: 1470 bytes --]

[-- Attachment #2: Type: text/plain, Size: 152 bytes --]

_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC] cfg80211 merge
  2007-02-02 18:18                       ` Tomas Winkler
@ 2007-02-03 17:37                         ` Johannes Berg
  0 siblings, 0 replies; 52+ messages in thread
From: Johannes Berg @ 2007-02-03 17:37 UTC (permalink / raw)
  To: Tomas Winkler; +Cc: linux-wireless

On Fri, 2007-02-02 at 20:18 +0200, Tomas Winkler wrote:

> I don't know who is missing something here, but what I'm saying that
> (1) is not correct. priv ioctls don't work with compatibility  set
> on. 

Yup, looks like a bug. This should fix it.

johannes

--- wireless-dev.orig/net/wireless/wext-compat.c	2007-02-03 18:36:11.809570737 +0100
+++ wireless-dev/net/wireless/wext-compat.c	2007-02-03 18:36:35.589570737 +0100
@@ -1395,7 +1395,7 @@ int cfg80211_wext_nl_get(struct net_devi
 					      &iw_handler_get_iwstats,
 					      p_buf, p_len);
 	if(request->cmd == SIOCGIWPRIV)
-		return -EOPNOTSUPP;
+		return -ENOSYS;
 
 	/* Basic check */
 	if (!netif_device_present(dev))
@@ -1618,7 +1618,7 @@ int cfg80211_wext_ioctl(struct ifreq *if
 						   &iw_handler_get_iwstats);
 
 		case SIOCGIWPRIV:
-			return -EOPNOTSUPP;
+			return -ENOSYS;
 		default:
 			/* Basic check */
 			if (!netif_device_present(dev))



^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-02-01 10:18     ` Johannes Berg
@ 2007-02-05 17:45       ` Michael Wu
  2007-02-05 17:49         ` Johannes Berg
  0 siblings, 1 reply; 52+ messages in thread
From: Michael Wu @ 2007-02-05 17:45 UTC (permalink / raw)
  To: Johannes Berg; +Cc: wireless


[-- Attachment #1.1: Type: text/plain, Size: 1530 bytes --]

On Thursday 01 February 2007 05:18, Johannes Berg wrote:
> I disagree. Generic netlink is trivially extensible while radiotap
> isn't, and I really don't want a radiotap parser in the kernel.
> Secondly, a lot of notification items will only be given via netlink
> anyway, like failed decryption or whatever, and we don't want to keep
> all this in fake management frames.
>
Okay then, let's not use fake frames then. Let's make monitor interfaces 
resemble monitoring ethernet a little more by reporting both RXed frames and 
TXed frames. *BSD does this too and it makes running ethereal on monitor 
interfaces much more useful. TX frame reporting can be done simply in the 
ieee80211_tx_status call, allowing the TX status to be reported on real 
frames. 

Doing this is actually somewhat orthogonal to packet injection via netlink or 
monitor interface, but if we are reporting TXed frames with a radiotap 
header, we might as well allow frame injection with a radiotap frame for 
consistency. It seems far better than receiving frames on one interface and 
then using netlink to TX on some wiphy. Not to mention that using a 
configuration interface to send data doesn't make much sense when there's a 
perfectly fine network interface available. Radiotap parsing isn't much 
harder than EID parsing, allows current frame injection programs to continue 
working, and might even make it easier to run those programs on BSD too. (I 
don't know what mechanism BSD uses for frame injection)

-Michael Wu

[-- Attachment #1.2: Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 152 bytes --]

_______________________________________________
wireless mailing list
wireless@lists.tuxdriver.org
http://lists.tuxdriver.org/mailman/listinfo/wireless

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-02-05 17:45       ` Michael Wu
@ 2007-02-05 17:49         ` Johannes Berg
  2007-02-05 18:14           ` Michael Wu
  2007-02-05 18:16           ` Jiri Benc
  0 siblings, 2 replies; 52+ messages in thread
From: Johannes Berg @ 2007-02-05 17:49 UTC (permalink / raw)
  To: Michael Wu; +Cc: John W. Linville, linux-wireless

[-- Attachment #1: Type: text/plain, Size: 968 bytes --]

On Mon, 2007-02-05 at 12:45 -0500, Michael Wu wrote:

> Okay then, let's not use fake frames then. 

:)

> Let's make monitor interfaces 
> resemble monitoring ethernet a little more by reporting both RXed frames and 
> TXed frames. *BSD does this too and it makes running ethereal on monitor 
> interfaces much more useful. 

We do this right now afaik.

> TX frame reporting can be done simply in the 
> ieee80211_tx_status call, allowing the TX status to be reported on real 
> frames. 

I don't quite understand this comment. TX status reporting should go via
nl80211.

> Doing this is actually somewhat orthogonal to packet injection via netlink or 
> monitor interface, but if we are reporting TXed frames with a radiotap 
> header, we might as well allow frame injection with a radiotap frame for 
> consistency.

Well, it hasn't got much merit because for anything useful you'll need
to get state information on nl80211.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-02-05 17:49         ` Johannes Berg
@ 2007-02-05 18:14           ` Michael Wu
  2007-02-05 18:14             ` Johannes Berg
  2007-02-05 18:16           ` Jiri Benc
  1 sibling, 1 reply; 52+ messages in thread
From: Michael Wu @ 2007-02-05 18:14 UTC (permalink / raw)
  To: Johannes Berg; +Cc: John W. Linville, linux-wireless

[-- Attachment #1: Type: text/plain, Size: 1426 bytes --]

On Monday 05 February 2007 12:49, Johannes Berg wrote:
> > Let's make monitor interfaces
> > resemble monitoring ethernet a little more by reporting both RXed frames
> > and TXed frames. *BSD does this too and it makes running ethereal on
> > monitor interfaces much more useful.
>
> We do this right now afaik.
>
Where? I don't remember getting TX frames when running ethereal on a monitor 
interface.

> > TX frame reporting can be done simply in the
> > ieee80211_tx_status call, allowing the TX status to be reported on real
> > frames.
>
> I don't quite understand this comment. TX status reporting should go via
> nl80211.
>
If we report TXed frames at the point when the driver calls 
ieee80211_tx_status, we know whether or not an ack was received. This 
information can be added to the TX frame that is reported to the monitor 
interface.

> > Doing this is actually somewhat orthogonal to packet injection via
> > netlink or monitor interface, but if we are reporting TXed frames with a
> > radiotap header, we might as well allow frame injection with a radiotap
> > frame for consistency.
>
> Well, it hasn't got much merit because for anything useful you'll need
> to get state information on nl80211.
>
I'm not sure what you mean. The program can get the configuration info it 
needs from nl80211/cfg80211/WE and send/inject frames through the monitor 
interface.

-Michael Wu

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-02-05 18:14           ` Michael Wu
@ 2007-02-05 18:14             ` Johannes Berg
  2007-02-05 18:29               ` Jiri Benc
  2007-02-05 19:23               ` Michael Wu
  0 siblings, 2 replies; 52+ messages in thread
From: Johannes Berg @ 2007-02-05 18:14 UTC (permalink / raw)
  To: Michael Wu; +Cc: John W. Linville, linux-wireless

[-- Attachment #1: Type: text/plain, Size: 1002 bytes --]

On Mon, 2007-02-05 at 13:14 -0500, Michael Wu wrote:

> Where? I don't remember getting TX frames when running ethereal on a monitor 
> interface.

Ok, maybe it's missing. I think I saw them, but not sure. They do show
up on wmaster ;)

> If we report TXed frames at the point when the driver calls 
> ieee80211_tx_status, we know whether or not an ack was received. This 
> information can be added to the TX frame that is reported to the monitor 
> interface.

Oh but do we really want to report them that late? I wouldn't think so.
That would give us out-of-order issues wrt. the air...

> I'm not sure what you mean. The program can get the configuration info it 
> needs from nl80211/cfg80211/WE and send/inject frames through the monitor 
> interface.

Well, I guess I'm saying that wpa_supplicant shouldn't need the monitor
iface at all since it wants just management frames and we should give
those to it via netlink to avoid having to add more radiotap foo.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-02-05 17:49         ` Johannes Berg
  2007-02-05 18:14           ` Michael Wu
@ 2007-02-05 18:16           ` Jiri Benc
  1 sibling, 0 replies; 52+ messages in thread
From: Jiri Benc @ 2007-02-05 18:16 UTC (permalink / raw)
  To: Johannes Berg; +Cc: Michael Wu, John W. Linville, linux-wireless

On Mon, 05 Feb 2007 18:49:55 +0100, Johannes Berg wrote:
> > Let's make monitor interfaces 
> > resemble monitoring ethernet a little more by reporting both RXed frames and 
> > TXed frames. *BSD does this too and it makes running ethereal on monitor 
> > interfaces much more useful. 
> 
> We do this right now afaik.

AFAIK we report RXed frames only.

> > TX frame reporting can be done simply in the 
> > ieee80211_tx_status call, allowing the TX status to be reported on real 
> > frames. 
> 
> I don't quite understand this comment. TX status reporting should go via
> nl80211.

I think Michael meant that an elegant way for sending also TXed frames
to monitor interfaces is to do that in ieee80211_tx_status. Makes sense
to me.

 Jiri

-- 
Jiri Benc
SUSE Labs

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-02-05 18:29               ` Jiri Benc
@ 2007-02-05 18:29                 ` Johannes Berg
  2007-02-05 19:13                 ` Jouni Malinen
  1 sibling, 0 replies; 52+ messages in thread
From: Johannes Berg @ 2007-02-05 18:29 UTC (permalink / raw)
  To: Jiri Benc; +Cc: Michael Wu, John W. Linville, linux-wireless

[-- Attachment #1: Type: text/plain, Size: 760 bytes --]

On Mon, 2007-02-05 at 19:29 +0100, Jiri Benc wrote:

> No. It's the other way around, when they're reported immediately, they
> will be out of order. ieee80211_tx_status is called from the interrupt
> handler as the result of the frame being actually sent. Before that, a
> frame can sit for an arbitrary long time in the hw queue or not sent at
> all. Reporting only frames confirmed by ieee80211_tx_status as
> successfully sent at the time the function is called reflects better
> what is on the air (it's still not perfect as things like
> retransmissions are not caught).

Yeah, you're right. Then again, it doesn't matter all that much since if
you want exact timing you need a card that never transmits in monitor
mode anyway.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-02-05 18:14             ` Johannes Berg
@ 2007-02-05 18:29               ` Jiri Benc
  2007-02-05 18:29                 ` Johannes Berg
  2007-02-05 19:13                 ` Jouni Malinen
  2007-02-05 19:23               ` Michael Wu
  1 sibling, 2 replies; 52+ messages in thread
From: Jiri Benc @ 2007-02-05 18:29 UTC (permalink / raw)
  To: Johannes Berg; +Cc: Michael Wu, John W. Linville, linux-wireless

On Mon, 05 Feb 2007 19:14:28 +0100, Johannes Berg wrote:
> Oh but do we really want to report them that late? I wouldn't think so.
> That would give us out-of-order issues wrt. the air...

No. It's the other way around, when they're reported immediately, they
will be out of order. ieee80211_tx_status is called from the interrupt
handler as the result of the frame being actually sent. Before that, a
frame can sit for an arbitrary long time in the hw queue or not sent at
all. Reporting only frames confirmed by ieee80211_tx_status as
successfully sent at the time the function is called reflects better
what is on the air (it's still not perfect as things like
retransmissions are not caught).

> Well, I guess I'm saying that wpa_supplicant shouldn't need the monitor
> iface at all since it wants just management frames and we should give
> those to it via netlink to avoid having to add more radiotap foo.

Agreed.

 Jiri

-- 
Jiri Benc
SUSE Labs

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-02-05 18:29               ` Jiri Benc
  2007-02-05 18:29                 ` Johannes Berg
@ 2007-02-05 19:13                 ` Jouni Malinen
  1 sibling, 0 replies; 52+ messages in thread
From: Jouni Malinen @ 2007-02-05 19:13 UTC (permalink / raw)
  To: Jiri Benc; +Cc: Johannes Berg, Michael Wu, John W. Linville, linux-wireless

On Mon, Feb 05, 2007 at 07:29:58PM +0100, Jiri Benc wrote:

> No. It's the other way around, when they're reported immediately, they
> will be out of order. ieee80211_tx_status is called from the interrupt
> handler as the result of the frame being actually sent. Before that, a
> frame can sit for an arbitrary long time in the hw queue or not sent at
> all. Reporting only frames confirmed by ieee80211_tx_status as
> successfully sent at the time the function is called reflects better
> what is on the air (it's still not perfect as things like
> retransmissions are not caught).

What is your definition for "successfully sent" in this context? I would
probably also include frames that did not receive ACK, but did have some
transmit tries. If there is place for reporting TX status (ack/no ack,
number of retries) that could be added here. In addition, some hardware
designs may also return the timestamp of the transmission in TX status,
so that could also be added. This would actually make it possible to
figure out the exact sequence of frames (in userspace app), if needed.

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-02-05 18:14             ` Johannes Berg
  2007-02-05 18:29               ` Jiri Benc
@ 2007-02-05 19:23               ` Michael Wu
  2007-02-05 19:24                 ` Johannes Berg
  1 sibling, 1 reply; 52+ messages in thread
From: Michael Wu @ 2007-02-05 19:23 UTC (permalink / raw)
  To: Johannes Berg; +Cc: John W. Linville, linux-wireless

[-- Attachment #1: Type: text/plain, Size: 813 bytes --]

On Monday 05 February 2007 13:14, Johannes Berg wrote:
> > I'm not sure what you mean. The program can get the configuration info it
> > needs from nl80211/cfg80211/WE and send/inject frames through the monitor
> > interface.
>
> Well, I guess I'm saying that wpa_supplicant shouldn't need the monitor
> iface at all since it wants just management frames and we should give
> those to it via netlink to avoid having to add more radiotap foo.
>
Yeah, a monitor interface is overkill for wpa_supplicant. We can make the 
IEEE80211_IF_TYPE_MGMT be a sort of filtered monitor interface optimized for 
userspace MLME. Or maybe add some sort of filtering configuration for monitor 
interfaces.. might be overkill. Anyway, netlink does not seem appropriate for 
sending or receiving frames.

-Michael Wu

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-02-05 19:23               ` Michael Wu
@ 2007-02-05 19:24                 ` Johannes Berg
  2007-02-05 19:55                   ` Michael Wu
  0 siblings, 1 reply; 52+ messages in thread
From: Johannes Berg @ 2007-02-05 19:24 UTC (permalink / raw)
  To: Michael Wu; +Cc: John W. Linville, linux-wireless

[-- Attachment #1: Type: text/plain, Size: 185 bytes --]

On Mon, 2007-02-05 at 14:23 -0500, Michael Wu wrote:
> Anyway, netlink does not seem appropriate for 
> sending or receiving frames.

Care to explain why you think so?

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-02-05 19:24                 ` Johannes Berg
@ 2007-02-05 19:55                   ` Michael Wu
  2007-02-09 16:14                     ` Johannes Berg
  0 siblings, 1 reply; 52+ messages in thread
From: Michael Wu @ 2007-02-05 19:55 UTC (permalink / raw)
  To: Johannes Berg; +Cc: John W. Linville, linux-wireless

[-- Attachment #1: Type: text/plain, Size: 776 bytes --]

On Monday 05 February 2007 14:24, Johannes Berg wrote:
> On Mon, 2007-02-05 at 14:23 -0500, Michael Wu wrote:
> > Anyway, netlink does not seem appropriate for
> > sending or receiving frames.
>
> Care to explain why you think so?
>
1. Userspace usually expects to send things on network interfaces. The kernel 
usually receives frames from userspace on network interfaces. I don't see the 
point of making an exception for 802.11 frames. (or rather, the potential for 
simpler frame injection code is not enough to justify inventing a new 
interface for doing it)
2. nl80211/cfg80211 is for configuring interfaces. Everything there is related 
to configuration of wireless interfaces except for the frame injection hook. 
It doesn't look right.

-Michael Wu

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC v2] cfg80211 merge
  2007-01-31  1:37 [RFC] cfg80211 merge John W. Linville
  2007-01-31  1:38 ` [RFC PATCH 1/3] cfg80211 and nl80211 John W. Linville
  2007-01-31  2:48 ` [RFC] cfg80211 merge Jouni Malinen
@ 2007-02-07  0:46 ` John W. Linville
  2007-02-07  0:47   ` [RFC PATCH 1/3] wireless: add cfg80211 John W. Linville
  2007-02-07 14:39   ` [RFC v2] cfg80211 merge John W. Linville
  2 siblings, 2 replies; 52+ messages in thread
From: John W. Linville @ 2007-02-07  0:46 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes

In London we agreed that it would be good to go ahead and merge
cfg80211.  This provides some exposure to the API and eases some of
the pain for driver maintainers (who won't have to follow wireless-dev
to get it).  It also adds the ieee80211_ptr to the net_device struct.

I have refactored the cfg80211-related patches down to three patches.
This eliminates some of the churn in the series from wireless-dev,
and each patch leaves a buildable tree.  This version also removes
the nl80211 bits to a separate patch (available in wireless-dev on
the mm-master branch).
 
Let me know if I've left anything out, screwed anything up, etc...

Thanks,

John
-- 
John W. Linville
linville@tuxdriver.com

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RFC PATCH 1/3] wireless: add cfg80211
  2007-02-07  0:46 ` [RFC v2] " John W. Linville
@ 2007-02-07  0:47   ` John W. Linville
       [not found]     ` <20070207004832.GC23096@tuxdriver.com>
  2007-02-07  7:35     ` [RFC PATCH 1/3] wireless: add cfg80211 Christoph Hellwig
  2007-02-07 14:39   ` [RFC v2] cfg80211 merge John W. Linville
  1 sibling, 2 replies; 52+ messages in thread
From: John W. Linville @ 2007-02-07  0:47 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes

From: Johannes Berg <johannes@sipsolutions.net>

This patch adds cfg80211, a new configuration system for wireless
hardware.

It currently features a bunch of configuration requests, support for
adding and removing virtual interfaces, the ability to inject packets and
more.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
 include/linux/netdevice.h  |    1 +
 include/net/cfg80211.h     |  186 ++++++++++++++++++++++++++++++++++++++++++++
 net/Kconfig                |    3 +
 net/Makefile               |    1 +
 net/wireless/Makefile      |    4 +
 net/wireless/core.c        |  158 +++++++++++++++++++++++++++++++++++++
 net/wireless/core.h        |   57 ++++++++++++++
 net/wireless/wext-compat.c |   25 ++++++
 8 files changed, 435 insertions(+), 0 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index fea0d9d..c1e9962 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -398,6 +398,7 @@ struct net_device
 	void                    *ip6_ptr;       /* IPv6 specific data */
 	void			*ec_ptr;	/* Econet specific data	*/
 	void			*ax25_ptr;	/* AX.25 specific data */
+	void			*ieee80211_ptr;	/* IEEE 802.11 specific data */
 
 /*
  * Cache line mostly used on receive path (including eth_type_trans())
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
new file mode 100644
index 0000000..c0bc9d4
--- /dev/null
+++ b/include/net/cfg80211.h
@@ -0,0 +1,186 @@
+#ifndef __NET_CFG80211_H
+#define __NET_CFG80211_H
+
+#include <linux/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <net/genetlink.h>
+
+/*
+ * 802.11 configuration in-kernel interface
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+/**
+ * struct cfg80211_config - description of a configuration (request)
+ */
+struct cfg80211_config {
+	/* first fields with 'internal' validity */
+
+	/* SSID to use, valid if not NULL, ssid_len then
+	 * contains the length. change forces reassociation */
+	s8 ssid_len;
+	u8 *ssid;
+
+	/* now fields with explicit validity */
+#define CFG80211_CFG_VALID_NWID			(1<<0)
+#define CFG80211_CFG_VALID_RX_SENSITIVITY	(1<<1)
+#define CFG80211_CFG_VALID_TRANSMIT_POWER	(1<<2)
+#define CFG80211_CFG_VALID_FRAG_THRESHOLD	(1<<3)
+#define CFG80211_CFG_VALID_CHANNEL		(1<<4)
+	unsigned int valid;
+
+	u16 network_id;
+	s32 rx_sensitivity;
+	u32 transmit_power;
+	u32 fragmentation_threshold;
+	u32 channel;
+};
+
+struct scan_channel {
+	u32 channel;
+	int active;
+};
+
+struct scan_params {
+	/* number of items in 'channels' array
+	 * or -1 to indicate scanning all channels
+	 * (in that case 'channels' is NULL) */
+	int n_channels;
+
+	/* use only when n_channels is -1 to determine
+	 * whether scanning should be active or not */
+	int active;
+
+	/* the channel list if any */
+	struct scan_channel *channels;
+};
+
+/**
+ * struct cfg80211_ops - backend description for wireless configuration
+ *
+ * This struct is registered by fullmac card drivers and/or wireless stacks
+ * in order to handle configuration requests on their interfaces.
+ *
+ * The priv pointer passed to each call is the pointer that was
+ * registered in cfg80211_register_driver().
+ *
+ * All callbacks except where otherwise noted should return 0
+ * on success or a negative error code.
+ *
+ * @list_interfaces: for each interfaces belonging to the wiphy identified
+ *		     by the priv pointer, call the one() function with the
+ *		     given data and the ifindex. This callback is required.
+ *
+ * @inject_packet: inject the given frame with the NL80211_FLAG_*
+ *		   flags onto the given queue.
+ *
+ * @add_virtual_intf: create a new virtual interface with the given name
+ *
+ * @del_virtual_intf: remove the virtual interface determined by ifindex.
+ *
+ * @configure: configure the given interface as requested in the config struct.
+ *	       must not ignore any configuration item, if something is
+ *	       is requested that cannot be fulfilled return an error
+ *
+ * @get_config: fill the given config structure with the current configuration
+ *
+ * @reassociate: reassociate with current settings (SSID, BSSID if
+ *		 userspace roaming is enabled)
+ *
+ * @disassociate: disassociate from current AP
+ *
+ * @deauth: deauth from current AP
+ *
+ * @initiate_scan: ...
+ *
+ * @set_roaming: set who gets to control roaming, the roaming_control
+ *		 parameter is passed NL80211_ROAMING_CONTROL_* values.
+ *
+ * @get_roaming: return where roaming control currently is done or
+ *		 a negative error.
+ *
+ * @set_fixed_bssid: set BSSID to use with userspace roaming, forces
+ *		     reassociation if changing.
+ * @get_fixed_bssid: get BSSID that is used with userspace roaming,
+ *		     the bssid parameter has space for 6 bytes
+ *
+ * @get_association: get BSSID of the BSS that the device is currently
+ *		     associated to and return 1, or return 0 if not
+ *		     associated (or a negative error code)
+ * @get_auth_list: get list of BSSIDs of all BSSs the device has
+ *		   authenticated with, must call next_bssid for each,
+ *		   next_bssid returns non-zero on error, the given data
+ *		   is to be passed to that callback
+ */
+struct cfg80211_ops {
+	int	(*list_interfaces)(void *priv, void *data,
+				   int (*one)(void *data, int ifindex));
+
+
+	int	(*inject_packet)(void *priv, void *frame, int framelen,
+				 u32 flags, int queue);
+
+
+	int	(*add_virtual_intf)(void *priv, char *name,
+				    unsigned int type);
+	int	(*del_virtual_intf)(void *priv, int ifindex);
+
+
+	int	(*configure)(void *priv, struct net_device *dev,
+			     struct cfg80211_config *cfg);
+	void	(*get_config)(void *priv, struct net_device *dev,
+			      struct cfg80211_config *cfg);
+
+
+	int	(*reassociate)(void *priv, struct net_device *dev);
+	int	(*disassociate)(void *priv, struct net_device *dev);
+	int	(*deauth)(void *priv, struct net_device *dev);
+
+
+	int	(*initiate_scan)(void *priv, struct net_device *dev,
+				 struct scan_params *params);
+
+
+	int	(*set_roaming)(void *priv, struct net_device *dev,
+			       int roaming_control);
+	int	(*get_roaming)(void *priv, struct net_device *dev);
+	int	(*set_fixed_bssid)(void *priv, struct net_device *dev,
+				   u8 *bssid);
+	int	(*get_fixed_bssid)(void *priv, struct net_device *dev,
+				   u8 *bssid);
+
+
+	int	(*get_association)(void *priv, struct net_device *dev,
+				   u8 *bssid);
+
+	int	(*get_auth_list)(void *priv, struct net_device *dev,
+				 void *data,
+				 int (*next_bssid)(void *data, u8 *bssid));
+};
+
+/**
+ * cfg80211_register - register a wiphy with cfg80211
+ *
+ * register a given method structure with the cfg80211 system
+ * and associate the 'priv' pointer with it.
+ *
+ * Returns a non-negative wiphy index or a negative error code.
+ *
+ * NOTE: for proper operation, this priv pointer MUST also be
+ * assigned to each &struct net_device's @ieee80211_ptr member!
+ */
+extern int cfg80211_register(struct cfg80211_ops *ops, void *priv);
+
+/**
+ * cfg80211_unregister - deregister a wiphy from cfg80211
+ *
+ * unregister a device with the given priv pointer.
+ * After this call, no more requests can be made with this priv
+ * pointer, but the call may sleep to wait for an outstanding
+ * request that is being handled.
+ */
+extern void cfg80211_unregister(void *priv);
+
+#endif /* __NET_CFG80211_H */
diff --git a/net/Kconfig b/net/Kconfig
index 7dfc949..8d121a5 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -226,6 +226,9 @@ config WIRELESS_EXT
 config FIB_RULES
 	bool
 
+config CFG80211
+	tristate "Improved wireless configuration API"
+
 endif   # if NET
 endmenu # Networking
 
diff --git a/net/Makefile b/net/Makefile
index ad4d14f..278d6bf 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_ECONET)		+= econet/
 obj-$(CONFIG_VLAN_8021Q)	+= 8021q/
 obj-$(CONFIG_IP_DCCP)		+= dccp/
 obj-$(CONFIG_IP_SCTP)		+= sctp/
+obj-$(CONFIG_CFG80211)		+= wireless/
 obj-$(CONFIG_IEEE80211)		+= ieee80211/
 obj-$(CONFIG_TIPC)		+= tipc/
 obj-$(CONFIG_NETLABEL)		+= netlabel/
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
new file mode 100644
index 0000000..4a87dce
--- /dev/null
+++ b/net/wireless/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_CFG80211) += cfg80211.o
+
+cfg80211-objs := \
+	core.o
diff --git a/net/wireless/core.c b/net/wireless/core.c
new file mode 100644
index 0000000..60b280e
--- /dev/null
+++ b/net/wireless/core.c
@@ -0,0 +1,158 @@
+/*
+ * This is the new wireless configuration interface.
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include "core.h"
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <net/genetlink.h>
+#include <net/cfg80211.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+
+MODULE_AUTHOR("Johannes Berg");
+MODULE_LICENSE("GPL");
+
+/* RCU might be appropriate here since we usually
+ * only read the list, and that can happen quite
+ * often because we need to do it for each command */
+LIST_HEAD(cfg80211_drv_list);
+DEFINE_MUTEX(cfg80211_drv_mutex);
+static int wiphy_counter;
+
+/* requires cfg80211_drv_mutex to be held! */
+static struct cfg80211_registered_driver *cfg80211_drv_by_priv(void *priv)
+{
+	struct cfg80211_registered_driver *result = NULL, *drv;
+
+	if (!priv)
+		return NULL;
+
+	list_for_each_entry(drv, &cfg80211_drv_list, list) {
+		if (drv->priv == priv) {
+			result = drv;
+			break;
+		}
+	}
+
+	return result;
+}
+
+/* wext will need this */
+struct cfg80211_registered_driver *
+cfg80211_get_drv_from_ifindex(int ifindex)
+{
+	struct cfg80211_registered_driver *drv = ERR_PTR(-ENODEV);
+	struct net_device *dev;
+
+	mutex_lock(&cfg80211_drv_mutex);
+	dev = dev_get_by_index(ifindex);
+	if (!dev)
+		goto out;
+	drv = cfg80211_drv_by_priv(dev->ieee80211_ptr);
+	if (drv)
+		mutex_lock(&drv->mtx);
+	else
+		drv = ERR_PTR(-ENODEV);
+	dev_put(dev);
+ out:
+	mutex_unlock(&cfg80211_drv_mutex);
+	return drv;
+}
+
+void cfg80211_put_drv(struct cfg80211_registered_driver *drv)
+{
+	BUG_ON(IS_ERR(drv));
+	mutex_unlock(&drv->mtx);
+}
+
+/* exported functions */
+
+int cfg80211_register(struct cfg80211_ops *ops, void *priv)
+{
+	struct cfg80211_registered_driver *drv;
+	int res;
+
+	if (!priv || !ops->list_interfaces)
+		return -EINVAL;
+
+	mutex_lock(&cfg80211_drv_mutex);
+
+	if (cfg80211_drv_by_priv(priv)) {
+		res = -EALREADY;
+		goto out_unlock;
+	}
+
+	drv = kzalloc(sizeof(struct cfg80211_registered_driver), GFP_KERNEL);
+	if (!drv) {
+		res = -ENOMEM;
+		goto out_unlock;
+	}
+
+	drv->ops = ops;
+	drv->priv = priv;
+
+	if (unlikely(wiphy_counter<0)) {
+		/* ugh, wrapped! */
+		kfree(drv);
+		res = -ENOSPC;
+		goto out_unlock;
+	}
+	mutex_init(&drv->mtx);
+	drv->wiphy = wiphy_counter;
+	list_add(&drv->list, &cfg80211_drv_list);
+	/* return wiphy number */
+	res = drv->wiphy;
+
+	/* now increase counter for the next time */
+	wiphy_counter++;
+
+ out_unlock:
+	mutex_unlock(&cfg80211_drv_mutex);
+	return res;
+}
+EXPORT_SYMBOL_GPL(cfg80211_register);
+
+void cfg80211_unregister(void *priv)
+{
+	struct cfg80211_registered_driver *drv;
+
+	mutex_lock(&cfg80211_drv_mutex);
+	drv = cfg80211_drv_by_priv(priv);
+	if (!drv) {
+		printk(KERN_ERR "deregistering cfg80211 backend that "
+		       " was never registered!\n");
+		mutex_unlock(&cfg80211_drv_mutex);
+		return;
+	}
+
+	/* hold registered driver mutex during list removal as well
+	 * to make sure no commands are in progress at the moment */
+	mutex_lock(&drv->mtx);
+	list_del(&drv->list);
+	mutex_unlock(&drv->mtx);
+
+	mutex_unlock(&cfg80211_drv_mutex);
+
+	mutex_destroy(&drv->mtx);
+	kfree(drv);
+}
+EXPORT_SYMBOL_GPL(cfg80211_unregister);
+
+/* module initialisation/exit functions */
+
+static int cfg80211_init(void)
+{
+	/* possibly need to do more later */
+	return 0;
+}
+
+static void cfg80211_exit(void)
+{
+}
+
+module_init(cfg80211_init);
+module_exit(cfg80211_exit);
diff --git a/net/wireless/core.h b/net/wireless/core.h
new file mode 100644
index 0000000..562c476
--- /dev/null
+++ b/net/wireless/core.h
@@ -0,0 +1,57 @@
+/*
+ * Wireless configuration interface internals.
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+#ifndef __NET_WIRELESS_CORE_H
+#define __NET_WIRELESS_CORE_H
+#include <net/cfg80211.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <net/genetlink.h>
+
+struct cfg80211_registered_driver {
+	struct cfg80211_ops *ops;
+	int wiphy;
+	void *priv;
+	struct list_head list;
+	/* we hold this mutex during any call so that
+	 * we cannot do multiple calls at once, and also
+	 * to avoid the deregister call to proceed while
+	 * any call is in progress */
+	struct mutex mtx;
+};
+
+extern struct mutex cfg80211_drv_mutex;
+extern struct list_head cfg80211_drv_list;
+
+/*
+ * This function returns a pointer to the driver
+ * that the genl_info item that is passed refers to.
+ * If successful, it returns non-NULL and also locks
+ * the driver's mutex!
+ *
+ * This means that you need to call cfg80211_put_drv()
+ * before being allowed to acquire &cfg80211_drv_mutex!
+ *
+ * This is necessary because we need to lock the global
+ * mutex to get an item off the list safely, and then
+ * we lock the drv mutex so it doesn't go away under us.
+ *
+ * We don't want to keep cfg80211_drv_mutex locked
+ * for all the time in order to allow requests on
+ * other interfaces to go through at the same time.
+ *
+ * The result of this can be a PTR_ERR and hence must
+ * be checked with IS_ERR() for errors.
+ */
+extern struct cfg80211_registered_driver *
+cfg80211_get_drv_from_info(struct genl_info *info);
+
+/* identical to cfg80211_get_drv_from_info but only operate on ifindex */
+extern struct cfg80211_registered_driver *
+cfg80211_get_drv_from_ifindex(int ifindex);
+
+extern void cfg80211_put_drv(struct cfg80211_registered_driver *drv);
+
+#endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
new file mode 100644
index 0000000..1c7c361
--- /dev/null
+++ b/net/wireless/wext-compat.c
@@ -0,0 +1,25 @@
+/* NOT YET */
+
+To implement compatibility, we add a new field to struct net_device
+that contains the pending configuration structure. This is dynamically
+allocated when needed and freed when committed.
+In a way it replaces the wireless_handlers field in there which is now
+done by dynamic lookup. No worries. No one is going to have thousands
+of wireless devices, and if that changes we can still trivially change
+this assumption :)
+
+Commit is done some time after the last parameter was changed
+(with each parameter change simply (re-)schedule a timer) or
+if explicitly asked for. This is probably not what most people
+would expect, but perfectly fine in the WE API.
+
+compatibility mappings:
+
+SIOCSIWAP
+  -> if bssid is all-ones: set roaming to kernel, reassociate
+  -> if bssid is all-zeroes: set roaming to kernel
+  -> otherwise: set roaming to userspace, set bssid
+
+SIOCGIWAP
+  -> get association parameters and fill return bssid appropriately
+
-- 
1.4.4.2

-- 
John W. Linville
linville@tuxdriver.com

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RFC PATCH 3/3] cfg80211: add wext-compatible client
       [not found]     ` <20070207004832.GC23096@tuxdriver.com>
@ 2007-02-07  0:49       ` John W. Linville
  2007-02-07  7:54         ` Christoph Hellwig
  0 siblings, 1 reply; 52+ messages in thread
From: John W. Linville @ 2007-02-07  0:49 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes

From: Johannes Berg <johannes@sipsolutions.net>

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Jiri Benc <jbenc@suse.cz>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
 include/linux/netdevice.h  |    7 +-
 include/net/cfg80211.h     |   34 +-
 net/Kconfig                |   28 +
 net/core/dev.c             |   38 +-
 net/core/net-sysfs.c       |    4 +-
 net/core/rtnetlink.c       |   42 ++-
 net/wireless/Makefile      |   10 +
 net/wireless/core.c        |   15 +-
 net/wireless/core.h        |   21 +
 net/wireless/wext-common.c |  610 ++++++++++++++++
 net/wireless/wext-compat.c | 1646 +++++++++++++++++++++++++++++++++++++++++++-
 net/wireless/wext-old.c    |  629 +-----------------
 net/wireless/wext.h        |   13 +
 13 files changed, 2433 insertions(+), 664 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index c1e9962..6a9b4c8 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -348,12 +348,17 @@ struct net_device
 
 	struct net_device_stats* (*get_stats)(struct net_device *dev);
 
+#ifdef CONFIG_WIRELESS_EXT
 	/* List of functions to handle Wireless Extensions (instead of ioctl).
 	 * See <net/iw_handler.h> for details. Jean II */
 	const struct iw_handler_def *	wireless_handlers;
 	/* Instance data managed by the core of Wireless Extensions. */
 	struct iw_public_data *	wireless_data;
-
+#endif
+#ifdef CONFIG_CFG80211_WEXT_COMPAT
+	/* pending config used by cfg80211/wext compat code only */
+	void *cfg80211_wext_pending_config;
+#endif
 	const struct ethtool_ops *ethtool_ops;
 
 	/*
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index c0bc9d4..353d3ce 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -5,6 +5,7 @@
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <net/genetlink.h>
+#include <linux/wireless.h>
 
 /*
  * 802.11 configuration in-kernel interface
@@ -16,21 +17,12 @@
  * struct cfg80211_config - description of a configuration (request)
  */
 struct cfg80211_config {
-	/* first fields with 'internal' validity */
+	/* see below */
+	u32 valid;
 
-	/* SSID to use, valid if not NULL, ssid_len then
-	 * contains the length. change forces reassociation */
 	s8 ssid_len;
 	u8 *ssid;
 
-	/* now fields with explicit validity */
-#define CFG80211_CFG_VALID_NWID			(1<<0)
-#define CFG80211_CFG_VALID_RX_SENSITIVITY	(1<<1)
-#define CFG80211_CFG_VALID_TRANSMIT_POWER	(1<<2)
-#define CFG80211_CFG_VALID_FRAG_THRESHOLD	(1<<3)
-#define CFG80211_CFG_VALID_CHANNEL		(1<<4)
-	unsigned int valid;
-
 	u16 network_id;
 	s32 rx_sensitivity;
 	u32 transmit_power;
@@ -38,6 +30,13 @@ struct cfg80211_config {
 	u32 channel;
 };
 
+#define CFG80211_CFG_VALID_SSID			(1<<0)
+#define CFG80211_CFG_VALID_NWID			(1<<1)
+#define CFG80211_CFG_VALID_RX_SENSITIVITY	(1<<2)
+#define CFG80211_CFG_VALID_TRANSMIT_POWER	(1<<3)
+#define CFG80211_CFG_VALID_FRAG_THRESHOLD	(1<<4)
+#define CFG80211_CFG_VALID_CHANNEL		(1<<5)
+
 struct scan_channel {
 	u32 channel;
 	int active;
@@ -86,6 +85,9 @@ struct scan_params {
  *
  * @get_config: fill the given config structure with the current configuration
  *
+ * @get_config_valid: return a bitmask of CFG80211_CFG_VALID_* indicating
+ *		      which parameters can be set.
+ *
  * @reassociate: reassociate with current settings (SSID, BSSID if
  *		 userspace roaming is enabled)
  *
@@ -132,6 +134,7 @@ struct cfg80211_ops {
 			     struct cfg80211_config *cfg);
 	void	(*get_config)(void *priv, struct net_device *dev,
 			      struct cfg80211_config *cfg);
+	u32	(*get_config_valid)(void *priv, struct net_device *dev);
 
 
 	int	(*reassociate)(void *priv, struct net_device *dev);
@@ -183,4 +186,13 @@ extern int cfg80211_register(struct cfg80211_ops *ops, void *priv);
  */
 extern void cfg80211_unregister(void *priv);
 
+#ifdef CONFIG_CFG80211_WEXT_COMPAT
+extern int cfg80211_wext_ioctl(struct ifreq *ifr, unsigned int cmd);
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+int cfg80211_wext_nl_set(struct net_device *dev, char *data, int len);
+int cfg80211_wext_nl_get(struct net_device *dev, char *data, int len,
+			 char **p_buf, int *p_len);
+#endif
+#endif
+
 #endif /* __NET_CFG80211_H */
diff --git a/net/Kconfig b/net/Kconfig
index 8d121a5..fade3fd 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -229,6 +229,34 @@ config FIB_RULES
 config CFG80211
 	tristate "Improved wireless configuration API"
 
+config CFG80211_WEXT_COMPAT
+	bool "cfg80211 Wireless Extensions compatibility"
+	depends CFG80211
+	default y
+	---help---
+	This option allows using devices whose drivers have been
+	converted to use the new cfg80211 with wireless extensions,
+	providing WE-20 compatibility. Note that cfg80211's "native"
+	interface is nl80211 using generic netlink. The wireless
+	extensions are being deprecated, but userspace tools may still
+	be using them.
+
+	If unsure, say Y.
+
+config CFG80211_WEXTNL_COMPAT
+	bool "cfg80211 WE-netlink compatibility"
+	depends CFG80211 && CFG80211_WEXT_COMPAT
+	---help---
+	This option allows using devices whose drivers have been
+	converted to use the new cfg80211 with wireless extensions
+	over rtnetlink, providing WE-20 compatibility. Note that
+	cfg80211's "native" interface is nl80211 using generic netlink.
+	The wireless extensions are being deprecated and the netlink
+	based API for WE was never configured by default, nor do any
+	userspace tools use this feature.
+
+	This option exists only to make Jean happy. Say N.
+
 endif   # if NET
 endmenu # Networking
 
diff --git a/net/core/dev.c b/net/core/dev.c
index c8822aa..31a3cba 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -116,6 +116,7 @@
 #include <linux/dmaengine.h>
 #include <linux/err.h>
 #include <linux/ctype.h>
+#include <net/cfg80211.h>
 
 /*
  *	The list of packet types we will receive (as opposed to discard)
@@ -2228,7 +2229,7 @@ static struct file_operations softnet_seq_fops = {
 	.release = seq_release,
 };
 
-#ifdef CONFIG_WIRELESS_EXT
+#if defined(CONFIG_WIRELESS_EXT) || defined(CFG80211_WEXT_COMPAT)
 extern int wireless_proc_init(void);
 #else
 #define wireless_proc_init() 0
@@ -2798,6 +2799,39 @@ int dev_ioctl(unsigned int cmd, void __user *arg)
 					ret = -EFAULT;
 				return ret;
 			}
+#ifdef CONFIG_CFG80211_WEXT_COMPAT
+			/* Take care of cfg80211 WE compatibility */
+			if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
+				/* If command is `set a parameter', or
+				 * `get the encoding parameters', check if
+				 * the user has the right to do it */
+				if (IW_IS_SET(cmd) || cmd == SIOCGIWENCODE
+				    || cmd == SIOCGIWENCODEEXT) {
+					if (!capable(CAP_NET_ADMIN))
+						return -EPERM;
+				}
+				dev_load(ifr.ifr_name);
+				rtnl_lock();
+				/* Follow me in net/wireless/wext-compat.c */
+				ret = cfg80211_wext_ioctl(&ifr, cmd);
+				rtnl_unlock();
+				if (ret == 0 && IW_IS_GET(cmd) &&
+				    copy_to_user(arg, &ifr,
+						 sizeof(struct ifreq)))
+					ret = -EFAULT;
+				/* haha, I cheat here by allowing a driver or
+				 * stack to have both WE or CFG80211-WE for
+				 * a little while during conversion... hope that
+				 * ENOSYS is only used to indicate not implemented
+				 *
+				 * if wireless extensions are not configured
+				 * then this is the last thing here so that
+				 * if we fall through we return -EINVAL
+				 */
+				if (ret != -ENOSYS)
+					return ret;
+			}
+#endif
 #ifdef CONFIG_WIRELESS_EXT
 			/* Take care of Wireless Extensions */
 			if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
@@ -2814,7 +2848,7 @@ int dev_ioctl(unsigned int cmd, void __user *arg)
 				/* Follow me in net/wireless/wext-old.c */
 				ret = wireless_process_ioctl(&ifr, cmd);
 				rtnl_unlock();
-				if (IW_IS_GET(cmd) &&
+				if (ret == 0 && IW_IS_GET(cmd) &&
 				    copy_to_user(arg, &ifr,
 					    	 sizeof(struct ifreq)))
 					ret = -EFAULT;
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index f47f319..44e69a2 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -329,7 +329,7 @@ static struct attribute_group netstat_group = {
 	.attrs  = netstat_attrs,
 };
 
-#ifdef WIRELESS_EXT
+#ifdef CONFIG_WIRELESS_EXT
 /* helper function that does all the locking etc for wireless stats */
 static ssize_t wireless_show(struct class_device *cd, char *buf,
 			     ssize_t (*format)(const struct iw_statistics *,
@@ -462,7 +462,7 @@ int netdev_register_sysfs(struct net_device *net)
 	if (net->get_stats)
 		*groups++ = &netstat_group;
 
-#ifdef WIRELESS_EXT
+#ifdef CONFIG_WIRELESS_EXT
 	if (net->wireless_handlers && net->wireless_handlers->get_wireless_stats)
 		*groups++ = &wireless_group;
 #endif
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index e76539a..45c3d39 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -56,6 +56,9 @@
 #include <linux/wireless.h>
 #include <net/iw_handler.h>
 #endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+#include <net/cfg80211.h>
+#endif
 
 static DEFINE_MUTEX(rtnl_mutex);
 static struct sock *rtnl;
@@ -536,6 +539,20 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 		modified = 1;
 	}
 
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+	if (tb[IFLA_WIRELESS]) {
+		/* Call cfg80211 WE backward compat code.
+		 * Various stuff checked in there... */
+		err = cfg80211_wext_nl_set(dev, nla_data(tb[IFLA_WIRELESS]),
+					   nla_len(tb[IFLA_WIRELESS]));
+		if (err < 0 && err != -ENOSYS)
+			goto errout_dev;
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+		if (err == 0)
+			goto skip_old_wext_nl;
+#endif
+	}
+#endif
 #ifdef CONFIG_NET_WIRELESS_RTNETLINK
 	if (tb[IFLA_WIRELESS]) {
 		/* Call Wireless Extensions.
@@ -545,8 +562,10 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 		if (err < 0)
 			goto errout_dev;
 	}
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+ skip_old_wext_nl:
+#endif
 #endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
-
 	if (tb[IFLA_BROADCAST]) {
 		nla_memcpy(dev->broadcast, tb[IFLA_BROADCAST], dev->addr_len);
 		send_addr_notify = 1;
@@ -611,6 +630,24 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 		return -EINVAL;
 
 
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+	if (tb[IFLA_WIRELESS]) {
+		/* Call Wireless Extensions. We need to know the size before
+		 * we can alloc. Various stuff checked in there... */
+		err = cfg80211_wext_nl_get(dev, nla_data(tb[IFLA_WIRELESS]),
+					   nla_len(tb[IFLA_WIRELESS]),
+					   &iw_buf, &iw_buf_len);
+		if (err < 0 && err != -ENOSYS)
+			goto errout;
+
+		iw += IW_EV_POINT_OFF;
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+		if (err == 0)
+			goto skip_old_wext_nl;
+		iw -= IW_EV_POINT_OFF;
+#endif
+	}
+#endif
 #ifdef CONFIG_NET_WIRELESS_RTNETLINK
 	if (tb[IFLA_WIRELESS]) {
 		/* Call Wireless Extensions. We need to know the size before
@@ -623,6 +660,9 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 
 		iw += IW_EV_POINT_OFF;
 	}
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+ skip_old_wext_nl:
+#endif
 #endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
 
 	nskb = nlmsg_new(if_nlmsg_size(iw_buf_len), GFP_KERNEL);
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index 663a7d8..62e67b7 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -4,3 +4,13 @@ cfg80211-objs := \
 	core.o
 
 obj-$(CONFIG_WIRELESS_EXT) += wext-old.o
+
+obj-nn :=
+obj-yy :=
+obj-yn :=
+obj-ny :=
+
+# this needs to be compiled in...
+obj-$(CONFIG_CFG80211_WEXT_COMPAT) += wext-compat.o
+obj-$(CONFIG_CFG80211_WEXT_COMPAT)$(CONFIG_NET_WIRELESS) += wext-common.o
+obj-y += $(obj-yy) $(obj-yn) $(obj-ny)
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 60b280e..ce50ae4 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -56,7 +56,7 @@ cfg80211_get_drv_from_ifindex(int ifindex)
 	if (drv)
 		mutex_lock(&drv->mtx);
 	else
-		drv = ERR_PTR(-ENODEV);
+		drv = ERR_PTR(-ENOSYS);
 	dev_put(dev);
  out:
 	mutex_unlock(&cfg80211_drv_mutex);
@@ -146,12 +146,23 @@ EXPORT_SYMBOL_GPL(cfg80211_unregister);
 
 static int cfg80211_init(void)
 {
-	/* possibly need to do more later */
+#ifdef CONFIG_CFG80211_WEXT_COMPAT
+	cfg80211_core_ops.get_drv_from_ifidx = cfg80211_get_drv_from_ifindex;
+	cfg80211_core_ops.put_drv = cfg80211_put_drv;
+	cfg80211_core_ops.module = THIS_MODULE;
+	cfg80211_core_ops.loaded = 1;
+#endif
 	return 0;
 }
 
 static void cfg80211_exit(void)
 {
+#ifdef CONFIG_CFG80211_WEXT_COMPAT
+	cfg80211_core_ops.loaded = 0;
+	cfg80211_core_ops.module = NULL;
+	cfg80211_core_ops.get_drv_from_ifidx = NULL;
+	cfg80211_core_ops.put_drv = NULL;
+#endif
 }
 
 module_init(cfg80211_init);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 562c476..595f184 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -9,6 +9,7 @@
 #include <linux/mutex.h>
 #include <linux/list.h>
 #include <net/genetlink.h>
+#include <linux/module.h>
 
 struct cfg80211_registered_driver {
 	struct cfg80211_ops *ops;
@@ -25,6 +26,26 @@ struct cfg80211_registered_driver {
 extern struct mutex cfg80211_drv_mutex;
 extern struct list_head cfg80211_drv_list;
 
+#ifdef CONFIG_CFG80211_WEXT_COMPAT
+/* wext compatibility must be compiled in...
+ * this extern is in wext-compat.c */
+struct cfg80211_core_ops {
+	/* flag to see if cfg80211 is there.
+	 * FIXME: isn't that racy? */
+	int loaded;
+
+	/* used to make sure the module isn't going away
+	 * can't really happen, except if no driver has cfg80211
+	 * in use, but in that case  */
+	struct module *module;
+
+	/* and finally these are used to do work */
+	struct cfg80211_registered_driver *(*get_drv_from_ifidx)(int ifidx);
+	void (*put_drv)(struct cfg80211_registered_driver *drv);
+};
+extern struct cfg80211_core_ops cfg80211_core_ops;
+#endif
+
 /*
  * This function returns a pointer to the driver
  * that the genl_info item that is passed refers to.
diff --git a/net/wireless/wext-common.c b/net/wireless/wext-common.c
new file mode 100644
index 0000000..c1de1d7
--- /dev/null
+++ b/net/wireless/wext-common.c
@@ -0,0 +1,610 @@
+/* common wext support routines, proc interface and events */
+
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/wireless.h>
+#include <linux/types.h>
+#include <net/iw_handler.h>
+#include <linux/seq_file.h>
+#include <net/netlink.h>
+#include <linux/rtnetlink.h>
+#include "wext.h"
+
+/* common data */
+/*
+ * Meta-data about all the standard Wireless Extension request we
+ * know about.
+ */
+const struct iw_ioctl_description standard_ioctl[] = {
+	[SIOCSIWCOMMIT	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWNAME	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_CHAR,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWNWID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWNWID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWFREQ	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_FREQ,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWFREQ	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_FREQ,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWMODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_UINT,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWMODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_UINT,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWSENS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWSENS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWRANGE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWRANGE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_range),
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWPRIV	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWPRIV	- SIOCIWFIRST] = { /* (handled directly by us) */
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct iw_priv_args),
+		.max_tokens	= 16,
+		.flags		= IW_DESCR_FLAG_NOMAX,
+	},
+	[SIOCSIWSTATS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_NULL,
+	},
+	[SIOCGIWSTATS	- SIOCIWFIRST] = { /* (handled directly by us) */
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_statistics),
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct sockaddr),
+		.max_tokens	= IW_MAX_SPY,
+	},
+	[SIOCGIWSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct sockaddr) +
+				  sizeof(struct iw_quality),
+		.max_tokens	= IW_MAX_SPY,
+	},
+	[SIOCSIWTHRSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct iw_thrspy),
+		.min_tokens	= 1,
+		.max_tokens	= 1,
+	},
+	[SIOCGIWTHRSPY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct iw_thrspy),
+		.min_tokens	= 1,
+		.max_tokens	= 1,
+	},
+	[SIOCSIWAP	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[SIOCGIWAP	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWMLME	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_mlme),
+		.max_tokens	= sizeof(struct iw_mlme),
+	},
+	[SIOCGIWAPLIST	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= sizeof(struct sockaddr) +
+				  sizeof(struct iw_quality),
+		.max_tokens	= IW_MAX_AP,
+		.flags		= IW_DESCR_FLAG_NOMAX,
+	},
+	[SIOCSIWSCAN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= 0,
+		.max_tokens	= sizeof(struct iw_scan_req),
+	},
+	[SIOCGIWSCAN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_SCAN_MAX_DATA,
+		.flags		= IW_DESCR_FLAG_NOMAX,
+	},
+	[SIOCSIWESSID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+		.flags		= IW_DESCR_FLAG_EVENT,
+	},
+	[SIOCGIWESSID	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+		.flags		= IW_DESCR_FLAG_DUMP,
+	},
+	[SIOCSIWNICKN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+	},
+	[SIOCGIWNICKN	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ESSID_MAX_SIZE,
+	},
+	[SIOCSIWRATE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWRATE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWRTS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWRTS	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWFRAG	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWFRAG	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWTXPOW	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWTXPOW	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWRETRY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWRETRY	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWENCODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ENCODING_TOKEN_MAX,
+		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
+	},
+	[SIOCGIWENCODE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_ENCODING_TOKEN_MAX,
+		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
+	},
+	[SIOCSIWPOWER	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWPOWER	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWGENIE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[SIOCGIWGENIE	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[SIOCSIWAUTH	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCGIWAUTH	- SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_PARAM,
+	},
+	[SIOCSIWENCODEEXT - SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_encode_ext),
+		.max_tokens	= sizeof(struct iw_encode_ext) +
+				  IW_ENCODING_TOKEN_MAX,
+	},
+	[SIOCGIWENCODEEXT - SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_encode_ext),
+		.max_tokens	= sizeof(struct iw_encode_ext) +
+				  IW_ENCODING_TOKEN_MAX,
+	},
+	[SIOCSIWPMKSA - SIOCIWFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.min_tokens	= sizeof(struct iw_pmksa),
+		.max_tokens	= sizeof(struct iw_pmksa),
+	},
+};
+const unsigned standard_ioctl_num = (sizeof(standard_ioctl) /
+					sizeof(struct iw_ioctl_description));
+
+/*
+ * Meta-data about all the additional standard Wireless Extension events
+ * we know about.
+ */
+const struct iw_ioctl_description standard_event[] = {
+	[IWEVTXDROP	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[IWEVQUAL	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_QUAL,
+	},
+	[IWEVCUSTOM	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_CUSTOM_MAX,
+	},
+	[IWEVREGISTERED	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[IWEVEXPIRED	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_ADDR,
+	},
+	[IWEVGENIE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[IWEVMICHAELMICFAILURE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_michaelmicfailure),
+	},
+	[IWEVASSOCREQIE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[IWEVASSOCRESPIE	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= IW_GENERIC_IE_MAX,
+	},
+	[IWEVPMKIDCAND	- IWEVFIRST] = {
+		.header_type	= IW_HEADER_TYPE_POINT,
+		.token_size	= 1,
+		.max_tokens	= sizeof(struct iw_pmkid_cand),
+	},
+};
+unsigned standard_event_num = (sizeof(standard_event) /
+					sizeof(struct iw_ioctl_description));
+
+/* Size (in bytes) of various events */
+const int event_type_size[] = {
+	IW_EV_LCP_LEN,			/* IW_HEADER_TYPE_NULL */
+	0,
+	IW_EV_CHAR_LEN,			/* IW_HEADER_TYPE_CHAR */
+	0,
+	IW_EV_UINT_LEN,			/* IW_HEADER_TYPE_UINT */
+	IW_EV_FREQ_LEN,			/* IW_HEADER_TYPE_FREQ */
+	IW_EV_ADDR_LEN,			/* IW_HEADER_TYPE_ADDR */
+	0,
+	IW_EV_POINT_LEN,		/* Without variable payload */
+	IW_EV_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */
+	IW_EV_QUAL_LEN,			/* IW_HEADER_TYPE_QUAL */
+};
+
+
+struct iw_statistics *get_wireless_stats(struct net_device *dev,
+					 struct iw_statistics *out)
+{
+#ifdef CONFIG_CFG80211
+	if (dev->ieee80211_ptr && out) {
+		/* bah, just fake some stuff for now */
+		memset(out, 0, sizeof(*out));
+		return out;
+	}
+#endif
+#ifdef CONFIG_WIRELESS_EXT
+	if ((dev->wireless_handlers != NULL) &&
+	    (dev->wireless_handlers->get_wireless_stats != NULL))
+		return dev->wireless_handlers->get_wireless_stats(dev);
+#endif
+	return NULL;
+}
+
+/*
+ * The /proc/net/wireless file is a human readable user-space interface
+ * exporting various wireless specific statistics from the wireless devices.
+ * This is the most popular part of the Wireless Extensions ;-)
+ *
+ * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
+ * The content of the file is basically the content of "struct iw_statistics".
+ */
+
+#ifdef CONFIG_PROC_FS
+
+/*
+ * Print one entry (line) of /proc/net/wireless
+ */
+static void wireless_seq_printf_stats(struct seq_file *seq,
+				      struct net_device *dev)
+{
+	/* Get stats from the driver */
+	struct iw_statistics stats_buf;
+	struct iw_statistics *stats = get_wireless_stats(dev, &stats_buf);
+
+	if (stats) {
+		seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
+				"%6d %6d   %6d\n",
+			   dev->name, stats->status, stats->qual.qual,
+			   stats->qual.updated & IW_QUAL_QUAL_UPDATED
+			   ? '.' : ' ',
+			   ((__s32) stats->qual.level) -
+			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+			   stats->qual.updated & IW_QUAL_LEVEL_UPDATED
+			   ? '.' : ' ',
+			   ((__s32) stats->qual.noise) -
+			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+			   stats->qual.updated & IW_QUAL_NOISE_UPDATED
+			   ? '.' : ' ',
+			   stats->discard.nwid, stats->discard.code,
+			   stats->discard.fragment, stats->discard.retries,
+			   stats->discard.misc, stats->miss.beacon);
+		stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+	}
+}
+
+/*
+ * Print info for /proc/net/wireless (print all entries)
+ */
+static int wireless_seq_show(struct seq_file *seq, void *v)
+{
+	if (v == SEQ_START_TOKEN)
+		seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
+				"packets               | Missed | WE\n"
+				" face | tus | link level noise |  nwid  "
+				"crypt   frag  retry   misc | beacon | %d\n",
+			   WIRELESS_EXT);
+	else
+		wireless_seq_printf_stats(seq, v);
+	return 0;
+}
+
+static struct seq_operations wireless_seq_ops = {
+	.start = dev_seq_start,
+	.next  = dev_seq_next,
+	.stop  = dev_seq_stop,
+	.show  = wireless_seq_show,
+};
+
+static int wireless_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &wireless_seq_ops);
+}
+
+static struct file_operations wireless_seq_fops = {
+	.owner	 = THIS_MODULE,
+	.open    = wireless_seq_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+int __init wireless_proc_init(void)
+{
+	/* Create /proc/net/wireless entry */
+	if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
+		return -ENOMEM;
+
+	return 0;
+}
+#endif	/* CONFIG_PROC_FS */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Locking...
+ * ----------
+ *
+ * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
+ * the locking issue in here and implementing this code !
+ *
+ * The issue : wireless_send_event() is often called in interrupt context,
+ * while the Netlink layer can never be called in interrupt context.
+ * The fully formed RtNetlink events are queued, and then a tasklet is run
+ * to feed those to Netlink.
+ * The skb_queue is interrupt safe, and its lock is not held while calling
+ * Netlink, so there is no possibility of dealock.
+ * Jean II
+ */
+
+static struct sk_buff_head wireless_nlevent_queue;
+
+static int __init wireless_nlevent_init(void)
+{
+	skb_queue_head_init(&wireless_nlevent_queue);
+	return 0;
+}
+
+subsys_initcall(wireless_nlevent_init);
+
+static void wireless_nlevent_process(unsigned long data)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&wireless_nlevent_queue)))
+		rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
+}
+
+static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
+
+/* ---------------------------------------------------------------- */
+/*
+ * Fill a rtnetlink message with our event data.
+ * Note that we propage only the specified event and don't dump the
+ * current wireless config. Dumping the wireless config is far too
+ * expensive (for each parameter, the driver need to query the hardware).
+ */
+static inline int rtnetlink_fill_iwinfo(struct sk_buff *	skb,
+					struct net_device *	dev,
+					int			type,
+					char *			event,
+					int			event_len)
+{
+	struct ifinfomsg *r;
+	struct nlmsghdr  *nlh;
+	unsigned char	 *b = skb->tail;
+
+	nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
+	r = NLMSG_DATA(nlh);
+	r->ifi_family = AF_UNSPEC;
+	r->__ifi_pad = 0;
+	r->ifi_type = dev->type;
+	r->ifi_index = dev->ifindex;
+	r->ifi_flags = dev_get_flags(dev);
+	r->ifi_change = 0;	/* Wireless changes don't affect those flags */
+
+	/* Add the wireless events in the netlink packet */
+	RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
+
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+	skb_trim(skb, b - skb->data);
+	return -1;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Create and broadcast and send it on the standard rtnetlink socket
+ * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c
+ * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
+ * within a RTM_NEWLINK event.
+ */
+static inline void rtmsg_iwinfo(struct net_device *	dev,
+				char *			event,
+				int			event_len)
+{
+	struct sk_buff *skb;
+	int size = NLMSG_GOODSIZE;
+
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (!skb)
+		return;
+
+	if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
+				  event, event_len) < 0) {
+		kfree_skb(skb);
+		return;
+	}
+	NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
+	skb_queue_tail(&wireless_nlevent_queue, skb);
+	tasklet_schedule(&wireless_nlevent_tasklet);
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main event dispatcher. Called from other parts and drivers.
+ * Send the event on the appropriate channels.
+ * May be called from interrupt context.
+ */
+void wireless_send_event(struct net_device *	dev,
+			 unsigned int		cmd,
+			 union iwreq_data *	wrqu,
+			 char *			extra)
+{
+	const struct iw_ioctl_description *	descr = NULL;
+	int extra_len = 0;
+	struct iw_event  *event;		/* Mallocated whole event */
+	int event_len;				/* Its size */
+	int hdr_len;				/* Size of the event header */
+	int wrqu_off = 0;			/* Offset in wrqu */
+	/* Don't "optimise" the following variable, it will crash */
+	unsigned	cmd_index;		/* *MUST* be unsigned */
+
+	/* Get the description of the Event */
+	if(cmd <= SIOCIWLAST) {
+		cmd_index = cmd - SIOCIWFIRST;
+		if(cmd_index < standard_ioctl_num)
+			descr = &(standard_ioctl[cmd_index]);
+	} else {
+		cmd_index = cmd - IWEVFIRST;
+		if(cmd_index < standard_event_num)
+			descr = &(standard_event[cmd_index]);
+	}
+	/* Don't accept unknown events */
+	if(descr == NULL) {
+		/* Note : we don't return an error to the driver, because
+		 * the driver would not know what to do about it. It can't
+		 * return an error to the user, because the event is not
+		 * initiated by a user request.
+		 * The best the driver could do is to log an error message.
+		 * We will do it ourselves instead...
+		 */
+		printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
+		       dev->name, cmd);
+		return;
+	}
+
+	/* Check extra parameters and set extra_len */
+	if(descr->header_type == IW_HEADER_TYPE_POINT) {
+		/* Check if number of token fits within bounds */
+		if(wrqu->data.length > descr->max_tokens) {
+			printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
+			return;
+		}
+		if(wrqu->data.length < descr->min_tokens) {
+			printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
+			return;
+		}
+		/* Calculate extra_len - extra is NULL for restricted events */
+		if(extra != NULL)
+			extra_len = wrqu->data.length * descr->token_size;
+		/* Always at an offset in wrqu */
+		wrqu_off = IW_EV_POINT_OFF;
+	}
+
+	/* Total length of the event */
+	hdr_len = event_type_size[descr->header_type];
+	event_len = hdr_len + extra_len;
+
+	/* Create temporary buffer to hold the event */
+	event = kmalloc(event_len, GFP_ATOMIC);
+	if(event == NULL)
+		return;
+
+	/* Fill event */
+	event->len = event_len;
+	event->cmd = cmd;
+	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
+	if(extra != NULL)
+		memcpy(((char *) event) + hdr_len, extra, extra_len);
+
+	/* Send via the RtNetlink event channel */
+	rtmsg_iwinfo(dev, (char *) event, event_len);
+
+	/* Cleanup */
+	kfree(event);
+
+	return;		/* Always success, I guess ;-) */
+}
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 1c7c361..0cf6ce9 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -1,25 +1,1633 @@
-/* NOT YET */
+/*
+ * wireless extensions compatibility for cfg80211.
+ *
+ * Lots of code from the original wireless.c:
+ * Copyright 1997-2006	Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ * Copyright 2006	Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPLv2.
+ *
+ * Theory of operation, so to speak:
+ *
+ * To implement compatibility, I added a new field to struct net_device
+ * that contains the pending configuration structure. This is dynamically
+ * allocated when needed and freed when committed.
+ *
+ * Commit is done some time after the last parameter was changed
+ * (with each parameter change simply (re-)schedule a timer) or
+ * if explicitly asked for. This is probably not what most people
+ * would expect, but perfectly fine in the WE API.
+ *
+ * NB: we leak memory if the user
+ *      - changes some settings
+ *      - quickly rmmod's the module so that the net device is destroyed
+ * Since only root can do it and I don't see a way to hook into
+ * the net device's destruction... tough.
+ *
+ * NB2: Note that each of the wrappers should check if the cfg80211
+ * user provides the command, and for configure() it must also check
+ * if that parameter can be set or not via get_config_valid()
+ *
+ * NB3: It's really bad that we can't report an error from the timer-
+ * based commit... Hopefully get_config_valid() can catch everything?
+ *
+ * see set_essid for an example
+ *
+ * another question I just thought about.. does wext expect to see
+ * the new config even if it wasn't committed... if so, we need to
+ * look at the pending config in various _get_ calls...
+ */
 
-To implement compatibility, we add a new field to struct net_device
-that contains the pending configuration structure. This is dynamically
-allocated when needed and freed when committed.
-In a way it replaces the wireless_handlers field in there which is now
-done by dynamic lookup. No worries. No one is going to have thousands
-of wireless devices, and if that changes we can still trivially change
-this assumption :)
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <net/netlink.h>
+#include <asm/uaccess.h>
+#include <net/cfg80211.h>
 
-Commit is done some time after the last parameter was changed
-(with each parameter change simply (re-)schedule a timer) or
-if explicitly asked for. This is probably not what most people
-would expect, but perfectly fine in the WE API.
+#include "core.h"
+#include "wext.h"
 
-compatibility mappings:
+/* The cfg80211 driver assigns callbacks in this
+ * if it is loaded. If not, then we can't config
+ * anything anyway... */
+struct cfg80211_core_ops cfg80211_core_ops;
+EXPORT_SYMBOL_GPL(cfg80211_core_ops);
 
-SIOCSIWAP
-  -> if bssid is all-ones: set roaming to kernel, reassociate
-  -> if bssid is all-zeroes: set roaming to kernel
-  -> otherwise: set roaming to userspace, set bssid
+static struct cfg80211_registered_driver *cfg80211_wx_setup(int ifindex)
+{
+	if (!cfg80211_core_ops.loaded)
+		return ERR_PTR(-ENOSYS);
+	if (!try_module_get(cfg80211_core_ops.module))
+		return ERR_PTR(-ENOSYS);
 
-SIOCGIWAP
-  -> get association parameters and fill return bssid appropriately
+	return cfg80211_core_ops.get_drv_from_ifidx(ifindex);
+}
 
+static void cfg80211_wx_teardown(struct cfg80211_registered_driver *drv)
+{
+	if (!IS_ERR(drv))
+		cfg80211_core_ops.put_drv(drv);
+	module_put(cfg80211_core_ops.module);
+}
+
+/* internal API: use this function when changing
+ * some parameter that needs to be committed */
+static void cfg80211_wx_start_commit_timer(int ifindex)
+{
+	/* TODO:
+	 * start a timer associate with this interface
+	 * and then when it expires commit the pending
+	 * data...
+	 * This function must be callable when the timer
+	 * is already running, and the timer must
+	 * be able to deal with an unassigned
+	 * dev->cfg80211_wext_pending_config pointer
+	 * as well as taking the rtnl lock (due to wext)! */
+}
+
+static void cfg80211_ensure_netdev_pending_cfg(struct net_device *dev)
+{
+	struct cfg80211_config *cfg = dev->cfg80211_wext_pending_config;
+	if (!cfg) {
+		cfg = kmalloc(sizeof(*cfg)+32, GFP_KERNEL);
+		cfg->ssid = (char*)cfg + sizeof(*cfg);
+		dev->cfg80211_wext_pending_config = cfg;
+	}
+}
+
+/* operations we implement. whew, I machine-generated these */
+static int cfg80211_wx_set_commit(struct net_device *net_dev,
+				  struct iw_request_info *info,
+				  union iwreq_data *data,
+				  char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	if (!net_dev->cfg80211_wext_pending_config) {
+		err = 0;
+		goto out;
+	}
+
+	err = drv->ops->configure(drv->priv, net_dev,
+				  net_dev->cfg80211_wext_pending_config);
+
+	kfree(net_dev->cfg80211_wext_pending_config);
+	net_dev->cfg80211_wext_pending_config = NULL;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_name(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_nwid(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_nwid(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_freq(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_freq(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_mode(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_mode(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_sens(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_sens(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_range(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_range(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_ap(struct net_device *net_dev,
+			      struct iw_request_info *info,
+			      union iwreq_data *data,
+			      char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	/* SIOCSIWAP
+	 *   -> if bssid is all-ones: set roaming to kernel, reassociate
+	 *   -> if bssid is all-zeroes: set roaming to kernel
+	 *   -> otherwise: set roaming to userspace, set bssid
+	 */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_ap(struct net_device *net_dev,
+			      struct iw_request_info *info,
+			      union iwreq_data *data,
+			      char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	/* SIOCGIWAP
+	 *   -> get association parameters and fill return bssid appropriately
+	 */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_mlme(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_waplist(struct net_device *net_dev,
+				   struct iw_request_info *info,
+				   union iwreq_data *data,
+				   char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_scan(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_scan(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_essid(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct cfg80211_config *cfg;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	err = -ENOSYS;
+	if (!drv->ops->configure || !drv->ops->get_config_valid)
+		goto out;
+	if (!(drv->ops->get_config_valid(drv->priv, net_dev)
+			& CFG80211_CFG_VALID_SSID))
+		goto out;
+
+	cfg80211_ensure_netdev_pending_cfg(net_dev);
+	cfg = net_dev->cfg80211_wext_pending_config;
+	if (!cfg) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	memcpy(cfg->ssid, extra, data->essid.length);
+	cfg->ssid_len = data->essid.length;
+	cfg->valid |= CFG80211_CFG_VALID_SSID;
+
+	cfg80211_wx_start_commit_timer(net_dev->ifindex);
+	err = 0;
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_essid(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_rate(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_rate(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_rts(struct net_device *net_dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *data,
+			       char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_rts(struct net_device *net_dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *data,
+			       char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_frag(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_frag(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_txpow(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_txpow(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_retry(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_retry(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_encode(struct net_device *net_dev,
+				  struct iw_request_info *info,
+				  union iwreq_data *data,
+				  char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_encode(struct net_device *net_dev,
+				  struct iw_request_info *info,
+				  union iwreq_data *data,
+				  char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_power(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_power(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_genie(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_genie(struct net_device *net_dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *data,
+				 char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_auth(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_auth(struct net_device *net_dev,
+			        struct iw_request_info *info,
+			        union iwreq_data *data,
+			        char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_encodeext(struct net_device *net_dev,
+				     struct iw_request_info *info,
+				     union iwreq_data *data,
+				     char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_get_encodeext(struct net_device *net_dev,
+				     struct iw_request_info *info,
+				     union iwreq_data *data,
+				     char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+static int cfg80211_wx_set_wpmksa(struct net_device *net_dev,
+				  struct iw_request_info *info,
+				  union iwreq_data *data,
+				  char *extra)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+
+	drv = cfg80211_wx_setup(net_dev->ifindex);
+	if (IS_ERR(drv)) {
+		err = PTR_ERR(drv);
+		goto out;
+	}
+
+	/* TODO: DO SOMETHING */
+	err = -ENOSYS;
+
+ out:
+	cfg80211_wx_teardown(drv);
+	return err;
+}
+
+
+
+/* operations array */
+#ifdef WX
+# undef WX
+#endif
+#define WX(ioctl)  [(ioctl) - SIOCIWFIRST]
+static const iw_handler cfg80211_wx_handlers[] = {
+	WX(SIOCSIWCOMMIT)	= cfg80211_wx_set_commit,
+	WX(SIOCGIWNAME)		= cfg80211_wx_get_name,
+	WX(SIOCSIWNWID)		= cfg80211_wx_set_nwid,
+	WX(SIOCGIWNWID)		= cfg80211_wx_get_nwid,
+	WX(SIOCSIWFREQ)		= cfg80211_wx_set_freq,
+	WX(SIOCGIWFREQ)		= cfg80211_wx_get_freq,
+	WX(SIOCSIWMODE)		= cfg80211_wx_set_mode,
+	WX(SIOCGIWMODE)		= cfg80211_wx_get_mode,
+	WX(SIOCSIWSENS)		= cfg80211_wx_set_sens,
+	WX(SIOCGIWSENS)		= cfg80211_wx_get_sens,
+	WX(SIOCSIWRANGE)	= cfg80211_wx_set_range,
+	WX(SIOCGIWRANGE)	= cfg80211_wx_get_range,
+	WX(SIOCSIWAP)		= cfg80211_wx_set_ap,
+	WX(SIOCGIWAP)		= cfg80211_wx_get_ap,
+	WX(SIOCSIWMLME)		= cfg80211_wx_set_mlme,
+	WX(SIOCGIWAPLIST)	= cfg80211_wx_get_waplist,
+	WX(SIOCSIWSCAN)		= cfg80211_wx_set_scan,
+	WX(SIOCGIWSCAN)		= cfg80211_wx_get_scan,
+	WX(SIOCSIWESSID)	= cfg80211_wx_set_essid,
+	WX(SIOCGIWESSID)	= cfg80211_wx_get_essid,
+	WX(SIOCSIWRATE)		= cfg80211_wx_set_rate,
+	WX(SIOCGIWRATE)		= cfg80211_wx_get_rate,
+	WX(SIOCSIWRTS)		= cfg80211_wx_set_rts,
+	WX(SIOCGIWRTS)		= cfg80211_wx_get_rts,
+	WX(SIOCSIWFRAG)		= cfg80211_wx_set_frag,
+	WX(SIOCGIWFRAG)		= cfg80211_wx_get_frag,
+	WX(SIOCSIWTXPOW)	= cfg80211_wx_set_txpow,
+	WX(SIOCGIWTXPOW)	= cfg80211_wx_get_txpow,
+	WX(SIOCSIWRETRY)	= cfg80211_wx_set_retry,
+	WX(SIOCGIWRETRY)	= cfg80211_wx_get_retry,
+	WX(SIOCSIWENCODE)	= cfg80211_wx_set_encode,
+	WX(SIOCGIWENCODE)	= cfg80211_wx_get_encode,
+	WX(SIOCSIWPOWER)	= cfg80211_wx_set_power,
+	WX(SIOCGIWPOWER)	= cfg80211_wx_get_power,
+	WX(SIOCSIWGENIE)	= cfg80211_wx_set_genie,
+	WX(SIOCGIWGENIE)	= cfg80211_wx_get_genie,
+	WX(SIOCSIWAUTH)		= cfg80211_wx_set_auth,
+	WX(SIOCGIWAUTH)		= cfg80211_wx_get_auth,
+	WX(SIOCSIWENCODEEXT)	= cfg80211_wx_set_encodeext,
+	WX(SIOCGIWENCODEEXT)	= cfg80211_wx_get_encodeext,
+	WX(SIOCSIWPMKSA)	= cfg80211_wx_set_wpmksa,
+};
+
+/* dummy so I didn't have to change that much code... */
+static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
+{
+	int idx = cmd - SIOCIWFIRST;
+	if (idx < ARRAY_SIZE(cfg80211_wx_handlers))
+		return cfg80211_wx_handlers[idx];
+	return NULL;
+}
+
+/*
+ * this is sort of backwards and wouldn't need to call
+ * get_wireless_stats, but it was easier to just copy the code...
+ */
+static int iw_handler_get_iwstats(struct net_device *		dev,
+				  struct iw_request_info *	info,
+				  union iwreq_data *		wrqu,
+				  char *			extra)
+{
+	/* Get stats from the driver */
+	struct iw_statistics stats_buf;
+	struct iw_statistics *stats;
+
+	stats = get_wireless_stats(dev, &stats_buf);
+	if (stats != (struct iw_statistics *) NULL) {
+
+		/* Copy statistics to extra */
+		memcpy(extra, stats, sizeof(struct iw_statistics));
+		wrqu->data.length = sizeof(struct iw_statistics);
+
+		/* Check if we need to clear the updated flag */
+		if(wrqu->data.flags != 0)
+			stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+		return 0;
+	} else
+		return -EOPNOTSUPP;
+}
+
+#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
+/*
+ * Wrapper to call a standard Wireless Extension GET handler.
+ * We do various checks and call the handler with the proper args.
+ */
+static int rtnetlink_standard_get(struct net_device *	dev,
+				  struct iw_event *	request,
+				  int			request_len,
+				  iw_handler		handler,
+				  char **		p_buf,
+				  int *			p_len)
+{
+	const struct iw_ioctl_description *	descr = NULL;
+	unsigned int				cmd;
+	union iwreq_data *			wrqu;
+	int					hdr_len;
+	struct iw_request_info			info;
+	char *					buffer = NULL;
+	int					buffer_size = 0;
+	int					ret = -EINVAL;
+
+	/* Get the description of the Request */
+	cmd = request->cmd;
+	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+		return -EOPNOTSUPP;
+	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+	/* Check if wrqu is complete */
+	hdr_len = event_type_size[descr->header_type];
+	if(request_len < hdr_len)
+		return -EINVAL;
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have extra data in the reply or not */
+	if(descr->header_type != IW_HEADER_TYPE_POINT) {
+
+		/* Create the kernel buffer that we will return.
+		 * It's at an offset to match the TYPE_POINT case... */
+		buffer_size = request_len + IW_EV_POINT_OFF;
+		buffer = kmalloc(buffer_size, GFP_KERNEL);
+		if (buffer == NULL) {
+			return -ENOMEM;
+		}
+		/* Copy event data */
+		memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
+		/* Use our own copy of wrqu */
+		wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
+					     + IW_EV_LCP_LEN);
+
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, wrqu, NULL);
+
+	} else {
+		union iwreq_data	wrqu_point;
+		char *			extra = NULL;
+		int			extra_size = 0;
+
+		/* Get a temp copy of wrqu (skip pointer) */
+		memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
+		       ((char *) request) + IW_EV_LCP_LEN,
+		       IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+
+		/* Calculate space needed by arguments. Always allocate
+		 * for max space. Easier, and won't last long... */
+		extra_size = descr->max_tokens * descr->token_size;
+		/* Support for very large requests */
+		if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+		   (wrqu_point.data.length > descr->max_tokens))
+			extra_size = (wrqu_point.data.length
+				      * descr->token_size);
+		buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
+
+		/* Create the kernel buffer that we will return */
+		buffer = kmalloc(buffer_size, GFP_KERNEL);
+		if (buffer == NULL) {
+			return -ENOMEM;
+		}
+
+		/* Put wrqu in the right place (just before extra).
+		 * Leave space for IWE header and dummy pointer...
+		 * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
+		 */
+		memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
+		       ((char *) &wrqu_point) + IW_EV_POINT_OFF,
+		       IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+		wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
+
+		/* Extra comes logically after that. Offset +12 bytes. */
+		extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
+
+		/* Call the handler */
+		ret = handler(dev, &info, wrqu, extra);
+
+		/* Calculate real returned length */
+		extra_size = (wrqu->data.length * descr->token_size);
+		/* Re-adjust reply size */
+		request->len = extra_size + IW_EV_POINT_LEN;
+
+		/* Put the iwe header where it should, i.e. scrap the
+		 * dummy pointer. */
+		memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
+
+		/* Check if there is enough buffer up there */
+		if(wrqu_point.data.length < wrqu->data.length)
+			ret = -E2BIG;
+	}
+
+	/* Return the buffer to the caller */
+	if (!ret) {
+		*p_buf = buffer;
+		*p_len = request->len;
+	} else {
+		/* Cleanup */
+		if(buffer)
+			kfree(buffer);
+	}
+
+	return ret;
+}
+
+/*
+ * Wrapper to call a standard Wireless Extension SET handler.
+ * We do various checks and call the handler with the proper args.
+ */
+static inline int rtnetlink_standard_set(struct net_device *	dev,
+					 struct iw_event *	request,
+					 int			request_len,
+					 iw_handler		handler)
+{
+	const struct iw_ioctl_description *	descr = NULL;
+	unsigned int				cmd;
+	union iwreq_data *			wrqu;
+	union iwreq_data			wrqu_point;
+	int					hdr_len;
+	char *					extra = NULL;
+	int					extra_size = 0;
+	struct iw_request_info			info;
+	int					ret = -EINVAL;
+
+	/* Get the description of the Request */
+	cmd = request->cmd;
+	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+		return -EOPNOTSUPP;
+	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+	/* Extract fixed header from request. This is properly aligned. */
+	wrqu = &request->u;
+
+	/* Check if wrqu is complete */
+	hdr_len = event_type_size[descr->header_type];
+	if(request_len < hdr_len)
+		return -EINVAL;
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have extra data in the request or not */
+	if(descr->header_type != IW_HEADER_TYPE_POINT) {
+
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, wrqu, NULL);
+
+	} else {
+		int	extra_len;
+
+		/* Put wrqu in the right place (skip pointer) */
+		memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
+		       wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+		/* Don't forget about the event code... */
+		wrqu = &wrqu_point;
+
+		/* Check if number of token fits within bounds */
+		if(wrqu_point.data.length > descr->max_tokens)
+			return -E2BIG;
+		if(wrqu_point.data.length < descr->min_tokens)
+			return -EINVAL;
+
+		/* Real length of payload */
+		extra_len = wrqu_point.data.length * descr->token_size;
+
+		/* Check if request is self consistent */
+		if((request_len - hdr_len) < extra_len)
+			return -EINVAL;
+
+		/* Always allocate for max space. Easier, and won't last
+		 * long... */
+		extra_size = descr->max_tokens * descr->token_size;
+		extra = kmalloc(extra_size, GFP_KERNEL);
+		if (extra == NULL)
+			return -ENOMEM;
+
+		/* Copy extra in aligned buffer */
+		memcpy(extra, ((char *) request) + hdr_len, extra_len);
+
+		/* Call the handler */
+		ret = handler(dev, &info, &wrqu_point, extra);
+	}
+
+        /* Generate an event to notify listeners of the change */
+	if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
+	    ((ret == 0) || (ret == -EIWCOMMIT))) {
+		if(descr->flags & IW_DESCR_FLAG_RESTRICT)
+			/* If the event is restricted, don't
+			 * export the payload */
+			wireless_send_event(dev, cmd, wrqu, NULL);
+		else
+			wireless_send_event(dev, cmd, wrqu, extra);
+	}
+
+	/* Cleanup - I told you it wasn't that long ;-) */
+	if(extra)
+		kfree(extra);
+
+	return ret;
+}
+
+/*
+ * Main RtNetlink dispatcher. Called from the main networking code
+ * (do_getlink() in net/core/rtnetlink.c).
+ * Check the type of Request and call the appropriate wrapper...
+ */
+int cfg80211_wext_nl_get(struct net_device *	dev,
+			 char *			data,
+			 int			len,
+			 char **		p_buf,
+			 int *			p_len)
+{
+	struct iw_event *	request = (struct iw_event *) data;
+	iw_handler		handler;
+
+	/* Check length */
+	if(len < IW_EV_LCP_LEN) {
+		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
+		       dev->name, len);
+		return -EINVAL;
+	}
+
+	/* ReCheck length (len may have padding) */
+	if(request->len > len) {
+		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
+		       dev->name, request->len, len);
+		return -EINVAL;
+	}
+
+	/* Only accept GET requests in here */
+	if(!IW_IS_GET(request->cmd))
+		return -EOPNOTSUPP;
+
+	/* If command is `get the encoding parameters', check if
+	 * the user has the right to do it */
+	if (request->cmd == SIOCGIWENCODE ||
+	    request->cmd == SIOCGIWENCODEEXT) {
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+	}
+
+	/* Special cases */
+	if(request->cmd == SIOCGIWSTATS)
+		/* Get Wireless Stats */
+		return rtnetlink_standard_get(dev,
+					      request,
+					      request->len,
+					      &iw_handler_get_iwstats,
+					      p_buf, p_len);
+	if(request->cmd == SIOCGIWPRIV)
+		return -EOPNOTSUPP;
+
+	/* Basic check */
+	if (!netif_device_present(dev))
+		return -ENODEV;
+
+	/* Try to find the handler */
+	handler = get_handler(dev, request->cmd);
+	if (handler != NULL && request->cmd < SIOCIWFIRSTPRIV)
+		return rtnetlink_standard_get(dev,
+					      request,
+					      request->len,
+					      handler,
+					      p_buf, p_len);
+
+	return -EOPNOTSUPP;
+}
+
+/*
+ * Main RtNetlink dispatcher. Called from the main networking code
+ * (do_setlink() in net/core/rtnetlink.c).
+ * Check the type of Request and call the appropriate wrapper...
+ */
+int cfg80211_wext_nl_set(struct net_device *	dev,
+			 char *			data,
+			 int			len)
+{
+	struct iw_event *	request = (struct iw_event *) data;
+	iw_handler		handler;
+
+	/* Check length */
+	if(len < IW_EV_LCP_LEN) {
+		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
+		       dev->name, len);
+		return -EINVAL;
+	}
+
+	/* ReCheck length (len may have padding) */
+	if(request->len > len) {
+		printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
+		       dev->name, request->len, len);
+		return -EINVAL;
+	}
+
+	/* Only accept SET requests in here */
+	if(!IW_IS_SET(request->cmd))
+		return -EOPNOTSUPP;
+
+	/* Basic check */
+	if (!netif_device_present(dev))
+		return -ENODEV;
+
+	/* New driver API : try to find the handler */
+	handler = get_handler(dev, request->cmd);
+	if(handler != NULL && request->cmd < SIOCIWFIRSTPRIV)
+		return rtnetlink_standard_set(dev,
+					      request,
+					      request->len,
+					      handler);
+
+	return -EOPNOTSUPP;
+}
+#endif
+
+/*
+ * Wrapper to call a standard Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ */
+static int ioctl_standard_call(struct net_device *	dev,
+			       struct ifreq *		ifr,
+			       unsigned int		cmd,
+			       iw_handler		handler)
+{
+	struct iwreq *				iwr = (struct iwreq *) ifr;
+	const struct iw_ioctl_description *	descr;
+	struct iw_request_info			info;
+	int					ret = -EINVAL;
+
+	/* Get the description of the IOCTL */
+	if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+		return -EOPNOTSUPP;
+	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+	/* Prepare the call */
+	info.cmd = cmd;
+	info.flags = 0;
+
+	/* Check if we have a pointer to user space data or not */
+	if(descr->header_type != IW_HEADER_TYPE_POINT) {
+
+		/* No extra arguments. Trivial to handle */
+		ret = handler(dev, &info, &(iwr->u), NULL);
+
+		/* Generate an event to notify listeners of the change */
+		if((descr->flags & IW_DESCR_FLAG_EVENT) &&
+		   ((ret == 0) || (ret == -EIWCOMMIT)))
+			wireless_send_event(dev, cmd, &(iwr->u), NULL);
+	} else {
+		char *	extra;
+		int	extra_size;
+		int	user_length = 0;
+		int	err;
+
+		/* Calculate space needed by arguments. Always allocate
+		 * for max space. Easier, and won't last long... */
+		extra_size = descr->max_tokens * descr->token_size;
+
+		/* Check what user space is giving us */
+		if(IW_IS_SET(cmd)) {
+			/* Check NULL pointer */
+			if((iwr->u.data.pointer == NULL) &&
+			   (iwr->u.data.length != 0))
+				return -EFAULT;
+			/* Check if number of token fits within bounds */
+			if(iwr->u.data.length > descr->max_tokens)
+				return -E2BIG;
+			if(iwr->u.data.length < descr->min_tokens)
+				return -EINVAL;
+		} else {
+			/* Check NULL pointer */
+			if(iwr->u.data.pointer == NULL)
+				return -EFAULT;
+			/* Save user space buffer size for checking */
+			user_length = iwr->u.data.length;
+
+			/* Don't check if user_length > max to allow forward
+			 * compatibility. The test user_length < min is
+			 * implied by the test at the end. */
+
+			/* Support for very large requests */
+			if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+			   (user_length > descr->max_tokens)) {
+				/* Allow userspace to GET more than max so
+				 * we can support any size GET requests.
+				 * There is still a limit : -ENOMEM. */
+				extra_size = user_length * descr->token_size;
+				/* Note : user_length is originally a __u16,
+				 * and token_size is controlled by us,
+				 * so extra_size won't get negative and
+				 * won't overflow... */
+			}
+		}
+
+		/* Create the kernel buffer */
+		extra = kmalloc(extra_size, GFP_KERNEL);
+		if (extra == NULL) {
+			return -ENOMEM;
+		}
+
+		/* If it is a SET, get all the extra data in here */
+		if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+			err = copy_from_user(extra, iwr->u.data.pointer,
+					     iwr->u.data.length *
+					     descr->token_size);
+			if (err) {
+				kfree(extra);
+				return -EFAULT;
+			}
+		}
+
+		/* Call the handler */
+		ret = handler(dev, &info, &(iwr->u), extra);
+
+		/* If we have something to return to the user */
+		if (!ret && IW_IS_GET(cmd)) {
+			/* Check if there is enough buffer up there */
+			if(user_length < iwr->u.data.length) {
+				kfree(extra);
+				return -E2BIG;
+			}
+
+			err = copy_to_user(iwr->u.data.pointer, extra,
+					   iwr->u.data.length *
+					   descr->token_size);
+			if (err)
+				ret =  -EFAULT;
+		}
+
+		/* Generate an event to notify listeners of the change */
+		if((descr->flags & IW_DESCR_FLAG_EVENT) &&
+		   ((ret == 0) || (ret == -EIWCOMMIT))) {
+			if(descr->flags & IW_DESCR_FLAG_RESTRICT)
+				/* If the event is restricted, don't
+				 * export the payload */
+				wireless_send_event(dev, cmd, &(iwr->u), NULL);
+			else
+				wireless_send_event(dev, cmd, &(iwr->u),
+						    extra);
+		}
+
+		/* Cleanup - I told you it wasn't that long ;-) */
+		kfree(extra);
+	}
+
+	return ret;
+}
+
+/* and finally the ioctl wrapper */
+int cfg80211_wext_ioctl(struct ifreq *ifr, unsigned int cmd)
+{
+	struct net_device *dev;
+	iw_handler	handler;
+
+	/* Permissions are already checked in dev_ioctl() before calling us.
+	 * The copy_to/from_user() of ifr is also dealt with in there */
+
+	/* Make sure the device exist */
+	if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
+		return -ENODEV;
+
+	/* A bunch of special cases, then the generic case...
+	 * Note that 'cmd' is already filtered in dev_ioctl() with
+	 * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
+	switch(cmd) {
+		case SIOCGIWSTATS:
+			/* Get Wireless Stats */
+			return ioctl_standard_call(dev,
+						   ifr,
+						   cmd,
+						   &iw_handler_get_iwstats);
+
+		case SIOCGIWPRIV:
+			return -EOPNOTSUPP;
+		default:
+			/* Basic check */
+			if (!netif_device_present(dev))
+				return -ENODEV;
+			handler = get_handler(dev, cmd);
+			if(cmd < SIOCIWFIRSTPRIV && handler != NULL)
+				return ioctl_standard_call(dev, ifr, cmd,
+							   handler);
+			return -EOPNOTSUPP;
+	}
+	return -EINVAL;
+}
diff --git a/net/wireless/wext-old.c b/net/wireless/wext-old.c
index f69ab7b..9892396 100644
--- a/net/wireless/wext-old.c
+++ b/net/wireless/wext-old.c
@@ -83,9 +83,7 @@
 #include <linux/module.h>
 #include <linux/types.h>		/* off_t */
 #include <linux/netdevice.h>		/* struct ifreq, dev_get_by_name() */
-#include <linux/proc_fs.h>
 #include <linux/rtnetlink.h>		/* rtnetlink stuff */
-#include <linux/seq_file.h>
 #include <linux/init.h>			/* for __init */
 #include <linux/if_arp.h>		/* ARPHRD_ETHER */
 #include <linux/etherdevice.h>		/* compare_ether_addr */
@@ -97,6 +95,8 @@
 
 #include <asm/uaccess.h>		/* copy_to_user() */
 
+#include "wext.h"
+
 /**************************** CONSTANTS ****************************/
 
 /* Debugging stuff */
@@ -111,294 +111,6 @@
 #define WE_SET_EVENT		/* Generate an event on some set commands */
 
 /************************* GLOBAL VARIABLES *************************/
-/*
- * You should not use global variables, because of re-entrancy.
- * On our case, it's only const, so it's OK...
- */
-/*
- * Meta-data about all the standard Wireless Extension request we
- * know about.
- */
-static const struct iw_ioctl_description standard_ioctl[] = {
-	[SIOCSIWCOMMIT	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWNAME	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_CHAR,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWNWID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWNWID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWFREQ	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_FREQ,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWFREQ	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_FREQ,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWMODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_UINT,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWMODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_UINT,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWSENS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWSENS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWRANGE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWRANGE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_range),
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWPRIV	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWPRIV	- SIOCIWFIRST] = { /* (handled directly by us) */
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct iw_priv_args),
-		.max_tokens	= 16,
-		.flags		= IW_DESCR_FLAG_NOMAX,
-	},
-	[SIOCSIWSTATS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_NULL,
-	},
-	[SIOCGIWSTATS	- SIOCIWFIRST] = { /* (handled directly by us) */
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_statistics),
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct sockaddr),
-		.max_tokens	= IW_MAX_SPY,
-	},
-	[SIOCGIWSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct sockaddr) +
-				  sizeof(struct iw_quality),
-		.max_tokens	= IW_MAX_SPY,
-	},
-	[SIOCSIWTHRSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct iw_thrspy),
-		.min_tokens	= 1,
-		.max_tokens	= 1,
-	},
-	[SIOCGIWTHRSPY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct iw_thrspy),
-		.min_tokens	= 1,
-		.max_tokens	= 1,
-	},
-	[SIOCSIWAP	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-	},
-	[SIOCGIWAP	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWMLME	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_mlme),
-		.max_tokens	= sizeof(struct iw_mlme),
-	},
-	[SIOCGIWAPLIST	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= sizeof(struct sockaddr) +
-				  sizeof(struct iw_quality),
-		.max_tokens	= IW_MAX_AP,
-		.flags		= IW_DESCR_FLAG_NOMAX,
-	},
-	[SIOCSIWSCAN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= 0,
-		.max_tokens	= sizeof(struct iw_scan_req),
-	},
-	[SIOCGIWSCAN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_SCAN_MAX_DATA,
-		.flags		= IW_DESCR_FLAG_NOMAX,
-	},
-	[SIOCSIWESSID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-		.flags		= IW_DESCR_FLAG_EVENT,
-	},
-	[SIOCGIWESSID	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-		.flags		= IW_DESCR_FLAG_DUMP,
-	},
-	[SIOCSIWNICKN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-	},
-	[SIOCGIWNICKN	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ESSID_MAX_SIZE,
-	},
-	[SIOCSIWRATE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWRATE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWRTS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWRTS	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWFRAG	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWFRAG	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWTXPOW	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWTXPOW	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWRETRY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWRETRY	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWENCODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ENCODING_TOKEN_MAX,
-		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
-	},
-	[SIOCGIWENCODE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_ENCODING_TOKEN_MAX,
-		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
-	},
-	[SIOCSIWPOWER	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWPOWER	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWGENIE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[SIOCGIWGENIE	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[SIOCSIWAUTH	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCGIWAUTH	- SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_PARAM,
-	},
-	[SIOCSIWENCODEEXT - SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_encode_ext),
-		.max_tokens	= sizeof(struct iw_encode_ext) +
-				  IW_ENCODING_TOKEN_MAX,
-	},
-	[SIOCGIWENCODEEXT - SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_encode_ext),
-		.max_tokens	= sizeof(struct iw_encode_ext) +
-				  IW_ENCODING_TOKEN_MAX,
-	},
-	[SIOCSIWPMKSA - SIOCIWFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.min_tokens	= sizeof(struct iw_pmksa),
-		.max_tokens	= sizeof(struct iw_pmksa),
-	},
-};
-static const unsigned standard_ioctl_num = (sizeof(standard_ioctl) /
-					    sizeof(struct iw_ioctl_description));
-
-/*
- * Meta-data about all the additional standard Wireless Extension events
- * we know about.
- */
-static const struct iw_ioctl_description standard_event[] = {
-	[IWEVTXDROP	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-	},
-	[IWEVQUAL	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_QUAL,
-	},
-	[IWEVCUSTOM	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_CUSTOM_MAX,
-	},
-	[IWEVREGISTERED	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR,
-	},
-	[IWEVEXPIRED	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_ADDR, 
-	},
-	[IWEVGENIE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[IWEVMICHAELMICFAILURE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT, 
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_michaelmicfailure),
-	},
-	[IWEVASSOCREQIE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[IWEVASSOCRESPIE	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= IW_GENERIC_IE_MAX,
-	},
-	[IWEVPMKIDCAND	- IWEVFIRST] = {
-		.header_type	= IW_HEADER_TYPE_POINT,
-		.token_size	= 1,
-		.max_tokens	= sizeof(struct iw_pmkid_cand),
-	},
-};
-static const unsigned standard_event_num = (sizeof(standard_event) /
-					    sizeof(struct iw_ioctl_description));
 
 /* Size (in bytes) of the various private data types */
 static const char iw_priv_type_size[] = {
@@ -412,21 +124,6 @@ static const char iw_priv_type_size[] = {
 	0,				/* Not defined */
 };
 
-/* Size (in bytes) of various events */
-static const int event_type_size[] = {
-	IW_EV_LCP_LEN,			/* IW_HEADER_TYPE_NULL */
-	0,
-	IW_EV_CHAR_LEN,			/* IW_HEADER_TYPE_CHAR */
-	0,
-	IW_EV_UINT_LEN,			/* IW_HEADER_TYPE_UINT */
-	IW_EV_FREQ_LEN,			/* IW_HEADER_TYPE_FREQ */
-	IW_EV_ADDR_LEN,			/* IW_HEADER_TYPE_ADDR */
-	0,
-	IW_EV_POINT_LEN,		/* Without variable payload */
-	IW_EV_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */
-	IW_EV_QUAL_LEN,			/* IW_HEADER_TYPE_QUAL */
-};
-
 /************************ COMMON SUBROUTINES ************************/
 /*
  * Stuff that may be used in various place or doesn't fit in one
@@ -464,21 +161,6 @@ static inline iw_handler get_handler(struct net_device *dev,
 
 /* ---------------------------------------------------------------- */
 /*
- * Get statistics out of the driver
- */
-static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
-{
-	/* New location */
-	if((dev->wireless_handlers != NULL) &&
-	   (dev->wireless_handlers->get_wireless_stats != NULL))
-		return dev->wireless_handlers->get_wireless_stats(dev);
-
-	/* Not found */
-	return (struct iw_statistics *) NULL;
-}
-
-/* ---------------------------------------------------------------- */
-/*
  * Call the commit handler in the driver
  * (if exist and if conditions are right)
  *
@@ -551,7 +233,7 @@ static int iw_handler_get_iwstats(struct net_device *		dev,
 	/* Get stats from the driver */
 	struct iw_statistics *stats;
 
-	stats = get_wireless_stats(dev);
+	stats = get_wireless_stats(dev, NULL);
 	if (stats != (struct iw_statistics *) NULL) {
 
 		/* Copy statistics to extra */
@@ -601,97 +283,6 @@ static int iw_handler_get_private(struct net_device *		dev,
 	return 0;
 }
 
-
-/******************** /proc/net/wireless SUPPORT ********************/
-/*
- * The /proc/net/wireless file is a human readable user-space interface
- * exporting various wireless specific statistics from the wireless devices.
- * This is the most popular part of the Wireless Extensions ;-)
- *
- * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
- * The content of the file is basically the content of "struct iw_statistics".
- */
-
-#ifdef CONFIG_PROC_FS
-
-/* ---------------------------------------------------------------- */
-/*
- * Print one entry (line) of /proc/net/wireless
- */
-static __inline__ void wireless_seq_printf_stats(struct seq_file *seq,
-						 struct net_device *dev)
-{
-	/* Get stats from the driver */
-	struct iw_statistics *stats = get_wireless_stats(dev);
-
-	if (stats) {
-		seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
-				"%6d %6d   %6d\n",
-			   dev->name, stats->status, stats->qual.qual,
-			   stats->qual.updated & IW_QUAL_QUAL_UPDATED
-			   ? '.' : ' ',
-			   ((__s32) stats->qual.level) - 
-			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
-			   stats->qual.updated & IW_QUAL_LEVEL_UPDATED
-			   ? '.' : ' ',
-			   ((__s32) stats->qual.noise) - 
-			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
-			   stats->qual.updated & IW_QUAL_NOISE_UPDATED
-			   ? '.' : ' ',
-			   stats->discard.nwid, stats->discard.code,
-			   stats->discard.fragment, stats->discard.retries,
-			   stats->discard.misc, stats->miss.beacon);
-		stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
-	}
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Print info for /proc/net/wireless (print all entries)
- */
-static int wireless_seq_show(struct seq_file *seq, void *v)
-{
-	if (v == SEQ_START_TOKEN)
-		seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
-				"packets               | Missed | WE\n"
-				" face | tus | link level noise |  nwid  "
-				"crypt   frag  retry   misc | beacon | %d\n",
-			   WIRELESS_EXT);
-	else
-		wireless_seq_printf_stats(seq, v);
-	return 0;
-}
-
-static struct seq_operations wireless_seq_ops = {
-	.start = dev_seq_start,
-	.next  = dev_seq_next,
-	.stop  = dev_seq_stop,
-	.show  = wireless_seq_show,
-};
-
-static int wireless_seq_open(struct inode *inode, struct file *file)
-{
-	return seq_open(file, &wireless_seq_ops);
-}
-
-static struct file_operations wireless_seq_fops = {
-	.owner	 = THIS_MODULE,
-	.open    = wireless_seq_open,
-	.read    = seq_read,
-	.llseek  = seq_lseek,
-	.release = seq_release,
-};
-
-int __init wireless_proc_init(void)
-{
-	/* Create /proc/net/wireless entry */
-	if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
-		return -ENOMEM;
-
-	return 0;
-}
-#endif	/* CONFIG_PROC_FS */
-
 /************************** IOCTL SUPPORT **************************/
 /*
  * The original user space API to configure all those Wireless Extensions
@@ -1863,220 +1454,6 @@ int wireless_rtnetlink_set(struct net_device *	dev,
 }
 #endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
 
-
-/************************* EVENT PROCESSING *************************/
-/*
- * Process events generated by the wireless layer or the driver.
- * Most often, the event will be propagated through rtnetlink
- */
-
-#ifdef WE_EVENT_RTNETLINK
-/* ---------------------------------------------------------------- */
-/*
- * Locking...
- * ----------
- *
- * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
- * the locking issue in here and implementing this code !
- *
- * The issue : wireless_send_event() is often called in interrupt context,
- * while the Netlink layer can never be called in interrupt context.
- * The fully formed RtNetlink events are queued, and then a tasklet is run
- * to feed those to Netlink.
- * The skb_queue is interrupt safe, and its lock is not held while calling
- * Netlink, so there is no possibility of dealock.
- * Jean II
- */
-
-static struct sk_buff_head wireless_nlevent_queue;
-
-static int __init wireless_nlevent_init(void)
-{
-	skb_queue_head_init(&wireless_nlevent_queue);
-	return 0;
-}
-
-subsys_initcall(wireless_nlevent_init);
-
-static void wireless_nlevent_process(unsigned long data)
-{
-	struct sk_buff *skb;
-
-	while ((skb = skb_dequeue(&wireless_nlevent_queue)))
-		rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
-}
-
-static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
-
-/* ---------------------------------------------------------------- */
-/*
- * Fill a rtnetlink message with our event data.
- * Note that we propage only the specified event and don't dump the
- * current wireless config. Dumping the wireless config is far too
- * expensive (for each parameter, the driver need to query the hardware).
- */
-static inline int rtnetlink_fill_iwinfo(struct sk_buff *	skb,
-					struct net_device *	dev,
-					int			type,
-					char *			event,
-					int			event_len)
-{
-	struct ifinfomsg *r;
-	struct nlmsghdr  *nlh;
-	unsigned char	 *b = skb->tail;
-
-	nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
-	r = NLMSG_DATA(nlh);
-	r->ifi_family = AF_UNSPEC;
-	r->__ifi_pad = 0;
-	r->ifi_type = dev->type;
-	r->ifi_index = dev->ifindex;
-	r->ifi_flags = dev_get_flags(dev);
-	r->ifi_change = 0;	/* Wireless changes don't affect those flags */
-
-	/* Add the wireless events in the netlink packet */
-	RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
-
-	nlh->nlmsg_len = skb->tail - b;
-	return skb->len;
-
-nlmsg_failure:
-rtattr_failure:
-	skb_trim(skb, b - skb->data);
-	return -1;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Create and broadcast and send it on the standard rtnetlink socket
- * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c
- * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
- * within a RTM_NEWLINK event.
- */
-static inline void rtmsg_iwinfo(struct net_device *	dev,
-				char *			event,
-				int			event_len)
-{
-	struct sk_buff *skb;
-	int size = NLMSG_GOODSIZE;
-
-	skb = alloc_skb(size, GFP_ATOMIC);
-	if (!skb)
-		return;
-
-	if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
-				  event, event_len) < 0) {
-		kfree_skb(skb);
-		return;
-	}
-	NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
-	skb_queue_tail(&wireless_nlevent_queue, skb);
-	tasklet_schedule(&wireless_nlevent_tasklet);
-}
-
-#endif	/* WE_EVENT_RTNETLINK */
-
-/* ---------------------------------------------------------------- */
-/*
- * Main event dispatcher. Called from other parts and drivers.
- * Send the event on the appropriate channels.
- * May be called from interrupt context.
- */
-void wireless_send_event(struct net_device *	dev,
-			 unsigned int		cmd,
-			 union iwreq_data *	wrqu,
-			 char *			extra)
-{
-	const struct iw_ioctl_description *	descr = NULL;
-	int extra_len = 0;
-	struct iw_event  *event;		/* Mallocated whole event */
-	int event_len;				/* Its size */
-	int hdr_len;				/* Size of the event header */
-	int wrqu_off = 0;			/* Offset in wrqu */
-	/* Don't "optimise" the following variable, it will crash */
-	unsigned	cmd_index;		/* *MUST* be unsigned */
-
-	/* Get the description of the Event */
-	if(cmd <= SIOCIWLAST) {
-		cmd_index = cmd - SIOCIWFIRST;
-		if(cmd_index < standard_ioctl_num)
-			descr = &(standard_ioctl[cmd_index]);
-	} else {
-		cmd_index = cmd - IWEVFIRST;
-		if(cmd_index < standard_event_num)
-			descr = &(standard_event[cmd_index]);
-	}
-	/* Don't accept unknown events */
-	if(descr == NULL) {
-		/* Note : we don't return an error to the driver, because
-		 * the driver would not know what to do about it. It can't
-		 * return an error to the user, because the event is not
-		 * initiated by a user request.
-		 * The best the driver could do is to log an error message.
-		 * We will do it ourselves instead...
-		 */
-	  	printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
-		       dev->name, cmd);
-		return;
-	}
-#ifdef WE_EVENT_DEBUG
-	printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n",
-	       dev->name, cmd);
-	printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif	/* WE_EVENT_DEBUG */
-
-	/* Check extra parameters and set extra_len */
-	if(descr->header_type == IW_HEADER_TYPE_POINT) {
-		/* Check if number of token fits within bounds */
-		if(wrqu->data.length > descr->max_tokens) {
-		  	printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
-			return;
-		}
-		if(wrqu->data.length < descr->min_tokens) {
-		  	printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
-			return;
-		}
-		/* Calculate extra_len - extra is NULL for restricted events */
-		if(extra != NULL)
-			extra_len = wrqu->data.length * descr->token_size;
-		/* Always at an offset in wrqu */
-		wrqu_off = IW_EV_POINT_OFF;
-#ifdef WE_EVENT_DEBUG
-		printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len);
-#endif	/* WE_EVENT_DEBUG */
-	}
-
-	/* Total length of the event */
-	hdr_len = event_type_size[descr->header_type];
-	event_len = hdr_len + extra_len;
-
-#ifdef WE_EVENT_DEBUG
-	printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, wrqu_off %d, event_len %d\n", dev->name, cmd, hdr_len, wrqu_off, event_len);
-#endif	/* WE_EVENT_DEBUG */
-
-	/* Create temporary buffer to hold the event */
-	event = kmalloc(event_len, GFP_ATOMIC);
-	if(event == NULL)
-		return;
-
-	/* Fill event */
-	event->len = event_len;
-	event->cmd = cmd;
-	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
-	if(extra != NULL)
-		memcpy(((char *) event) + hdr_len, extra, extra_len);
-
-#ifdef WE_EVENT_RTNETLINK
-	/* Send via the RtNetlink event channel */
-	rtmsg_iwinfo(dev, (char *) event, event_len);
-#endif	/* WE_EVENT_RTNETLINK */
-
-	/* Cleanup */
-	kfree(event);
-
-	return;		/* Always success, I guess ;-) */
-}
-
 /********************** ENHANCED IWSPY SUPPORT **********************/
 /*
  * In the old days, the driver was handling spy support all by itself.
diff --git a/net/wireless/wext.h b/net/wireless/wext.h
new file mode 100644
index 0000000..fcf1c5a
--- /dev/null
+++ b/net/wireless/wext.h
@@ -0,0 +1,13 @@
+/*
+ * some foo for wext compat/wext interoperability
+ */
+#ifndef _WEXT_H
+#define _WEXT_H
+#include <linux/wireless.h>
+extern struct iw_statistics *get_wireless_stats(struct net_device *dev,
+						struct iw_statistics *out);
+extern const struct iw_ioctl_description standard_ioctl[];
+extern const unsigned standard_ioctl_num;
+extern const struct iw_ioctl_description standard_event[];
+extern const int event_type_size[];
+#endif /* _WEXT_H */
-- 
1.4.4.2

-- 
John W. Linville
linville@tuxdriver.com

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] wireless: add cfg80211
  2007-02-07  0:47   ` [RFC PATCH 1/3] wireless: add cfg80211 John W. Linville
       [not found]     ` <20070207004832.GC23096@tuxdriver.com>
@ 2007-02-07  7:35     ` Christoph Hellwig
  2007-02-08 13:12       ` Johannes Berg
  1 sibling, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2007-02-07  7:35 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, johannes

On Tue, Feb 06, 2007 at 07:47:47PM -0500, John W. Linville wrote:
> From: Johannes Berg <johannes@sipsolutions.net>
> 
> This patch adds cfg80211, a new configuration system for wireless
> hardware.
> 
> It currently features a bunch of configuration requests, support for
> adding and removing virtual interfaces, the ability to inject packets and
> more.
> 
> Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
> Signed-off-by: John W. Linville <linville@tuxdriver.com>
> ---
>  include/linux/netdevice.h  |    1 +
>  include/net/cfg80211.h     |  186 ++++++++++++++++++++++++++++++++++++++++++++
>  net/Kconfig                |    3 +
>  net/Makefile               |    1 +
>  net/wireless/Makefile      |    4 +
>  net/wireless/core.c        |  158 +++++++++++++++++++++++++++++++++++++
>  net/wireless/core.h        |   57 ++++++++++++++
>  net/wireless/wext-compat.c |   25 ++++++
>  8 files changed, 435 insertions(+), 0 deletions(-)
> 
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index fea0d9d..c1e9962 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -398,6 +398,7 @@ struct net_device
>  	void                    *ip6_ptr;       /* IPv6 specific data */
>  	void			*ec_ptr;	/* Econet specific data	*/
>  	void			*ax25_ptr;	/* AX.25 specific data */
> +	void			*ieee80211_ptr;	/* IEEE 802.11 specific data */
>  
>  /*
>   * Cache line mostly used on receive path (including eth_type_trans())

> --- /dev/null
> +++ b/net/wireless/Makefile
> @@ -0,0 +1,4 @@
> +obj-$(CONFIG_CFG80211) += cfg80211.o
> +
> +cfg80211-objs := \
> +	core.o

the contents of this file should be:

obj-$(CONFIG_CFG80211)	+= cfg80211.o
cfg80211-y		+= core.o


> @@ -0,0 +1,158 @@
> +/*
> + * This is the new wireless configuration interface.

I don't think new makes a lot of sense here, it's hopefully the
standad one soon.

> + *
> + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
> + */
> +
> +#include "core.h"
> +#include <linux/if.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <net/genetlink.h>
> +#include <net/cfg80211.h>
> +#include <linux/mutex.h>
> +#include <linux/list.h>

This include order seems odd.  We normally include local headers
last and net/ after linux/

> +MODULE_AUTHOR("Johannes Berg");
> +MODULE_LICENSE("GPL");

Can you please add a MODULE_DESCRIPTION aswell?

> +
> +/* RCU might be appropriate here since we usually
> + * only read the list, and that can happen quite
> + * often because we need to do it for each command */
> +LIST_HEAD(cfg80211_drv_list);
> +DEFINE_MUTEX(cfg80211_drv_mutex);

Any reason these are non-static?  They aren't actually used outside
of this file, and in general having non-static lists isn't very nice,
we prefer having proper accessor functions.

> +static int cfg80211_init(void)
> +{
> +	/* possibly need to do more later */
> +	return 0;
> +}
> +
> +static void cfg80211_exit(void)
> +{
> +}
> +
> +module_init(cfg80211_init);
> +module_exit(cfg80211_exit);

Just drop these two, there's not point in adding dead code.

> --- /dev/null
> +++ b/net/wireless/wext-compat.c
> @@ -0,0 +1,25 @@
> +/* NOT YET */
> +

huh?  this file isn't added to the build process and only contains
a (non-standard formatted) comment.  No point in adding it in this
patch.


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 3/3] cfg80211: add wext-compatible client
  2007-02-07  0:49       ` [RFC PATCH 3/3] cfg80211: add wext-compatible client John W. Linville
@ 2007-02-07  7:54         ` Christoph Hellwig
  2007-02-08 13:13           ` Johannes Berg
  0 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2007-02-07  7:54 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, johannes

> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index c1e9962..6a9b4c8 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -348,12 +348,17 @@ struct net_device
>  
>  	struct net_device_stats* (*get_stats)(struct net_device *dev);
>  
> +#ifdef CONFIG_WIRELESS_EXT
>  	/* List of functions to handle Wireless Extensions (instead of ioctl).
>  	 * See <net/iw_handler.h> for details. Jean II */
>  	const struct iw_handler_def *	wireless_handlers;
>  	/* Instance data managed by the core of Wireless Extensions. */
>  	struct iw_public_data *	wireless_data;
> -
> +#endif
> +#ifdef CONFIG_CFG80211_WEXT_COMPAT
> +	/* pending config used by cfg80211/wext compat code only */
> +	void *cfg80211_wext_pending_config;
> +#endif

Do we really have to add this directly to struct net_device and bloat it
for all users?  I'd put this behind  ieee80211_ptr instead unless there's
a good reason against that.

> +#ifdef CONFIG_CFG80211_WEXT_COMPAT
> +extern int cfg80211_wext_ioctl(struct ifreq *ifr, unsigned int cmd);
> +#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
> +int cfg80211_wext_nl_set(struct net_device *dev, char *data, int len);
> +int cfg80211_wext_nl_get(struct net_device *dev, char *data, int len,
> +			 char **p_buf, int *p_len);
> +#endif
> +#endif

Please chose either to use the extern prefix or not, but stick to one
choice for a single file :)  Also we don't need to ifdef out prototypes.

> +config CFG80211_WEXTNL_COMPAT
> +	bool "cfg80211 WE-netlink compatibility"
> +	depends CFG80211 && CFG80211_WEXT_COMPAT
> +	---help---
> +	This option allows using devices whose drivers have been
> +	converted to use the new cfg80211 with wireless extensions
> +	over rtnetlink, providing WE-20 compatibility. Note that
> +	cfg80211's "native" interface is nl80211 using generic netlink.
> +	The wireless extensions are being deprecated and the netlink
> +	based API for WE was never configured by default, nor do any
> +	userspace tools use this feature.
> +
> +	This option exists only to make Jean happy. Say N.

Should we really put this code in?  It seems like unessecary bloat to me.


> @@ -2798,6 +2799,39 @@ int dev_ioctl(unsigned int cmd, void __user *arg)
>  					ret = -EFAULT;
>  				return ret;
>  			}
> +#ifdef CONFIG_CFG80211_WEXT_COMPAT
> +			/* Take care of cfg80211 WE compatibility */
> +			if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
> +				/* If command is `set a parameter', or
> +				 * `get the encoding parameters', check if
> +				 * the user has the right to do it */
> +				if (IW_IS_SET(cmd) || cmd == SIOCGIWENCODE
> +				    || cmd == SIOCGIWENCODEEXT) {
> +					if (!capable(CAP_NET_ADMIN))
> +						return -EPERM;
> +				}
> +				dev_load(ifr.ifr_name);
> +				rtnl_lock();
> +				/* Follow me in net/wireless/wext-compat.c */
> +				ret = cfg80211_wext_ioctl(&ifr, cmd);
> +				rtnl_unlock();
> +				if (ret == 0 && IW_IS_GET(cmd) &&
> +				    copy_to_user(arg, &ifr,
> +						 sizeof(struct ifreq)))
> +					ret = -EFAULT;
> +				/* haha, I cheat here by allowing a driver or
> +				 * stack to have both WE or CFG80211-WE for
> +				 * a little while during conversion... hope that
> +				 * ENOSYS is only used to indicate not implemented
> +				 *
> +				 * if wireless extensions are not configured
> +				 * then this is the last thing here so that
> +				 * if we fall through we return -EINVAL
> +				 */
> +				if (ret != -ENOSYS)
> +					return ret;
> +			}

Can we split this into a nice little helper function in wext-compat.c
or a dummy macro if it's not configured?

> --- a/net/core/rtnetlink.c
> +++ b/net/core/rtnetlink.c
> @@ -56,6 +56,9 @@
>  #include <linux/wireless.h>
>  #include <net/iw_handler.h>
>  #endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
> +#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
> +#include <net/cfg80211.h>
> +#endif

There's not point in including headers conditionally.

> diff --git a/net/wireless/Makefile b/net/wireless/Makefile
> index 663a7d8..62e67b7 100644
> --- a/net/wireless/Makefile
> +++ b/net/wireless/Makefile
> @@ -4,3 +4,13 @@ cfg80211-objs := \
>  	core.o
>  
>  obj-$(CONFIG_WIRELESS_EXT) += wext-old.o
> +
> +obj-nn :=
> +obj-yy :=
> +obj-yn :=
> +obj-ny :=
> +
> +# this needs to be compiled in...
> +obj-$(CONFIG_CFG80211_WEXT_COMPAT) += wext-compat.o
> +obj-$(CONFIG_CFG80211_WEXT_COMPAT)$(CONFIG_NET_WIRELESS) += wext-common.o
> +obj-y += $(obj-yy) $(obj-yn) $(obj-ny)

This looks odd. Kbuild filters out duplicate objects, so something like
the snipplet below should work aswell:

obj-$(CONFIG_WIRELESS_EXT)		+= wext-common.o wext-old.o
obj-$(CONFIG_CFG80211_WEXT_COMPAT)	+= wext-common.o wext-compat.o	


> +#ifdef CONFIG_CFG80211_WEXT_COMPAT
> +/* wext compatibility must be compiled in...
> + * this extern is in wext-compat.c */

Is there really a point in making cfg80211 a loadable module?
This whle operation vector stuff looks like a lot of unnessecary
complexity to me.

> new file mode 100644
> index 0000000..c1de1d7
> --- /dev/null
> +++ b/net/wireless/wext-common.c
> @@ -0,0 +1,610 @@
> +/* common wext support routines, proc interface and events */

This is mostly Jean's old code, right?  Probably wants at least some
attribution.

> +static void cfg80211_ensure_netdev_pending_cfg(struct net_device *dev)
> +{
> +	struct cfg80211_config *cfg = dev->cfg80211_wext_pending_config;
> +	if (!cfg) {
> +		cfg = kmalloc(sizeof(*cfg)+32, GFP_KERNEL);

kmalloc return values need to be checked.

> +/* operations we implement. whew, I machine-generated these */

So should we put the shell-script to generate them intree instead? :)
Well, probably not, but then again we probably don't want this
comment either as it won't be true anymore as soon as someone touches
the code.


Btw, if we submit cfg80211 for inclusion there should be at least one patch
in the series actually converts a driver to use it.  Both to make this
code not dead and give people and example on how to use the cfg80211
infrastructure.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC v2] cfg80211 merge
  2007-02-07  0:46 ` [RFC v2] " John W. Linville
  2007-02-07  0:47   ` [RFC PATCH 1/3] wireless: add cfg80211 John W. Linville
@ 2007-02-07 14:39   ` John W. Linville
  1 sibling, 0 replies; 52+ messages in thread
From: John W. Linville @ 2007-02-07 14:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes

On Tue, Feb 06, 2007 at 07:46:26PM -0500, John W. Linville wrote:
> In London we agreed that it would be good to go ahead and merge
> cfg80211.  This provides some exposure to the API and eases some of
> the pain for driver maintainers (who won't have to follow wireless-dev
> to get it).  It also adds the ieee80211_ptr to the net_device struct.
> 
> I have refactored the cfg80211-related patches down to three patches.
> This eliminates some of the churn in the series from wireless-dev,
> and each patch leaves a buildable tree.  This version also removes
> the nl80211 bits to a separate patch (available in wireless-dev on
> the mm-master branch).

Hmmm...looks like 2/3 got dropped, probably due to size.  FWIW, it
is basically identical to the last round and is really just a rename
of wireless.c to wext-old.c.

John
-- 
John W. Linville
linville@tuxdriver.com

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] wireless: add cfg80211
  2007-02-07  7:35     ` [RFC PATCH 1/3] wireless: add cfg80211 Christoph Hellwig
@ 2007-02-08 13:12       ` Johannes Berg
  2007-02-08 19:17         ` Christoph Hellwig
  0 siblings, 1 reply; 52+ messages in thread
From: Johannes Berg @ 2007-02-08 13:12 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: John W. Linville, linux-wireless

[-- Attachment #1: Type: text/plain, Size: 2125 bytes --]

> > +/*
> > + * This is the new wireless configuration interface.
> 
> I don't think new makes a lot of sense here, it's hopefully the
> standad one soon.

Heh. I suppose it'll need a better description anyway.

> > + *
> > + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
> > + */
> > +
> > +#include "core.h"
> > +#include <linux/if.h>
> > +#include <linux/module.h>
> > +#include <linux/err.h>
> > +#include <net/genetlink.h>
> > +#include <net/cfg80211.h>
> > +#include <linux/mutex.h>
> > +#include <linux/list.h>
> 
> This include order seems odd.  We normally include local headers
> last and net/ after linux/

Oh well. I often just stick in what I need in the order I needed it ;)
I'll change it.

> > +MODULE_AUTHOR("Johannes Berg");
> > +MODULE_LICENSE("GPL");
> 
> Can you please add a MODULE_DESCRIPTION aswell?

Sure. Then again, didn't you just ask for "why can this be a module at
all" too? :)

> > +
> > +/* RCU might be appropriate here since we usually
> > + * only read the list, and that can happen quite
> > + * often because we need to do it for each command */
> > +LIST_HEAD(cfg80211_drv_list);
> > +DEFINE_MUTEX(cfg80211_drv_mutex);
> 
> Any reason these are non-static?  They aren't actually used outside
> of this file, and in general having non-static lists isn't very nice,
> we prefer having proper accessor functions.

They'll be used in nl80211 which wasn't part of this patchset. Having
accessor functions for lists is nice as long as it's add/remove, but I
don't like having iterator functions with callbacks and all that much.
And in fact, a list_for_each() is the only thing done with this list...
I'd prefer keeping it this way.

> > --- /dev/null
> > +++ b/net/wireless/wext-compat.c
> > @@ -0,0 +1,25 @@
> > +/* NOT YET */
> > +
> 
> huh?  this file isn't added to the build process and only contains
> a (non-standard formatted) comment.  No point in adding it in this
> patch.

Heh, yeah, it's replaced right away in the next one. I just had wanted
to document the thoughts that went into that.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 3/3] cfg80211: add wext-compatible client
  2007-02-07  7:54         ` Christoph Hellwig
@ 2007-02-08 13:13           ` Johannes Berg
  2007-02-08 18:38             ` Luis R. Rodriguez
  0 siblings, 1 reply; 52+ messages in thread
From: Johannes Berg @ 2007-02-08 13:13 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: John W. Linville, linux-wireless

[-- Attachment #1: Type: text/plain, Size: 4754 bytes --]

On Wed, 2007-02-07 at 07:54 +0000, Christoph Hellwig wrote:

> Do we really have to add this directly to struct net_device and bloat it
> for all users?  I'd put this behind  ieee80211_ptr instead unless there's
> a good reason against that.

Well, the reason currently is that cfg80211 treats ieee80211_ptr as an
opaque cookie, it doesn't need to know what it points to.

Of course, I intend to change that too...

> > +config CFG80211_WEXTNL_COMPAT
> > +	bool "cfg80211 WE-netlink compatibility"
> > +	depends CFG80211 && CFG80211_WEXT_COMPAT
> > +	---help---
> > +	This option allows using devices whose drivers have been
> > +	converted to use the new cfg80211 with wireless extensions
> > +	over rtnetlink, providing WE-20 compatibility. Note that
> > +	cfg80211's "native" interface is nl80211 using generic netlink.
> > +	The wireless extensions are being deprecated and the netlink
> > +	based API for WE was never configured by default, nor do any
> > +	userspace tools use this feature.
> > +
> > +	This option exists only to make Jean happy. Say N.
> 
> Should we really put this code in?  It seems like unessecary bloat to me.

Yeah. Jean disappeared anyway, I'll kill it.

> > @@ -2798,6 +2799,39 @@ int dev_ioctl(unsigned int cmd, void __user *arg)
> >  					ret = -EFAULT;
> >  				return ret;
> >  			}
> > +#ifdef CONFIG_CFG80211_WEXT_COMPAT
> > +			/* Take care of cfg80211 WE compatibility */
> > +			if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
> > +				/* If command is `set a parameter', or
> > +				 * `get the encoding parameters', check if
> > +				 * the user has the right to do it */
> > +				if (IW_IS_SET(cmd) || cmd == SIOCGIWENCODE
> > +				    || cmd == SIOCGIWENCODEEXT) {
> > +					if (!capable(CAP_NET_ADMIN))
> > +						return -EPERM;
> > +				}
> > +				dev_load(ifr.ifr_name);
> > +				rtnl_lock();
> > +				/* Follow me in net/wireless/wext-compat.c */
> > +				ret = cfg80211_wext_ioctl(&ifr, cmd);
> > +				rtnl_unlock();
> > +				if (ret == 0 && IW_IS_GET(cmd) &&
> > +				    copy_to_user(arg, &ifr,
> > +						 sizeof(struct ifreq)))
> > +					ret = -EFAULT;
> > +				/* haha, I cheat here by allowing a driver or
> > +				 * stack to have both WE or CFG80211-WE for
> > +				 * a little while during conversion... hope that
> > +				 * ENOSYS is only used to indicate not implemented
> > +				 *
> > +				 * if wireless extensions are not configured
> > +				 * then this is the last thing here so that
> > +				 * if we fall through we return -EINVAL
> > +				 */
> > +				if (ret != -ENOSYS)
> > +					return ret;
> > +			}
> 
> Can we split this into a nice little helper function in wext-compat.c
> or a dummy macro if it's not configured?

I guess I can do that. Maybe I'll do the same with wext too.

> > --- a/net/core/rtnetlink.c
> > +++ b/net/core/rtnetlink.c
> > @@ -56,6 +56,9 @@
> >  #include <linux/wireless.h>
> >  #include <net/iw_handler.h>
> >  #endif	/* CONFIG_NET_WIRELESS_RTNETLINK */
> > +#ifdef CONFIG_CFG80211_WEXTNL_COMPAT
> > +#include <net/cfg80211.h>
> > +#endif
> 
> There's not point in including headers conditionally.

It helps ccache when you change that header, but yeah, let's just
include it.

> This looks odd. Kbuild filters out duplicate objects, so something like
> the snipplet below should work aswell:
> 
> obj-$(CONFIG_WIRELESS_EXT)		+= wext-common.o wext-old.o
> obj-$(CONFIG_CFG80211_WEXT_COMPAT)	+= wext-common.o wext-compat.o	

Hah, good point, I never even thought of something that would just
duplicate it.

> > +#ifdef CONFIG_CFG80211_WEXT_COMPAT
> > +/* wext compatibility must be compiled in...
> > + * this extern is in wext-compat.c */
> 
> Is there really a point in making cfg80211 a loadable module?
> This whle operation vector stuff looks like a lot of unnessecary
> complexity to me.

I think there is point in doing it as it allows us to replace it with an
out-of-tree newer version for getting it into the hands of users who
might not want to replace their whole kernel.

> > new file mode 100644
> > index 0000000..c1de1d7
> > --- /dev/null
> > +++ b/net/wireless/wext-common.c
> > @@ -0,0 +1,610 @@
> > +/* common wext support routines, proc interface and events */
> 
> This is mostly Jean's old code, right?  Probably wants at least some
> attribution.

Yeah, good point, it's mostly code that moved from that file to here.

> Btw, if we submit cfg80211 for inclusion there should be at least one patch
> in the series actually converts a driver to use it.  Both to make this
> code not dead and give people and example on how to use the cfg80211
> infrastructure.

I guess I could make softmac use it.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 3/3] cfg80211: add wext-compatible client
  2007-02-08 13:13           ` Johannes Berg
@ 2007-02-08 18:38             ` Luis R. Rodriguez
  2007-02-08 18:50               ` John W. Linville
  2007-02-08 19:55               ` Christoph Hellwig
  0 siblings, 2 replies; 52+ messages in thread
From: Luis R. Rodriguez @ 2007-02-08 18:38 UTC (permalink / raw)
  To: Johannes Berg; +Cc: Christoph Hellwig, John W. Linville, linux-wireless

On 2/8/07, Johannes Berg <johannes@sipsolutions.net> wrote:
> On Wed, 2007-02-07 at 07:54 +0000, Christoph Hellwig wrote:
> > Btw, if we submit cfg80211 for inclusion there should be at least one patch
> > in the series actually converts a driver to use it.  Both to make this
> > code not dead and give people and example on how to use the cfg80211
> > infrastructure.
>
> I guess I could make softmac use it.

Another idea here is we can provide a configfs frontend to cfg80211
therefore any device which implements cfg80211 will automagically have
a userspace frontend (/configfs/wireless/foo) ready to test cfg80211.
With this we wouldn't have to wait for the next userspace nl80211
application to start testing cfg80211 directly (without wireless
extensions compat). A configfs frontend can also allow more rapid
development of userspace interfaces to configure wireless devices
through cfg80211. Comments?

  Luis

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 3/3] cfg80211: add wext-compatible client
  2007-02-08 18:38             ` Luis R. Rodriguez
@ 2007-02-08 18:50               ` John W. Linville
  2007-02-08 19:41                 ` Luis R. Rodriguez
  2007-02-08 19:55               ` Christoph Hellwig
  1 sibling, 1 reply; 52+ messages in thread
From: John W. Linville @ 2007-02-08 18:50 UTC (permalink / raw)
  To: Luis R. Rodriguez; +Cc: Johannes Berg, Christoph Hellwig, linux-wireless

On Thu, Feb 08, 2007 at 01:38:02PM -0500, Luis R. Rodriguez wrote:
> On 2/8/07, Johannes Berg <johannes@sipsolutions.net> wrote:
> >On Wed, 2007-02-07 at 07:54 +0000, Christoph Hellwig wrote:
> >> Btw, if we submit cfg80211 for inclusion there should be at least one 
> >patch
> >> in the series actually converts a driver to use it.  Both to make this
> >> code not dead and give people and example on how to use the cfg80211
> >> infrastructure.
> >
> >I guess I could make softmac use it.
> 
> Another idea here is we can provide a configfs frontend to cfg80211
> therefore any device which implements cfg80211 will automagically have
> a userspace frontend (/configfs/wireless/foo) ready to test cfg80211.
> With this we wouldn't have to wait for the next userspace nl80211
> application to start testing cfg80211 directly (without wireless
> extensions compat). A configfs frontend can also allow more rapid
> development of userspace interfaces to configure wireless devices
> through cfg80211. Comments?

Luis,

You have mentioned this before, and FWIW I think it is a decent idea.
Let's see some code! :-)

Still, I don't think it changes the issue that we need a driver (or
stack) plugged-in to the bottom end.  I have this on my personal list
of "soon TODO", although I haven't picked a driver yet.

If anyone is working on a cfg80211 driver conversion, please speak-up
so we can avoid duplicating work.

Thanks,

John
-- 
John W. Linville
linville@tuxdriver.com

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] wireless: add cfg80211
  2007-02-08 13:12       ` Johannes Berg
@ 2007-02-08 19:17         ` Christoph Hellwig
  0 siblings, 0 replies; 52+ messages in thread
From: Christoph Hellwig @ 2007-02-08 19:17 UTC (permalink / raw)
  To: Johannes Berg; +Cc: Christoph Hellwig, John W. Linville, linux-wireless

On Thu, Feb 08, 2007 at 02:12:57PM +0100, Johannes Berg wrote:
> > > +MODULE_AUTHOR("Johannes Berg");
> > > +MODULE_LICENSE("GPL");
> > 
> > Can you please add a MODULE_DESCRIPTION aswell?
> 
> Sure. Then again, didn't you just ask for "why can this be a module at
> all" too? :)

True.  Then again I try to stick MODULE_DESCRIPTIONS in wherever I can :)


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 3/3] cfg80211: add wext-compatible client
  2007-02-08 18:50               ` John W. Linville
@ 2007-02-08 19:41                 ` Luis R. Rodriguez
  2007-02-09 15:43                   ` Johannes Berg
  0 siblings, 1 reply; 52+ messages in thread
From: Luis R. Rodriguez @ 2007-02-08 19:41 UTC (permalink / raw)
  To: John W. Linville; +Cc: Johannes Berg, Christoph Hellwig, linux-wireless

On 2/8/07, John W. Linville <linville@tuxdriver.com> wrote:
> On Thu, Feb 08, 2007 at 01:38:02PM -0500, Luis R. Rodriguez wrote:
> > On 2/8/07, Johannes Berg <johannes@sipsolutions.net> wrote:
> > >On Wed, 2007-02-07 at 07:54 +0000, Christoph Hellwig wrote:
> > >> Btw, if we submit cfg80211 for inclusion there should be at least one
> > >patch
> > >> in the series actually converts a driver to use it.  Both to make this
> > >> code not dead and give people and example on how to use the cfg80211
> > >> infrastructure.
> > >
> > >I guess I could make softmac use it.
> >
> > Another idea here is we can provide a configfs frontend to cfg80211
> > therefore any device which implements cfg80211 will automagically have
> > a userspace frontend (/configfs/wireless/foo) ready to test cfg80211.
> > With this we wouldn't have to wait for the next userspace nl80211
> > application to start testing cfg80211 directly (without wireless
> > extensions compat). A configfs frontend can also allow more rapid
> > development of userspace interfaces to configure wireless devices
> > through cfg80211. Comments?
>
> Luis,
>
> You have mentioned this before, and FWIW I think it is a decent idea.
> Let's see some code! :-)

I know, I know, talk is cheap, trust me, I abide by this motto every
day. I actually wanted to just code this up and send this as part of
my comment but do have other responsibilities I have to attend to
first. I figured I'd throw it out though in case someone who does have
time is willing to pick it up as I think it would be great for us.

> Still, I don't think it changes the issue that we need a driver (or
> stack) plugged-in to the bottom end.  I have this on my personal list
> of "soon TODO", although I haven't picked a driver yet.
>
> If anyone is working on a cfg80211 driver conversion, please speak-up
> so we can avoid duplicating work.

  Luis

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 3/3] cfg80211: add wext-compatible client
  2007-02-08 18:38             ` Luis R. Rodriguez
  2007-02-08 18:50               ` John W. Linville
@ 2007-02-08 19:55               ` Christoph Hellwig
  2007-02-08 21:56                 ` Luis R. Rodriguez
  2007-02-09  2:09                 ` Dan Williams
  1 sibling, 2 replies; 52+ messages in thread
From: Christoph Hellwig @ 2007-02-08 19:55 UTC (permalink / raw)
  To: Luis R. Rodriguez
  Cc: Johannes Berg, Christoph Hellwig, John W. Linville, linux-wireless

On Thu, Feb 08, 2007 at 01:38:02PM -0500, Luis R. Rodriguez wrote:
> >> in the series actually converts a driver to use it.  Both to make this
> >> code not dead and give people and example on how to use the cfg80211
> >> infrastructure.
> >
> >I guess I could make softmac use it.
> 
> Another idea here is we can provide a configfs frontend to cfg80211
> therefore any device which implements cfg80211 will automagically have
> a userspace frontend (/configfs/wireless/foo) ready to test cfg80211.

Huh?  We already have a wext fronted to nicely test cfg80211, what we
need are backends.  In addition to that I think we should agree on
one primary userspace configuration method for the future instead of
adding lots of them just because it's so easy.  If the public opinion
has changed 180 degrees and configs is preferred over nl80211 that's
fine by me as long as the implementation is sane..


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 3/3] cfg80211: add wext-compatible client
  2007-02-08 19:55               ` Christoph Hellwig
@ 2007-02-08 21:56                 ` Luis R. Rodriguez
  2007-02-09  2:09                 ` Dan Williams
  1 sibling, 0 replies; 52+ messages in thread
From: Luis R. Rodriguez @ 2007-02-08 21:56 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Johannes Berg, John W. Linville, linux-wireless, Michael Wu

On 2/8/07, Christoph Hellwig <hch@infradead.org> wrote:
> On Thu, Feb 08, 2007 at 01:38:02PM -0500, Luis R. Rodriguez wrote:
> > >> in the series actually converts a driver to use it.  Both to make this
> > >> code not dead and give people and example on how to use the cfg80211
> > >> infrastructure.
> > >
> > >I guess I could make softmac use it.
> >
> > Another idea here is we can provide a configfs frontend to cfg80211
> > therefore any device which implements cfg80211 will automagically have
> > a userspace frontend (/configfs/wireless/foo) ready to test cfg80211.
>
> Huh?  We already have a wext fronted to nicely test cfg80211, what we
> need are backends.

Yes, this is true.

> In addition to that I think we should agree on
> one primary userspace configuration method for the future instead of
> adding lots of them just because it's so easy.

Its not just that its provides a means to allow easy development of
userspace applications, that's just the cherry on top of the cake. The
real benefit of this is entirely architectural. Here are two examples:

a. Handling of private data

That is non-standard data, ie that doesn't go in the cfg80211_config
struct. Think of how iwpriv works and how nasty that was. Now granted
cfg80211 would still need to have some infrastructure for handling of
private data and nl80211 can be used as well but I find it would be
more convenient to scrape the available private set/get information
through a configfs tree on the device than have userspace applications
use nl80211 all the time.

b. Radiotap field selectivity

Although we currently do not fully support radiotap and although
radiotap currently  works by tacking on ALL the supported fields onto
the beginning of the skb if we want to provide selectivity of fields I
think configfs would provide an elegant way of dealing with this.
Think of echo'ing 0 or 1 to some /config/wireless/foo/radiotap/bar.
Alternative solutions to provide selectivity for radiotap can also be
to provide different levels of verbosity for wanted data, which would
make selectivity implementation much easier.

> If the public opinion
> has changed 180 degrees and configs is preferred over nl80211 that's
> fine by me as long as the implementation is sane..

I don't think the opinion has changed at all. I just think a configfs
frontend would be good as well, specially for some cases as
illustrated above.

  Luis

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 3/3] cfg80211: add wext-compatible client
  2007-02-08 19:55               ` Christoph Hellwig
  2007-02-08 21:56                 ` Luis R. Rodriguez
@ 2007-02-09  2:09                 ` Dan Williams
  1 sibling, 0 replies; 52+ messages in thread
From: Dan Williams @ 2007-02-09  2:09 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Luis R. Rodriguez, Johannes Berg, John W. Linville, linux-wireless

On Thu, 2007-02-08 at 19:55 +0000, Christoph Hellwig wrote:
> On Thu, Feb 08, 2007 at 01:38:02PM -0500, Luis R. Rodriguez wrote:
> > >> in the series actually converts a driver to use it.  Both to make this
> > >> code not dead and give people and example on how to use the cfg80211
> > >> infrastructure.
> > >
> > >I guess I could make softmac use it.
> > 
> > Another idea here is we can provide a configfs frontend to cfg80211
> > therefore any device which implements cfg80211 will automagically have
> > a userspace frontend (/configfs/wireless/foo) ready to test cfg80211.
> 
> Huh?  We already have a wext fronted to nicely test cfg80211, what we
> need are backends.  In addition to that I think we should agree on
> one primary userspace configuration method for the future instead of
> adding lots of them just because it's so easy.  If the public opinion
> has changed 180 degrees and configs is preferred over nl80211 that's
> fine by me as long as the implementation is sane..

I'd prefer to keep the netlink based API for use from userspace tools
like NetworkManager and wpa_supplicant...  configfs is nice for scripts
and screenscrapers but not really appropriate for desktop stuff.

Dan



^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 3/3] cfg80211: add wext-compatible client
  2007-02-08 19:41                 ` Luis R. Rodriguez
@ 2007-02-09 15:43                   ` Johannes Berg
  0 siblings, 0 replies; 52+ messages in thread
From: Johannes Berg @ 2007-02-09 15:43 UTC (permalink / raw)
  To: Luis R. Rodriguez; +Cc: John W. Linville, Christoph Hellwig, linux-wireless

[-- Attachment #1: Type: text/plain, Size: 1868 bytes --]

On Thu, 2007-02-08 at 14:41 -0500, Luis R. Rodriguez wrote:
> On 2/8/07, John W. Linville <linville@tuxdriver.com> wrote:
> > On Thu, Feb 08, 2007 at 01:38:02PM -0500, Luis R. Rodriguez wrote:
> > > On 2/8/07, Johannes Berg <johannes@sipsolutions.net> wrote:
> > > >On Wed, 2007-02-07 at 07:54 +0000, Christoph Hellwig wrote:
> > > >> Btw, if we submit cfg80211 for inclusion there should be at least one
> > > >patch
> > > >> in the series actually converts a driver to use it.  Both to make this
> > > >> code not dead and give people and example on how to use the cfg80211
> > > >> infrastructure.
> > > >
> > > >I guess I could make softmac use it.
> > >
> > > Another idea here is we can provide a configfs frontend to cfg80211
> > > therefore any device which implements cfg80211 will automagically have
> > > a userspace frontend (/configfs/wireless/foo) ready to test cfg80211.
> > > With this we wouldn't have to wait for the next userspace nl80211
> > > application to start testing cfg80211 directly (without wireless
> > > extensions compat). A configfs frontend can also allow more rapid
> > > development of userspace interfaces to configure wireless devices
> > > through cfg80211. Comments?
> >
> > Luis,
> >
> > You have mentioned this before, and FWIW I think it is a decent idea.
> > Let's see some code! :-)
> 
> I know, I know, talk is cheap, trust me, I abide by this motto every
> day. I actually wanted to just code this up and send this as part of
> my comment but do have other responsibilities I have to attend to
> first. I figured I'd throw it out though in case someone who does have
> time is willing to pick it up as I think it would be great for us.

Last time I tried configfs didn't support the group commit stuff yet so
it'd be only useful for *showing* things rather than changing...

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RFC PATCH 1/3] cfg80211 and nl80211
  2007-02-05 19:55                   ` Michael Wu
@ 2007-02-09 16:14                     ` Johannes Berg
  0 siblings, 0 replies; 52+ messages in thread
From: Johannes Berg @ 2007-02-09 16:14 UTC (permalink / raw)
  To: Michael Wu; +Cc: John W. Linville, linux-wireless

On Mon, 2007-02-05 at 14:55 -0500, Michael Wu wrote:

> 1. Userspace usually expects to send things on network interfaces. The kernel 
> usually receives frames from userspace on network interfaces. I don't see the 
> point of making an exception for 802.11 frames. (or rather, the potential for 
> simpler frame injection code is not enough to justify inventing a new 
> interface for doing it)

I guess I'm a purist here and would expect to be able to push frames of
whatever type the interface says it supports, if any. But this breaks,
you'll be pushing some sort of extensible format down there. [1]

Also, assuming that we do transmit software in userspace then has to
parse the 

> 2. nl80211/cfg80211 is for configuring interfaces. Everything there is related 
> to configuration of wireless interfaces except for the frame injection hook. 
> It doesn't look right.

Ok, well, I've removed it for now.

johannes

[1] it would have to be, I'm sure new frame injection requirements will
come up at some point and we can't break old apps



^ permalink raw reply	[flat|nested] 52+ messages in thread

end of thread, other threads:[~2007-02-09 16:16 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-01-31  1:37 [RFC] cfg80211 merge John W. Linville
2007-01-31  1:38 ` [RFC PATCH 1/3] cfg80211 and nl80211 John W. Linville
2007-01-31  1:39   ` [RFC PATCH 2/3] wireless: move wext to net/wireless/ John W. Linville
2007-01-31  1:41     ` [RFC PATCH 3/3] cfg80211: add wext-compatible client John W. Linville
2007-01-31 14:40     ` [RFC PATCH 2/3] wireless: move wext to net/wireless/ Christoph Hellwig
2007-01-31 19:00       ` Johannes Berg
2007-01-31  2:46   ` [RFC PATCH 1/3] cfg80211 and nl80211 Michael Wu
2007-01-31 17:37     ` Jiri Benc
2007-01-31 20:24       ` Michael Buesch
2007-02-01 10:18     ` Johannes Berg
2007-02-05 17:45       ` Michael Wu
2007-02-05 17:49         ` Johannes Berg
2007-02-05 18:14           ` Michael Wu
2007-02-05 18:14             ` Johannes Berg
2007-02-05 18:29               ` Jiri Benc
2007-02-05 18:29                 ` Johannes Berg
2007-02-05 19:13                 ` Jouni Malinen
2007-02-05 19:23               ` Michael Wu
2007-02-05 19:24                 ` Johannes Berg
2007-02-05 19:55                   ` Michael Wu
2007-02-09 16:14                     ` Johannes Berg
2007-02-05 18:16           ` Jiri Benc
2007-01-31  2:48 ` [RFC] cfg80211 merge Jouni Malinen
2007-01-31 17:29   ` Jiri Benc
2007-01-31 18:32     ` John W. Linville
2007-01-31 19:25       ` Jiri Benc
2007-01-31 20:07         ` Christoph Hellwig
2007-01-31 20:44           ` John W. Linville
2007-01-31 21:06             ` Johannes Berg
2007-01-31 23:54               ` Tomas Winkler
2007-02-01 13:07                 ` Johannes Berg
2007-02-01 14:04                   ` Tomas Winkler
2007-02-01 14:11                     ` Johannes Berg
2007-02-02 18:18                       ` Tomas Winkler
2007-02-03 17:37                         ` Johannes Berg
2007-02-01 14:12                     ` Jiri Benc
2007-02-07  0:46 ` [RFC v2] " John W. Linville
2007-02-07  0:47   ` [RFC PATCH 1/3] wireless: add cfg80211 John W. Linville
     [not found]     ` <20070207004832.GC23096@tuxdriver.com>
2007-02-07  0:49       ` [RFC PATCH 3/3] cfg80211: add wext-compatible client John W. Linville
2007-02-07  7:54         ` Christoph Hellwig
2007-02-08 13:13           ` Johannes Berg
2007-02-08 18:38             ` Luis R. Rodriguez
2007-02-08 18:50               ` John W. Linville
2007-02-08 19:41                 ` Luis R. Rodriguez
2007-02-09 15:43                   ` Johannes Berg
2007-02-08 19:55               ` Christoph Hellwig
2007-02-08 21:56                 ` Luis R. Rodriguez
2007-02-09  2:09                 ` Dan Williams
2007-02-07  7:35     ` [RFC PATCH 1/3] wireless: add cfg80211 Christoph Hellwig
2007-02-08 13:12       ` Johannes Berg
2007-02-08 19:17         ` Christoph Hellwig
2007-02-07 14:39   ` [RFC v2] cfg80211 merge John W. Linville

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.