linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "John W. Linville" <linville@tuxdriver.com>
To: wireless@lists.tuxdriver.org
Subject: [RFC PATCH 1/3] cfg80211 and nl80211
Date: Tue, 30 Jan 2007 20:38:40 -0500	[thread overview]
Message-ID: <20070131013840.GB28076@tuxdriver.com> (raw)
In-Reply-To: <20070131013717.GA28076@tuxdriver.com>

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

  reply	other threads:[~2007-01-31  2:04 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-01-31  1:37 [RFC] cfg80211 merge John W. Linville
2007-01-31  1:38 ` John W. Linville [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070131013840.GB28076@tuxdriver.com \
    --to=linville@tuxdriver.com \
    --cc=wireless@lists.tuxdriver.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).