Linux wireless drivers development
 help / color / mirror / Atom feed
* [RFC 04/11] cfg80211: implement iwpower
From: Johannes Berg @ 2009-06-24 12:07 UTC (permalink / raw)
  To: linux-wireless
In-Reply-To: <20090624120745.239294066@sipsolutions.net>

Just on/off and timeout, and with a hacky cfg80211 method
until we figure out what we want, though this is probably
sufficient as we want to use pm_qos for wifi everywhere.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 drivers/net/wireless/iwmc3200wifi/cfg80211.c |   22 ++++++++
 drivers/net/wireless/iwmc3200wifi/wext.c     |   47 ------------------
 include/net/cfg80211.h                       |   13 +++++
 net/mac80211/Kconfig                         |   16 ------
 net/mac80211/cfg.c                           |   26 ++++++++++
 net/mac80211/mlme.c                          |    5 -
 net/mac80211/wext.c                          |   70 ---------------------------
 net/wireless/Kconfig                         |   16 ++++++
 net/wireless/core.c                          |   11 +++-
 net/wireless/wext-compat.c                   |   60 +++++++++++++++++++++++
 10 files changed, 151 insertions(+), 135 deletions(-)

--- wireless-testing.orig/include/net/cfg80211.h	2009-06-24 13:54:06.000000000 +0200
+++ wireless-testing/include/net/cfg80211.h	2009-06-24 13:54:07.000000000 +0200
@@ -999,6 +999,10 @@ struct cfg80211_ops {
 	int	(*get_tx_power)(struct wiphy *wiphy, int *dbm);
 
 	void	(*rfkill_poll)(struct wiphy *wiphy);
+
+	/* some temporary stuff to finish wext */
+	int	(*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
+				  bool enabled, int timeout);
 };
 
 /*
@@ -1249,6 +1253,8 @@ struct wireless_dev {
 		u8 bssid[ETH_ALEN];
 		u8 ssid[IEEE80211_MAX_SSID_LEN];
 		s8 default_key, default_mgmt_key;
+		bool ps;
+		int ps_timeout;
 	} wext;
 #endif
 };
@@ -1593,6 +1599,13 @@ int cfg80211_wext_giwtxpower(struct net_
 			     struct iw_request_info *info,
 			     union iwreq_data *data, char *keybuf);
 
+int cfg80211_wext_siwpower(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_param *wrq, char *extra);
+int cfg80211_wext_giwpower(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_param *wrq, char *extra);
+
 /*
  * callbacks for asynchronous cfg80211 methods, notification
  * functions and BSS handling helpers
--- wireless-testing.orig/net/mac80211/Kconfig	2009-06-24 13:53:12.000000000 +0200
+++ wireless-testing/net/mac80211/Kconfig	2009-06-24 13:54:07.000000000 +0200
@@ -14,22 +14,6 @@ config MAC80211
 comment "CFG80211 needs to be enabled for MAC80211"
 	depends on CFG80211=n
 
-config MAC80211_DEFAULT_PS
-	bool "enable powersave by default"
-	depends on MAC80211
-	default y
-	help
-	  This option enables powersave mode by default.
-
-	  If this causes your applications to misbehave you should fix your
-	  applications instead -- they need to register their network
-	  latency requirement, see Documentation/power/pm_qos_interface.txt.
-
-config MAC80211_DEFAULT_PS_VALUE
-	int
-	default 1 if MAC80211_DEFAULT_PS
-	default 0
-
 menu "Rate control algorithm selection"
 	depends on MAC80211 != n
 
--- wireless-testing.orig/net/mac80211/wext.c	2009-06-24 13:54:06.000000000 +0200
+++ wireless-testing/net/mac80211/wext.c	2009-06-24 13:54:07.000000000 +0200
@@ -258,72 +258,6 @@ static int ieee80211_ioctl_giwrate(struc
 	return 0;
 }
 
-static int ieee80211_ioctl_siwpower(struct net_device *dev,
-				    struct iw_request_info *info,
-				    struct iw_param *wrq,
-				    char *extra)
-{
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_conf *conf = &local->hw.conf;
-	int timeout = 0;
-	bool ps;
-
-	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
-		return -EOPNOTSUPP;
-
-	if (sdata->vif.type != NL80211_IFTYPE_STATION)
-		return -EINVAL;
-
-	if (wrq->disabled) {
-		ps = false;
-		timeout = 0;
-		goto set;
-	}
-
-	switch (wrq->flags & IW_POWER_MODE) {
-	case IW_POWER_ON:       /* If not specified */
-	case IW_POWER_MODE:     /* If set all mask */
-	case IW_POWER_ALL_R:    /* If explicitely state all */
-		ps = true;
-		break;
-	default:                /* Otherwise we ignore */
-		return -EINVAL;
-	}
-
-	if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT))
-		return -EINVAL;
-
-	if (wrq->flags & IW_POWER_TIMEOUT)
-		timeout = wrq->value / 1000;
-
- set:
-	if (ps == sdata->u.mgd.powersave && timeout == conf->dynamic_ps_timeout)
-		return 0;
-
-	sdata->u.mgd.powersave = ps;
-	conf->dynamic_ps_timeout = timeout;
-
-	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
-		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-
-	ieee80211_recalc_ps(local, -1);
-
-	return 0;
-}
-
-static int ieee80211_ioctl_giwpower(struct net_device *dev,
-				    struct iw_request_info *info,
-				    union iwreq_data *wrqu,
-				    char *extra)
-{
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-	wrqu->power.disabled = !sdata->u.mgd.powersave;
-
-	return 0;
-}
-
 /* Get wireless statistics.  Called by /proc/net/wireless and by SIOCGIWSTATS */
 static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev)
 {
@@ -439,8 +373,8 @@ static const iw_handler ieee80211_handle
 	(iw_handler) cfg80211_wext_giwretry,		/* SIOCGIWRETRY */
 	(iw_handler) cfg80211_wext_siwencode,		/* SIOCSIWENCODE */
 	(iw_handler) cfg80211_wext_giwencode,		/* SIOCGIWENCODE */
-	(iw_handler) ieee80211_ioctl_siwpower,		/* SIOCSIWPOWER */
-	(iw_handler) ieee80211_ioctl_giwpower,		/* SIOCGIWPOWER */
+	(iw_handler) cfg80211_wext_siwpower,		/* SIOCSIWPOWER */
+	(iw_handler) cfg80211_wext_giwpower,		/* SIOCGIWPOWER */
 	(iw_handler) NULL,				/* -- hole -- */
 	(iw_handler) NULL,				/* -- hole -- */
 	(iw_handler) cfg80211_wext_siwgenie,		/* SIOCSIWGENIE */
--- wireless-testing.orig/net/wireless/Kconfig	2009-06-24 13:54:03.000000000 +0200
+++ wireless-testing/net/wireless/Kconfig	2009-06-24 13:54:07.000000000 +0200
@@ -11,6 +11,22 @@ config CFG80211_REG_DEBUG
 
 	  If unsure, say N.
 
+config CFG80211_DEFAULT_PS
+	bool "enable powersave by default"
+	depends on CFG80211
+	default y
+	help
+	  This option enables powersave mode by default.
+
+	  If this causes your applications to misbehave you should fix your
+	  applications instead -- they need to register their network
+	  latency requirement, see Documentation/power/pm_qos_interface.txt.
+
+config CFG80211_DEFAULT_PS_VALUE
+	int
+	default 1 if CFG80211_DEFAULT_PS
+	default 0
+
 config CFG80211_DEBUGFS
 	bool "cfg80211 DebugFS entries"
 	depends on CFG80211 && DEBUG_FS
--- wireless-testing.orig/net/wireless/core.c	2009-06-24 13:54:06.000000000 +0200
+++ wireless-testing/net/wireless/core.c	2009-06-24 13:54:07.000000000 +0200
@@ -550,11 +550,20 @@ static int cfg80211_netdev_notifier_call
 		}
 		wdev->netdev = dev;
 		wdev->sme_state = CFG80211_SME_IDLE;
+		mutex_unlock(&rdev->devlist_mtx);
 #ifdef CONFIG_WIRELESS_EXT
 		wdev->wext.default_key = -1;
 		wdev->wext.default_mgmt_key = -1;
+		wdev->wext.ps = CONFIG_CFG80211_DEFAULT_PS_VALUE;
+		wdev->wext.ps_timeout = 500;
+		if (rdev->ops->set_power_mgmt)
+			if (rdev->ops->set_power_mgmt(wdev->wiphy, dev,
+						      wdev->wext.ps,
+						      wdev->wext.ps_timeout)) {
+				/* assume this means it's off */
+				wdev->wext.ps = false;
+			}
 #endif
-		mutex_unlock(&rdev->devlist_mtx);
 		break;
 	case NETDEV_GOING_DOWN:
 		if (!wdev->ssid_len)
--- wireless-testing.orig/net/wireless/wext-compat.c	2009-06-24 13:54:06.000000000 +0200
+++ wireless-testing/net/wireless/wext-compat.c	2009-06-24 13:54:07.000000000 +0200
@@ -848,3 +848,63 @@ int cfg80211_wext_giwauth(struct net_dev
 	return -EOPNOTSUPP;
 }
 EXPORT_SYMBOL_GPL(cfg80211_wext_giwauth);
+
+int cfg80211_wext_siwpower(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_param *wrq, char *extra)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+	bool ps = wdev->wext.ps;
+	int timeout = wdev->wext.ps_timeout;
+	int err;
+
+	if (wdev->iftype != NL80211_IFTYPE_STATION)
+		return -EINVAL;
+
+	if (!rdev->ops->set_power_mgmt)
+		return -EOPNOTSUPP;
+
+	if (wrq->disabled) {
+		ps = false;
+	} else {
+		switch (wrq->flags & IW_POWER_MODE) {
+		case IW_POWER_ON:       /* If not specified */
+		case IW_POWER_MODE:     /* If set all mask */
+		case IW_POWER_ALL_R:    /* If explicitely state all */
+			ps = true;
+			break;
+		default:                /* Otherwise we ignore */
+			return -EINVAL;
+		}
+
+		if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT))
+			return -EINVAL;
+
+		if (wrq->flags & IW_POWER_TIMEOUT)
+			timeout = wrq->value / 1000;
+	}
+
+	err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, ps, timeout);
+	if (err)
+		return err;
+
+	wdev->wext.ps = ps;
+	wdev->wext.ps_timeout = timeout;
+
+	return 0;
+
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_siwpower);
+
+int cfg80211_wext_giwpower(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_param *wrq, char *extra)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+	wrq->disabled = !wdev->wext.ps;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_giwpower);
--- wireless-testing.orig/net/mac80211/cfg.c	2009-06-24 13:53:12.000000000 +0200
+++ wireless-testing/net/mac80211/cfg.c	2009-06-24 13:54:07.000000000 +0200
@@ -1376,6 +1376,31 @@ static void ieee80211_rfkill_poll(struct
 	drv_rfkill_poll(local);
 }
 
+static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
+				    bool enabled, int timeout)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_conf *conf = &local->hw.conf;
+
+	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
+		return -EOPNOTSUPP;
+
+	if (enabled == sdata->u.mgd.powersave &&
+	    timeout == conf->dynamic_ps_timeout)
+		return 0;
+
+	sdata->u.mgd.powersave = enabled;
+	conf->dynamic_ps_timeout = timeout;
+
+	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
+		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+
+	ieee80211_recalc_ps(local, -1);
+
+	return 0;
+}
+
 struct cfg80211_ops mac80211_config_ops = {
 	.add_virtual_intf = ieee80211_add_iface,
 	.del_virtual_intf = ieee80211_del_iface,
@@ -1418,4 +1443,5 @@ struct cfg80211_ops mac80211_config_ops 
 	.set_tx_power = ieee80211_set_tx_power,
 	.get_tx_power = ieee80211_get_tx_power,
 	.rfkill_poll = ieee80211_rfkill_poll,
+	.set_power_mgmt = ieee80211_set_power_mgmt,
 };
--- wireless-testing.orig/net/mac80211/mlme.c	2009-06-24 13:54:06.000000000 +0200
+++ wireless-testing/net/mac80211/mlme.c	2009-06-24 13:54:07.000000000 +0200
@@ -2354,11 +2354,6 @@ void ieee80211_sta_setup_sdata(struct ie
 		ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
 
 	hw_flags = sdata->local->hw.flags;
-
-	if (hw_flags & IEEE80211_HW_SUPPORTS_PS) {
-		ifmgd->powersave = CONFIG_MAC80211_DEFAULT_PS_VALUE;
-		sdata->local->hw.conf.dynamic_ps_timeout = 500;
-	}
 }
 
 /* configuration hooks */
--- wireless-testing.orig/drivers/net/wireless/iwmc3200wifi/cfg80211.c	2009-06-24 13:53:12.000000000 +0200
+++ wireless-testing/drivers/net/wireless/iwmc3200wifi/cfg80211.c	2009-06-24 13:54:07.000000000 +0200
@@ -522,6 +522,27 @@ static int iwm_cfg80211_get_txpower(stru
 	return 0;
 }
 
+static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+				       struct net_device *dev,
+				       bool enabled, int timeout)
+{
+	struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+	u32 power_index;
+
+	if (enabled)
+		power_index = IWM_POWER_INDEX_DEFAULT;
+	else
+		power_index = IWM_POWER_INDEX_MIN;
+
+	if (power_index == iwm->conf.power_index)
+		return 0;
+
+	iwm->conf.power_index = power_index;
+
+	return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				       CFG_POWER_INDEX, iwm->conf.power_index);
+}
+
 static struct cfg80211_ops iwm_cfg80211_ops = {
 	.change_virtual_intf = iwm_cfg80211_change_iface,
 	.add_key = iwm_cfg80211_add_key,
@@ -534,6 +555,7 @@ static struct cfg80211_ops iwm_cfg80211_
 	.leave_ibss = iwm_cfg80211_leave_ibss,
 	.set_tx_power = iwm_cfg80211_set_txpower,
 	.get_tx_power = iwm_cfg80211_get_txpower,
+	.set_power_mgmt = iwm_cfg80211_set_power_mgmt,
 };
 
 struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
--- wireless-testing.orig/drivers/net/wireless/iwmc3200wifi/wext.c	2009-06-24 13:53:12.000000000 +0200
+++ wireless-testing/drivers/net/wireless/iwmc3200wifi/wext.c	2009-06-24 13:54:07.000000000 +0200
@@ -238,49 +238,6 @@ static int iwm_set_wpa_version(struct iw
 	return 0;
 }
 
-static int iwm_wext_siwpower(struct net_device *dev,
-			     struct iw_request_info *info,
-			     struct iw_param *wrq, char *extra)
-{
-	struct iwm_priv *iwm = ndev_to_iwm(dev);
-	u32 power_index;
-
-	if (wrq->disabled) {
-		power_index = IWM_POWER_INDEX_MIN;
-		goto set;
-	} else
-		power_index = IWM_POWER_INDEX_DEFAULT;
-
-	switch (wrq->flags & IW_POWER_MODE) {
-	case IW_POWER_ON:
-	case IW_POWER_MODE:
-	case IW_POWER_ALL_R:
-		break;
-	default:
-		return -EINVAL;
-	}
-
- set:
-	if (power_index == iwm->conf.power_index)
-		return 0;
-
-	iwm->conf.power_index = power_index;
-
-	return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
-				       CFG_POWER_INDEX, iwm->conf.power_index);
-}
-
-static int iwm_wext_giwpower(struct net_device *dev,
-			     struct iw_request_info *info,
-			     union iwreq_data *wrqu, char *extra)
-{
-	struct iwm_priv *iwm = ndev_to_iwm(dev);
-
-	wrqu->power.disabled = (iwm->conf.power_index == IWM_POWER_INDEX_MIN);
-
-	return 0;
-}
-
 static int iwm_set_key_mgt(struct iwm_priv *iwm, u8 key_mgt)
 {
 	u8 *auth_type = &iwm->umac_profile->sec.auth_type;
@@ -458,8 +415,8 @@ static const iw_handler iwm_handlers[] =
 	(iw_handler) NULL,				/* SIOCGIWRETRY */
 	(iw_handler) cfg80211_wext_siwencode,		/* SIOCSIWENCODE */
 	(iw_handler) cfg80211_wext_giwencode,		/* SIOCGIWENCODE */
-	(iw_handler) iwm_wext_siwpower,			/* SIOCSIWPOWER */
-	(iw_handler) iwm_wext_giwpower,			/* SIOCGIWPOWER */
+	(iw_handler) cfg80211_wext_siwpower,		/* SIOCSIWPOWER */
+	(iw_handler) cfg80211_wext_giwpower,		/* SIOCGIWPOWER */
 	(iw_handler) NULL,				/* -- hole -- */
 	(iw_handler) NULL,				/* -- hole -- */
 	(iw_handler) NULL,                              /* SIOCSIWGENIE */

-- 


^ permalink raw reply

* [RFC 03/11] cfg80211: managed mode wext compatibility
From: Johannes Berg @ 2009-06-24 12:07 UTC (permalink / raw)
  To: linux-wireless
In-Reply-To: <20090624120745.239294066@sipsolutions.net>

This adds code to make it possible to use the cfg80211
connect() API with wireless extensions, and because the
previous patch added emulation of that API with auth()
and assoc(), by extension also supports wext on that.
At the same time, removes code from mac80211 for wext,
but doesn't yet clean up mac80211's mlme code more.

Signed-off-by: Samuel Ortiz <samuel.ortiz@intel.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 include/net/cfg80211.h     |   36 ++++-
 net/mac80211/mlme.c        |   69 ---------
 net/mac80211/wext.c        |  206 ++--------------------------
 net/wireless/Makefile      |    2 
 net/wireless/core.c        |   22 ++-
 net/wireless/core.h        |    3 
 net/wireless/nl80211.c     |    2 
 net/wireless/sme.c         |   45 +++---
 net/wireless/wext-compat.c |   90 ++++++------
 net/wireless/wext-sme.c    |  322 +++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 470 insertions(+), 327 deletions(-)

--- wireless-testing.orig/include/net/cfg80211.h	2009-06-24 13:54:06.000000000 +0200
+++ wireless-testing/include/net/cfg80211.h	2009-06-24 13:54:06.000000000 +0200
@@ -1240,8 +1240,14 @@ struct wireless_dev {
 #ifdef CONFIG_WIRELESS_EXT
 	/* wext data */
 	struct {
-		struct cfg80211_ibss_params ibss;
+		union {
+			struct cfg80211_ibss_params ibss;
+			struct cfg80211_connect_params connect;
+		};
+		u8 *ie;
+		size_t ie_len;
 		u8 bssid[ETH_ALEN];
+		u8 ssid[IEEE80211_MAX_SSID_LEN];
 		s8 default_key, default_mgmt_key;
 	} wext;
 #endif
@@ -1522,6 +1528,34 @@ int cfg80211_ibss_wext_giwap(struct net_
 			     struct iw_request_info *info,
 			     struct sockaddr *ap_addr, char *extra);
 
+int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
+			      struct iw_request_info *info,
+			      struct iw_freq *freq, char *extra);
+int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
+			      struct iw_request_info *info,
+			      struct iw_freq *freq, char *extra);
+int cfg80211_mgd_wext_siwessid(struct net_device *dev,
+			       struct iw_request_info *info,
+			       struct iw_point *data, char *ssid);
+int cfg80211_mgd_wext_giwessid(struct net_device *dev,
+			       struct iw_request_info *info,
+			       struct iw_point *data, char *ssid);
+int cfg80211_mgd_wext_siwap(struct net_device *dev,
+			    struct iw_request_info *info,
+			    struct sockaddr *ap_addr, char *extra);
+int cfg80211_mgd_wext_giwap(struct net_device *dev,
+			    struct iw_request_info *info,
+			    struct sockaddr *ap_addr, char *extra);
+int cfg80211_wext_siwgenie(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_point *data, char *extra);
+int cfg80211_wext_siwauth(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *data, char *extra);
+int cfg80211_wext_giwauth(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *data, char *extra);
+
 struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy,
 					     struct iw_freq *freq);
 
--- wireless-testing.orig/net/wireless/Makefile	2009-06-24 13:54:05.000000000 +0200
+++ wireless-testing/net/wireless/Makefile	2009-06-24 13:54:06.000000000 +0200
@@ -7,6 +7,6 @@ obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib
 
 cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o sme.o
 cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
-cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o
+cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o wext-sme.o
 
 ccflags-y += -D__CHECK_ENDIAN__
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/wireless/wext-sme.c	2009-06-24 13:54:06.000000000 +0200
@@ -0,0 +1,322 @@
+/*
+ * cfg80211 wext compat for managed mode.
+ *
+ * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (C) 2009   Intel Corporation. All rights reserved.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <net/cfg80211.h>
+#include "nl80211.h"
+
+static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
+				     struct wireless_dev *wdev)
+{
+	int err;
+
+	if (!netif_running(wdev->netdev))
+		return 0;
+
+	wdev->wext.connect.ie = wdev->wext.ie;
+	wdev->wext.connect.ie_len = wdev->wext.ie_len;
+	wdev->wext.connect.privacy = wdev->wext.default_key != -1;
+
+	err = 0;
+	if (wdev->wext.connect.ssid_len != 0)
+		err = cfg80211_connect(rdev, wdev->netdev,
+					&wdev->wext.connect);
+
+	return err;
+}
+
+int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
+			      struct iw_request_info *info,
+			      struct iw_freq *freq, char *extra)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+	struct ieee80211_channel *chan;
+	int err;
+
+	/* call only for station! */
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+		return -EINVAL;
+
+	chan = cfg80211_wext_freq(wdev->wiphy, freq);
+	if (chan && IS_ERR(chan))
+		return PTR_ERR(chan);
+
+	if (chan && (chan->flags & IEEE80211_CHAN_DISABLED))
+		return -EINVAL;
+
+	if (wdev->wext.connect.channel == chan)
+		return 0;
+
+	if (wdev->sme_state != CFG80211_SME_IDLE) {
+		bool event = true;
+		/* if SSID set, we'll try right again, avoid event */
+		if (wdev->wext.connect.ssid_len)
+			event = false;
+		err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy),
+					  dev, WLAN_REASON_DEAUTH_LEAVING,
+					  event);
+		if (err)
+			return err;
+	}
+
+	wdev->wext.connect.channel = chan;
+
+	/* SSID is not set, we just want to switch channel */
+	if (wdev->wext.connect.ssid_len && chan) {
+		if (!rdev->ops->set_channel)
+			return -EOPNOTSUPP;
+
+		return rdev->ops->set_channel(wdev->wiphy, chan,
+					      NL80211_CHAN_NO_HT);
+	}
+
+	return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev);
+}
+/* temporary symbol - mark GPL - in the future the handler won't be */
+EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwfreq);
+
+int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
+			      struct iw_request_info *info,
+			      struct iw_freq *freq, char *extra)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct ieee80211_channel *chan = NULL;
+
+	/* call only for station! */
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+		return -EINVAL;
+
+	if (wdev->current_bss)
+		chan = wdev->current_bss->channel;
+	else if (wdev->wext.connect.channel)
+		chan = wdev->wext.connect.channel;
+
+	if (chan) {
+		freq->m = chan->center_freq;
+		freq->e = 6;
+		return 0;
+	}
+
+	/* no channel if not joining */
+	return -EINVAL;
+}
+/* temporary symbol - mark GPL - in the future the handler won't be */
+EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwfreq);
+
+int cfg80211_mgd_wext_siwessid(struct net_device *dev,
+			       struct iw_request_info *info,
+			       struct iw_point *data, char *ssid)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	size_t len = data->length;
+	int err;
+
+	/* call only for station! */
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+		return -EINVAL;
+
+	if (wdev->sme_state != CFG80211_SME_IDLE) {
+		bool event = true;
+		/* if SSID set now, we'll try to connect, avoid event */
+		if (len)
+			event = false;
+		err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy),
+					  dev, WLAN_REASON_DEAUTH_LEAVING,
+					  event);
+		if (err)
+			return err;
+	}
+
+	/* iwconfig uses nul termination in SSID.. */
+	if (len > 0 && ssid[len - 1] == '\0')
+		len--;
+
+	if (data->flags) {
+		wdev->wext.connect.ssid = wdev->wext.ssid;
+		memcpy(wdev->wext.ssid, ssid, len);
+		wdev->wext.connect.ssid_len = len;
+	} else
+		wdev->wext.connect.ssid_len = 0;
+
+	wdev->wext.connect.control_port = false;
+
+	return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev);
+}
+/* temporary symbol - mark GPL - in the future the handler won't be */
+EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwessid);
+
+int cfg80211_mgd_wext_giwessid(struct net_device *dev,
+			       struct iw_request_info *info,
+			       struct iw_point *data, char *ssid)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+	/* call only for station! */
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+		return -EINVAL;
+
+	data->flags = 0;
+
+	if (wdev->ssid_len) {
+		data->flags = 1;
+		data->length = wdev->ssid_len;
+		memcpy(ssid, wdev->ssid, data->length);
+	} else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
+		data->flags = 1;
+		data->length = wdev->wext.connect.ssid_len;
+		memcpy(ssid, wdev->wext.connect.ssid, data->length);
+	} else
+		data->flags = 0;
+
+	return 0;
+}
+/* temporary symbol - mark GPL - in the future the handler won't be */
+EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwessid);
+
+int cfg80211_mgd_wext_siwap(struct net_device *dev,
+			    struct iw_request_info *info,
+			    struct sockaddr *ap_addr, char *extra)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	u8 *bssid = ap_addr->sa_data;
+	int err;
+
+	/* call only for station! */
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+		return -EINVAL;
+
+	if (ap_addr->sa_family != ARPHRD_ETHER)
+		return -EINVAL;
+
+	/* automatic mode */
+	if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
+		bssid = NULL;
+
+	/* both automatic */
+	if (!bssid && !wdev->wext.connect.bssid)
+		return 0;
+
+	/* fixed already - and no change */
+	if (wdev->wext.connect.bssid && bssid &&
+	    compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0)
+		return 0;
+
+	if (wdev->sme_state != CFG80211_SME_IDLE) {
+		err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy),
+					  dev, WLAN_REASON_DEAUTH_LEAVING,
+					  false);
+		if (err)
+			return err;
+	}
+
+	if (bssid) {
+		memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
+		wdev->wext.connect.bssid = wdev->wext.bssid;
+	} else
+		wdev->wext.connect.bssid = NULL;
+
+	return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev);
+}
+/* temporary symbol - mark GPL - in the future the handler won't be */
+EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwap);
+
+int cfg80211_mgd_wext_giwap(struct net_device *dev,
+			    struct iw_request_info *info,
+			    struct sockaddr *ap_addr, char *extra)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+	/* call only for station! */
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+		return -EINVAL;
+
+	ap_addr->sa_family = ARPHRD_ETHER;
+
+	if (wdev->current_bss)
+		memcpy(ap_addr->sa_data, wdev->current_bss->bssid, ETH_ALEN);
+	else
+		memcpy(ap_addr->sa_data, wdev->wext.connect.bssid, ETH_ALEN);
+
+	return 0;
+}
+/* temporary symbol - mark GPL - in the future the handler won't be */
+EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwap);
+
+int cfg80211_wext_siwgenie(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_point *data, char *extra)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+	u8 *ie = extra;
+	int ie_len = data->length, err;
+
+	if (wdev->iftype != NL80211_IFTYPE_STATION)
+		return -EOPNOTSUPP;
+
+	if (!ie_len)
+		ie = NULL;
+
+	/* no change */
+	if (wdev->wext.ie_len == ie_len &&
+	    memcmp(wdev->wext.ie, ie, ie_len) == 0)
+		return 0;
+
+	if (ie_len) {
+		ie = kmemdup(extra, ie_len, GFP_KERNEL);
+		if (!ie)
+			return -ENOMEM;
+	} else
+		ie = NULL;
+
+	kfree(wdev->wext.ie);
+	wdev->wext.ie = ie;
+	wdev->wext.ie_len = ie_len;
+
+	if (wdev->sme_state != CFG80211_SME_IDLE) {
+		err = cfg80211_disconnect(rdev, dev,
+					  WLAN_REASON_DEAUTH_LEAVING, false);
+		if (err)
+			return err;
+	}
+
+	/* userspace better not think we'll reconnect */
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_siwgenie);
+
+int cfg80211_wext_siwmlme(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_point *data, char *extra)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct iw_mlme *mlme = (struct iw_mlme *)extra;
+	struct cfg80211_registered_device *rdev;
+
+	if (!wdev)
+		return -EOPNOTSUPP;
+
+	rdev = wiphy_to_dev(wdev->wiphy);
+
+	if (wdev->iftype != NL80211_IFTYPE_STATION)
+		return -EINVAL;
+
+	if (mlme->addr.sa_family != ARPHRD_ETHER)
+		return -EINVAL;
+
+	switch (mlme->cmd) {
+	case IW_MLME_DEAUTH:
+	case IW_MLME_DISASSOC:
+		return cfg80211_disconnect(rdev, dev, mlme->reason_code,
+					   true);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme);
--- wireless-testing.orig/net/wireless/core.c	2009-06-24 13:54:06.000000000 +0200
+++ wireless-testing/net/wireless/core.c	2009-06-24 13:54:06.000000000 +0200
@@ -565,8 +565,13 @@ static int cfg80211_netdev_notifier_call
 			cfg80211_leave_ibss(rdev, dev, true);
 			break;
 		case NL80211_IFTYPE_STATION:
+#ifdef CONFIG_WIRELESS_EXT
+			kfree(wdev->wext.ie);
+			wdev->wext.ie = NULL;
+			wdev->wext.ie_len = 0;
+#endif
 			cfg80211_disconnect(rdev, dev,
-					    WLAN_REASON_DEAUTH_LEAVING);
+					    WLAN_REASON_DEAUTH_LEAVING, true);
 			break;
 		default:
 			break;
@@ -574,11 +579,20 @@ static int cfg80211_netdev_notifier_call
 		break;
 	case NETDEV_UP:
 #ifdef CONFIG_WIRELESS_EXT
-		if (wdev->iftype != NL80211_IFTYPE_ADHOC)
+		switch (wdev->iftype) {
+		case NL80211_IFTYPE_ADHOC:
+			if (wdev->wext.ibss.ssid_len)
+				cfg80211_join_ibss(rdev, dev,
+						   &wdev->wext.ibss);
+			break;
+		case NL80211_IFTYPE_STATION:
+			if (wdev->wext.connect.ssid_len)
+				cfg80211_connect(rdev, dev,
+						 &wdev->wext.connect);
 			break;
-		if (!wdev->wext.ibss.ssid_len)
+		default:
 			break;
-		cfg80211_join_ibss(rdev, dev, &wdev->wext.ibss);
+		}
 #endif
 		break;
 	case NETDEV_UNREGISTER:
--- wireless-testing.orig/net/mac80211/wext.c	2009-06-24 13:53:13.000000000 +0200
+++ wireless-testing/net/mac80211/wext.c	2009-06-24 13:54:06.000000000 +0200
@@ -27,29 +27,6 @@
 #include "aes_ccm.h"
 
 
-static int ieee80211_ioctl_siwgenie(struct net_device *dev,
-				    struct iw_request_info *info,
-				    struct iw_point *data, char *extra)
-{
-	struct ieee80211_sub_if_data *sdata;
-
-	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-		int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length);
-		if (ret && ret != -EALREADY)
-			return ret;
-		sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
-		sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
-		sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT;
-		if (ret != -EALREADY)
-			ieee80211_sta_req_auth(sdata);
-		return 0;
-	}
-
-	return -EOPNOTSUPP;
-}
-
 static int ieee80211_ioctl_siwfreq(struct net_device *dev,
 				   struct iw_request_info *info,
 				   struct iw_freq *freq, char *extra)
@@ -61,7 +38,7 @@ static int ieee80211_ioctl_siwfreq(struc
 	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
 		return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra);
 	else if (sdata->vif.type == NL80211_IFTYPE_STATION)
-		sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL;
+		return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra);
 
 	/* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */
 	if (freq->e == 0) {
@@ -95,9 +72,6 @@ static int ieee80211_ioctl_siwfreq(struc
 	if (local->oper_channel == chan)
 		return 0;
 
-	if (sdata->vif.type == NL80211_IFTYPE_STATION)
-		ieee80211_sta_req_auth(sdata);
-
 	local->oper_channel = chan;
 	local->oper_channel_type = NL80211_CHAN_NO_HT;
 	ieee80211_hw_config(local, 0);
@@ -115,6 +89,8 @@ static int ieee80211_ioctl_giwfreq(struc
 
 	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
 		return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
+	else if (sdata->vif.type == NL80211_IFTYPE_STATION)
+		return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra);
 
 	freq->m = local->oper_channel->center_freq;
 	freq->e = 6;
@@ -128,31 +104,11 @@ static int ieee80211_ioctl_siwessid(stru
 				    struct iw_point *data, char *ssid)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	size_t len = data->length;
-	int ret;
 
 	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
 		return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);
-
-	/* iwconfig uses nul termination in SSID.. */
-	if (len > 0 && ssid[len - 1] == '\0')
-		len--;
-
-	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-		if (data->flags)
-			sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
-		else
-			sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL;
-
-		ret = ieee80211_sta_set_ssid(sdata, ssid, len);
-		if (ret)
-			return ret;
-
-		sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
-		sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT;
-		ieee80211_sta_req_auth(sdata);
-		return 0;
-	}
+	else if (sdata->vif.type == NL80211_IFTYPE_STATION)
+		return cfg80211_mgd_wext_siwessid(dev, info, data, ssid);
 
 	return -EOPNOTSUPP;
 }
@@ -162,23 +118,14 @@ static int ieee80211_ioctl_giwessid(stru
 				    struct iw_request_info *info,
 				    struct iw_point *data, char *ssid)
 {
-	size_t len;
 	struct ieee80211_sub_if_data *sdata;
 
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
 	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
 		return cfg80211_ibss_wext_giwessid(dev, info, data, ssid);
-
-	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-		int res = ieee80211_sta_get_ssid(sdata, ssid, &len);
-		if (res == 0) {
-			data->length = len;
-			data->flags = 1;
-		} else
-			data->flags = 0;
-		return res;
-	}
+	else if (sdata->vif.type == NL80211_IFTYPE_STATION)
+		return cfg80211_mgd_wext_giwessid(dev, info, data, ssid);
 
 	return -EOPNOTSUPP;
 }
@@ -193,24 +140,10 @@ static int ieee80211_ioctl_siwap(struct 
 	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
 		return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
 
-	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-		int ret;
+	if (sdata->vif.type == NL80211_IFTYPE_STATION)
+		return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra);
 
-		if (is_zero_ether_addr((u8 *) &ap_addr->sa_data))
-			sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL |
-				IEEE80211_STA_AUTO_CHANNEL_SEL;
-		else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data))
-			sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL;
-		else
-			sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
-		ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data);
-		if (ret)
-			return ret;
-		sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
-		sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT;
-		ieee80211_sta_req_auth(sdata);
-		return 0;
-	} else if (sdata->vif.type == NL80211_IFTYPE_WDS) {
+	if (sdata->vif.type == NL80211_IFTYPE_WDS) {
 		/*
 		 * If it is necessary to update the WDS peer address
 		 * while the interface is running, then we need to do
@@ -240,14 +173,10 @@ static int ieee80211_ioctl_giwap(struct 
 	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
 		return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
 
-	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-		if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATED) {
-			ap_addr->sa_family = ARPHRD_ETHER;
-			memcpy(&ap_addr->sa_data, sdata->u.mgd.bssid, ETH_ALEN);
-		} else
-			memset(&ap_addr->sa_data, 0, ETH_ALEN);
-		return 0;
-	} else if (sdata->vif.type == NL80211_IFTYPE_WDS) {
+	if (sdata->vif.type == NL80211_IFTYPE_STATION)
+		return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra);
+
+	if (sdata->vif.type == NL80211_IFTYPE_WDS) {
 		ap_addr->sa_family = ARPHRD_ETHER;
 		memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN);
 		return 0;
@@ -395,85 +324,6 @@ static int ieee80211_ioctl_giwpower(stru
 	return 0;
 }
 
-static int ieee80211_ioctl_siwauth(struct net_device *dev,
-				   struct iw_request_info *info,
-				   struct iw_param *data, char *extra)
-{
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	int ret = 0;
-
-	switch (data->flags & IW_AUTH_INDEX) {
-	case IW_AUTH_WPA_VERSION:
-	case IW_AUTH_CIPHER_GROUP:
-	case IW_AUTH_WPA_ENABLED:
-	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
-	case IW_AUTH_KEY_MGMT:
-	case IW_AUTH_CIPHER_GROUP_MGMT:
-		break;
-	case IW_AUTH_CIPHER_PAIRWISE:
-		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-			if (data->value & (IW_AUTH_CIPHER_WEP40 |
-			    IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_TKIP))
-				sdata->u.mgd.flags |=
-					IEEE80211_STA_TKIP_WEP_USED;
-			else
-				sdata->u.mgd.flags &=
-					~IEEE80211_STA_TKIP_WEP_USED;
-		}
-		break;
-	case IW_AUTH_DROP_UNENCRYPTED:
-		sdata->drop_unencrypted = !!data->value;
-		break;
-	case IW_AUTH_PRIVACY_INVOKED:
-		if (sdata->vif.type != NL80211_IFTYPE_STATION)
-			ret = -EINVAL;
-		else {
-			sdata->u.mgd.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
-			/*
-			 * Privacy invoked by wpa_supplicant, store the
-			 * value and allow associating to a protected
-			 * network without having a key up front.
-			 */
-			if (data->value)
-				sdata->u.mgd.flags |=
-					IEEE80211_STA_PRIVACY_INVOKED;
-		}
-		break;
-	case IW_AUTH_80211_AUTH_ALG:
-		if (sdata->vif.type == NL80211_IFTYPE_STATION)
-			sdata->u.mgd.auth_algs = data->value;
-		else
-			ret = -EOPNOTSUPP;
-		break;
-	case IW_AUTH_MFP:
-		if (!(sdata->local->hw.flags & IEEE80211_HW_MFP_CAPABLE)) {
-			ret = -EOPNOTSUPP;
-			break;
-		}
-		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-			switch (data->value) {
-			case IW_AUTH_MFP_DISABLED:
-				sdata->u.mgd.mfp = IEEE80211_MFP_DISABLED;
-				break;
-			case IW_AUTH_MFP_OPTIONAL:
-				sdata->u.mgd.mfp = IEEE80211_MFP_OPTIONAL;
-				break;
-			case IW_AUTH_MFP_REQUIRED:
-				sdata->u.mgd.mfp = IEEE80211_MFP_REQUIRED;
-				break;
-			default:
-				ret = -EINVAL;
-			}
-		} else
-			ret = -EOPNOTSUPP;
-		break;
-	default:
-		ret = -EOPNOTSUPP;
-		break;
-	}
-	return ret;
-}
-
 /* Get wireless statistics.  Called by /proc/net/wireless and by SIOCGIWSTATS */
 static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev)
 {
@@ -541,28 +391,6 @@ static struct iw_statistics *ieee80211_g
 	return wstats;
 }
 
-static int ieee80211_ioctl_giwauth(struct net_device *dev,
-				   struct iw_request_info *info,
-				   struct iw_param *data, char *extra)
-{
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	int ret = 0;
-
-	switch (data->flags & IW_AUTH_INDEX) {
-	case IW_AUTH_80211_AUTH_ALG:
-		if (sdata->vif.type == NL80211_IFTYPE_STATION)
-			data->value = sdata->u.mgd.auth_algs;
-		else
-			ret = -EOPNOTSUPP;
-		break;
-	default:
-		ret = -EOPNOTSUPP;
-		break;
-	}
-	return ret;
-}
-
-
 /* Structures to export the Wireless Handlers */
 
 static const iw_handler ieee80211_handler[] =
@@ -615,10 +443,10 @@ static const iw_handler ieee80211_handle
 	(iw_handler) ieee80211_ioctl_giwpower,		/* SIOCGIWPOWER */
 	(iw_handler) NULL,				/* -- hole -- */
 	(iw_handler) NULL,				/* -- hole -- */
-	(iw_handler) ieee80211_ioctl_siwgenie,		/* SIOCSIWGENIE */
+	(iw_handler) cfg80211_wext_siwgenie,		/* SIOCSIWGENIE */
 	(iw_handler) NULL,				/* SIOCGIWGENIE */
-	(iw_handler) ieee80211_ioctl_siwauth,		/* SIOCSIWAUTH */
-	(iw_handler) ieee80211_ioctl_giwauth,		/* SIOCGIWAUTH */
+	(iw_handler) cfg80211_wext_siwauth,		/* SIOCSIWAUTH */
+	(iw_handler) cfg80211_wext_giwauth,		/* SIOCGIWAUTH */
 	(iw_handler) cfg80211_wext_siwencodeext,	/* SIOCSIWENCODEEXT */
 	(iw_handler) NULL,				/* SIOCGIWENCODEEXT */
 	(iw_handler) NULL,				/* SIOCSIWPMKSA */
--- wireless-testing.orig/net/mac80211/mlme.c	2009-06-24 13:54:04.000000000 +0200
+++ wireless-testing/net/mac80211/mlme.c	2009-06-24 13:54:06.000000000 +0200
@@ -870,70 +870,6 @@ static u32 ieee80211_handle_bss_capabili
 	return changed;
 }
 
-static void ieee80211_sta_send_apinfo(struct ieee80211_sub_if_data *sdata)
-{
-	union iwreq_data wrqu;
-
-	memset(&wrqu, 0, sizeof(wrqu));
-	if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED)
-		memcpy(wrqu.ap_addr.sa_data, sdata->u.mgd.bssid, ETH_ALEN);
-	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
-	wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL);
-}
-
-static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata)
-{
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	char *buf;
-	size_t len;
-	int i;
-	union iwreq_data wrqu;
-
-	if (!ifmgd->assocreq_ies && !ifmgd->assocresp_ies)
-		return;
-
-	buf = kmalloc(50 + 2 * (ifmgd->assocreq_ies_len +
-				ifmgd->assocresp_ies_len), GFP_KERNEL);
-	if (!buf)
-		return;
-
-	len = sprintf(buf, "ASSOCINFO(");
-	if (ifmgd->assocreq_ies) {
-		len += sprintf(buf + len, "ReqIEs=");
-		for (i = 0; i < ifmgd->assocreq_ies_len; i++) {
-			len += sprintf(buf + len, "%02x",
-				       ifmgd->assocreq_ies[i]);
-		}
-	}
-	if (ifmgd->assocresp_ies) {
-		if (ifmgd->assocreq_ies)
-			len += sprintf(buf + len, " ");
-		len += sprintf(buf + len, "RespIEs=");
-		for (i = 0; i < ifmgd->assocresp_ies_len; i++) {
-			len += sprintf(buf + len, "%02x",
-				       ifmgd->assocresp_ies[i]);
-		}
-	}
-	len += sprintf(buf + len, ")");
-
-	if (len > IW_CUSTOM_MAX) {
-		len = sprintf(buf, "ASSOCRESPIE=");
-		for (i = 0; i < ifmgd->assocresp_ies_len; i++) {
-			len += sprintf(buf + len, "%02x",
-				       ifmgd->assocresp_ies[i]);
-		}
-	}
-
-	if (len <= IW_CUSTOM_MAX) {
-		memset(&wrqu, 0, sizeof(wrqu));
-		wrqu.data.length = len;
-		wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf);
-	}
-
-	kfree(buf);
-}
-
-
 static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 				     u32 bss_info_changed)
 {
@@ -966,7 +902,6 @@ static void ieee80211_set_associated(str
 
 	ifmgd->flags |= IEEE80211_STA_PREV_BSSID_SET;
 	memcpy(ifmgd->prev_bssid, sdata->u.mgd.bssid, ETH_ALEN);
-	ieee80211_sta_send_associnfo(sdata);
 
 	ifmgd->last_probe = jiffies;
 	ieee80211_led_assoc(local, 1);
@@ -993,8 +928,6 @@ static void ieee80211_set_associated(str
 
 	netif_tx_start_all_queues(sdata->dev);
 	netif_carrier_on(sdata->dev);
-
-	ieee80211_sta_send_apinfo(sdata);
 }
 
 static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata)
@@ -1147,8 +1080,6 @@ static void ieee80211_set_disassoc(struc
 	changed |= BSS_CHANGED_ASSOC;
 	sdata->vif.bss_conf.assoc = false;
 
-	ieee80211_sta_send_apinfo(sdata);
-
 	if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT) {
 		ifmgd->state = IEEE80211_STA_MLME_DISABLED;
 		ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
--- wireless-testing.orig/net/wireless/wext-compat.c	2009-06-24 13:53:13.000000000 +0200
+++ wireless-testing/net/wireless/wext-compat.c	2009-06-24 13:54:06.000000000 +0200
@@ -261,50 +261,6 @@ int cfg80211_wext_giwrange(struct net_de
 }
 EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange);
 
-int cfg80211_wext_siwmlme(struct net_device *dev,
-			  struct iw_request_info *info,
-			  struct iw_point *data, char *extra)
-{
-	struct wireless_dev *wdev = dev->ieee80211_ptr;
-	struct iw_mlme *mlme = (struct iw_mlme *)extra;
-	struct cfg80211_registered_device *rdev;
-	union {
-		struct cfg80211_disassoc_request disassoc;
-		struct cfg80211_deauth_request deauth;
-	} cmd;
-
-	if (!wdev)
-		return -EOPNOTSUPP;
-
-	rdev = wiphy_to_dev(wdev->wiphy);
-
-	if (wdev->iftype != NL80211_IFTYPE_STATION)
-		return -EINVAL;
-
-	if (mlme->addr.sa_family != ARPHRD_ETHER)
-		return -EINVAL;
-
-	memset(&cmd, 0, sizeof(cmd));
-
-	switch (mlme->cmd) {
-	case IW_MLME_DEAUTH:
-		if (!rdev->ops->deauth)
-			return -EOPNOTSUPP;
-		cmd.deauth.peer_addr = mlme->addr.sa_data;
-		cmd.deauth.reason_code = mlme->reason_code;
-		return rdev->ops->deauth(wdev->wiphy, dev, &cmd.deauth);
-	case IW_MLME_DISASSOC:
-		if (!rdev->ops->disassoc)
-			return -EOPNOTSUPP;
-		cmd.disassoc.peer_addr = mlme->addr.sa_data;
-		cmd.disassoc.reason_code = mlme->reason_code;
-		return rdev->ops->disassoc(wdev->wiphy, dev, &cmd.disassoc);
-	default:
-		return -EOPNOTSUPP;
-	}
-}
-EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme);
-
 
 /**
  * cfg80211_wext_freq - get wext frequency for non-"auto"
@@ -846,3 +802,49 @@ int cfg80211_wext_giwtxpower(struct net_
 	return 0;
 }
 EXPORT_SYMBOL_GPL(cfg80211_wext_giwtxpower);
+
+int cfg80211_wext_siwauth(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *data, char *extra)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	int ret;
+
+	if (wdev->iftype != NL80211_IFTYPE_STATION)
+		return -EOPNOTSUPP;
+
+	switch (data->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_PRIVACY_INVOKED:
+		wdev->wext.connect.privacy = data->value;
+		ret = 0;
+		break;
+	case IW_AUTH_WPA_VERSION:
+	case IW_AUTH_CIPHER_GROUP:
+	case IW_AUTH_WPA_ENABLED:
+	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+	case IW_AUTH_KEY_MGMT:
+	case IW_AUTH_CIPHER_GROUP_MGMT:
+	case IW_AUTH_CIPHER_PAIRWISE:
+	case IW_AUTH_DROP_UNENCRYPTED:
+	case IW_AUTH_80211_AUTH_ALG:
+	case IW_AUTH_MFP:
+		ret = 0;
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_siwauth);
+
+int cfg80211_wext_giwauth(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *data, char *extra)
+{
+	/* XXX: what do we need? */
+
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_giwauth);
--- wireless-testing.orig/net/wireless/core.h	2009-06-24 13:54:06.000000000 +0200
+++ wireless-testing/net/wireless/core.h	2009-06-24 13:54:06.000000000 +0200
@@ -177,7 +177,8 @@ int cfg80211_connect(struct cfg80211_reg
 		     struct net_device *dev,
 		     struct cfg80211_connect_params *connect);
 int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
-			struct net_device *dev, u16 reason);
+			struct net_device *dev, u16 reason,
+			bool wextev);
 
 void cfg80211_conn_work(struct work_struct *work);
 
--- wireless-testing.orig/net/wireless/nl80211.c	2009-06-24 13:54:06.000000000 +0200
+++ wireless-testing/net/wireless/nl80211.c	2009-06-24 13:54:06.000000000 +0200
@@ -3540,7 +3540,7 @@ static int nl80211_disconnect(struct sk_
 		goto out;
 	}
 
-	err = cfg80211_disconnect(drv, dev, reason);
+	err = cfg80211_disconnect(drv, dev, reason, true);
 
 out:
 	cfg80211_put_dev(drv);
--- wireless-testing.orig/net/wireless/sme.c	2009-06-24 13:54:06.000000000 +0200
+++ wireless-testing/net/wireless/sme.c	2009-06-24 13:54:06.000000000 +0200
@@ -205,8 +205,9 @@ void cfg80211_sme_scan_done(struct net_d
 	}
 }
 
-void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
-			     u8 *ie, size_t ie_len, u16 status, gfp_t gfp)
+static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
+				      u8 *ie, size_t ie_len, u16 status,
+				      bool wextev, gfp_t gfp)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
 	struct cfg80211_bss *bss;
@@ -243,23 +244,33 @@ void cfg80211_connect_result(struct net_
 		wdev->sme_state = CFG80211_SME_IDLE;
 	}
 
-	nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid,
-				    ie, ie_len, status, gfp);
+	nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
+				    bssid, ie, ie_len, status, gfp);
 
 #ifdef CONFIG_WIRELESS_EXT
-	if (ie && status == WLAN_STATUS_SUCCESS) {
+	if (wextev) {
+		if (ie && status == WLAN_STATUS_SUCCESS) {
+			memset(&wrqu, 0, sizeof(wrqu));
+			wrqu.data.length = ie_len;
+			wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie);
+			printk(KERN_DEBUG "IWEVASSOCRESPIE EVENT\n");
+		}
+
 		memset(&wrqu, 0, sizeof(wrqu));
-		wrqu.data.length = ie_len;
-		wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie);
+		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+		if (bssid && status == WLAN_STATUS_SUCCESS)
+			memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
+		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
 	}
-
-	memset(&wrqu, 0, sizeof(wrqu));
-	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
-	if (bssid && status == WLAN_STATUS_SUCCESS)
-		memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
-	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
 #endif
 }
+
+void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
+			     u8 *ie, size_t ie_len, u16 status, gfp_t gfp)
+{
+	bool wextev = status == WLAN_STATUS_SUCCESS;
+	__cfg80211_connect_result(dev, bssid, ie, ie_len, status, wextev, gfp);
+}
 EXPORT_SYMBOL(cfg80211_connect_result);
 
 void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
@@ -430,7 +441,7 @@ int cfg80211_connect(struct cfg80211_reg
 }
 
 int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
-			struct net_device *dev, u16 reason)
+			struct net_device *dev, u16 reason, bool wextev)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
 	int err;
@@ -471,9 +482,9 @@ int cfg80211_disconnect(struct cfg80211_
 	if (wdev->sme_state == CFG80211_SME_CONNECTED)
 		__cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, 0, false);
 	else if (wdev->sme_state == CFG80211_SME_CONNECTING)
-		cfg80211_connect_result(dev, NULL, NULL, 0,
-					WLAN_STATUS_UNSPECIFIED_FAILURE,
-					GFP_KERNEL);
+		__cfg80211_connect_result(dev, NULL, NULL, 0,
+					  WLAN_STATUS_UNSPECIFIED_FAILURE,
+					  wextev, GFP_KERNEL);
 
 	return 0;
 }

-- 


^ permalink raw reply

* [RFC 02/11] cfg80211: emulate connect with auth/assoc
From: Johannes Berg @ 2009-06-24 12:07 UTC (permalink / raw)
  To: linux-wireless
In-Reply-To: <20090624120745.239294066@sipsolutions.net>

This adds code to cfg80211 so that drivers (mac80211 right
now) that don't implement connect but rather auth/assoc can
still be used with the nl80211 connect command. This will
also be necessary for the wext compat code.

Signed-off-by: Samuel Ortiz <samuel.ortiz@intel.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 include/net/cfg80211.h |   17 ++
 net/wireless/core.c    |    3 
 net/wireless/core.h    |    7 +
 net/wireless/mlme.c    |   94 ++++++++++++---
 net/wireless/nl80211.c |    4 
 net/wireless/scan.c    |    7 +
 net/wireless/sme.c     |  299 ++++++++++++++++++++++++++++++++++++++++++++++---
 7 files changed, 399 insertions(+), 32 deletions(-)

--- wireless-testing.orig/net/wireless/core.h	2009-06-24 13:54:05.000000000 +0200
+++ wireless-testing/net/wireless/core.h	2009-06-24 13:54:06.000000000 +0200
@@ -58,6 +58,8 @@ struct cfg80211_registered_device {
 	struct cfg80211_scan_request *scan_req; /* protected by RTNL */
 	unsigned long suspend_at;
 
+	struct work_struct conn_work;
+
 #ifdef CONFIG_CFG80211_DEBUGFS
 	/* Debugfs entries */
 	struct wiphy_debugfsdentries {
@@ -177,8 +179,13 @@ int cfg80211_connect(struct cfg80211_reg
 int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
 			struct net_device *dev, u16 reason);
 
+void cfg80211_conn_work(struct work_struct *work);
+
 /* internal helpers */
 int cfg80211_validate_key_settings(struct key_params *params, int key_idx,
 				   const u8 *mac_addr);
+void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie,
+			     size_t ie_len, u16 reason, bool from_ap);
+void cfg80211_sme_scan_done(struct net_device *dev);
 
 #endif /* __NET_WIRELESS_CORE_H */
--- wireless-testing.orig/net/wireless/nl80211.c	2009-06-24 13:54:05.000000000 +0200
+++ wireless-testing/net/wireless/nl80211.c	2009-06-24 13:54:06.000000000 +0200
@@ -349,12 +349,12 @@ static int nl80211_send_wiphy(struct sk_
 
 #undef CMD
 
-	if (dev->ops->connect) {
+	if (dev->ops->connect || dev->ops->auth) {
 		i++;
 		NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
 	}
 
-	if (dev->ops->disconnect) {
+	if (dev->ops->disconnect || dev->ops->deauth) {
 		i++;
 		NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
 	}
--- wireless-testing.orig/net/wireless/sme.c	2009-06-24 13:54:05.000000000 +0200
+++ wireless-testing/net/wireless/sme.c	2009-06-24 13:54:06.000000000 +0200
@@ -12,6 +12,198 @@
 #include <net/rtnetlink.h>
 #include "nl80211.h"
 
+static int cfg80211_conn_scan(struct wireless_dev *wdev)
+{
+	struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy);
+	struct cfg80211_scan_request *request;
+	int n_channels, err;
+
+	ASSERT_RTNL();
+
+	if (drv->scan_req)
+		return -EBUSY;
+
+	if (wdev->conn.params.channel) {
+		n_channels = 1;
+	} else {
+		enum ieee80211_band band;
+		n_channels = 0;
+
+		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+			if (!wdev->wiphy->bands[band])
+				continue;
+			n_channels += wdev->wiphy->bands[band]->n_channels;
+		}
+	}
+	request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +
+			  sizeof(request->channels[0]) * n_channels,
+			  GFP_KERNEL);
+	if (!request)
+		return -ENOMEM;
+
+	request->channels = (void *)((char *)request + sizeof(*request));
+	if (wdev->conn.params.channel)
+		request->channels[0] = wdev->conn.params.channel;
+	else {
+		int i = 0, j;
+		enum ieee80211_band band;
+
+		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+			if (!wdev->wiphy->bands[band])
+				continue;
+			for (j = 0; j < wdev->wiphy->bands[band]->n_channels;
+			     i++, j++)
+				request->channels[i] =
+					&wdev->wiphy->bands[band]->channels[j];
+		}
+	}
+	request->n_channels = n_channels;
+	request->ssids = (void *)(request->channels + n_channels);
+	request->n_ssids = 1;
+
+	memcpy(request->ssids[0].ssid, wdev->conn.params.ssid,
+		wdev->conn.params.ssid_len);
+	request->ssids[0].ssid_len = wdev->conn.params.ssid_len;
+
+	request->ifidx = wdev->netdev->ifindex;
+	request->wiphy = &drv->wiphy;
+
+	drv->scan_req = request;
+
+	err = drv->ops->scan(wdev->wiphy, wdev->netdev, request);
+	if (!err) {
+		wdev->conn.state = CFG80211_CONN_SCANNING;
+		nl80211_send_scan_start(drv, wdev->netdev);
+	} else {
+		drv->scan_req = NULL;
+		kfree(request);
+	}
+	return err;
+}
+
+static int cfg80211_conn_do_work(struct wireless_dev *wdev)
+{
+	struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy);
+	int err;
+	union {
+		struct cfg80211_auth_request auth_req;
+		struct cfg80211_assoc_request assoc_req;
+	} u;
+
+	memset(&u, 0, sizeof(u));
+
+	switch (wdev->conn.state) {
+	case CFG80211_CONN_SCAN_AGAIN:
+		return cfg80211_conn_scan(wdev);
+	case CFG80211_CONN_AUTHENTICATE_NEXT:
+		u.auth_req.chan = wdev->conn.params.channel;
+		u.auth_req.peer_addr = wdev->conn.params.bssid;
+		u.auth_req.ssid = wdev->conn.params.ssid;
+		u.auth_req.ssid_len = wdev->conn.params.ssid_len;
+		u.auth_req.auth_type = wdev->conn.params.auth_type;
+		u.auth_req.ie = NULL;
+		u.auth_req.ie_len = 0;
+		err = drv->ops->auth(wdev->wiphy, wdev->netdev, &u.auth_req);
+		if (!err)
+			wdev->conn.state = CFG80211_CONN_AUTHENTICATING;
+		return err;
+	case CFG80211_CONN_ASSOCIATE_NEXT:
+		u.assoc_req.chan = wdev->conn.params.channel;
+		u.assoc_req.peer_addr = wdev->conn.params.bssid;
+		u.assoc_req.ssid = wdev->conn.params.ssid;
+		u.assoc_req.ssid_len = wdev->conn.params.ssid_len;
+		u.assoc_req.ie = wdev->conn.params.ie;
+		u.assoc_req.ie_len = wdev->conn.params.ie_len;
+		u.assoc_req.use_mfp = false;
+		u.assoc_req.control_port = wdev->conn.params.control_port;
+		err = drv->ops->assoc(wdev->wiphy, wdev->netdev,
+				      &u.assoc_req);
+		if (!err)
+			wdev->conn.state = CFG80211_CONN_ASSOCIATING;
+		return err;
+	default:
+		return 0;
+	}
+}
+
+void cfg80211_conn_work(struct work_struct *work)
+{
+	struct cfg80211_registered_device *drv =
+		container_of(work, struct cfg80211_registered_device, conn_work);
+	struct wireless_dev *wdev;
+
+	rtnl_lock();
+	mutex_lock(&drv->devlist_mtx);
+
+	list_for_each_entry(wdev, &drv->netdev_list, list) {
+		if (!netif_running(wdev->netdev))
+			continue;
+		if (wdev->sme_state != CFG80211_SME_CONNECTING)
+			continue;
+		if (cfg80211_conn_do_work(wdev))
+			cfg80211_connect_result(wdev->netdev,
+						wdev->conn.params.bssid,
+						NULL, 0,
+						WLAN_STATUS_UNSPECIFIED_FAILURE,
+						GFP_ATOMIC);
+	}
+
+	mutex_unlock(&drv->devlist_mtx);
+	rtnl_unlock();
+}
+
+static bool cfg80211_get_conn_bss(struct wireless_dev *wdev)
+{
+	struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy);
+	struct cfg80211_bss *bss;
+	u16 capa = WLAN_CAPABILITY_ESS;
+
+	if (wdev->conn.params.privacy)
+		capa |= WLAN_CAPABILITY_PRIVACY;
+
+	bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn.params.bssid,
+			       wdev->conn.params.ssid,
+			       wdev->conn.params.ssid_len,
+			       WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
+			       capa);
+
+	if (!bss)
+		return false;
+
+	memcpy(wdev->conn.bssid, bss->bssid, ETH_ALEN);
+	wdev->conn.params.bssid = wdev->conn.bssid;
+	wdev->conn.params.channel = bss->channel;
+	wdev->conn.state = CFG80211_CONN_AUTHENTICATE_NEXT;
+	schedule_work(&drv->conn_work);
+
+	cfg80211_put_bss(bss);
+	return true;
+}
+
+void cfg80211_sme_scan_done(struct net_device *dev)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy);
+
+	if (wdev->sme_state != CFG80211_SME_CONNECTING)
+		return;
+
+	if (wdev->conn.state != CFG80211_CONN_SCANNING &&
+	    wdev->conn.state != CFG80211_CONN_SCAN_AGAIN)
+		return;
+
+	if (!cfg80211_get_conn_bss(wdev)) {
+		/* not found */
+		if (wdev->conn.state == CFG80211_CONN_SCAN_AGAIN)
+			schedule_work(&drv->conn_work);
+		else
+			cfg80211_connect_result(dev, wdev->conn.params.bssid,
+						NULL, 0,
+						WLAN_STATUS_UNSPECIFIED_FAILURE,
+						GFP_ATOMIC);
+		return;
+	}
+}
 
 void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
 			     u8 *ie, size_t ie_len, u16 status, gfp_t gfp)
@@ -63,7 +255,7 @@ void cfg80211_connect_result(struct net_
 
 	memset(&wrqu, 0, sizeof(wrqu));
 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
-	if (bssid)
+	if (bssid && status == WLAN_STATUS_SUCCESS)
 		memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
 	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
 #endif
@@ -122,9 +314,8 @@ void cfg80211_roamed(struct net_device *
 }
 EXPORT_SYMBOL(cfg80211_roamed);
 
-static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp,
-				    u8 *ie, size_t ie_len, u16 reason,
-				    bool from_ap)
+void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie,
+			     size_t ie_len, u16 reason, bool from_ap)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
 #ifdef CONFIG_WIRELESS_EXT
@@ -145,6 +336,9 @@ static void __cfg80211_disconnected(stru
 	wdev->current_bss = NULL;
 	wdev->sme_state = CFG80211_SME_IDLE;
 
+	kfree(wdev->conn.ie);
+	wdev->conn.ie = NULL;
+
 	nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev,
 				  reason, ie, ie_len, from_ap, gfp);
 
@@ -158,7 +352,7 @@ static void __cfg80211_disconnected(stru
 void cfg80211_disconnected(struct net_device *dev, u16 reason,
 			   u8 *ie, size_t ie_len, gfp_t gfp)
 {
-	__cfg80211_disconnected(dev, reason, ie, ie_len, true, gfp);
+	__cfg80211_disconnected(dev, gfp, ie, ie_len, reason, true);
 }
 EXPORT_SYMBOL(cfg80211_disconnected);
 
@@ -173,34 +367,113 @@ int cfg80211_connect(struct cfg80211_reg
 		return -EALREADY;
 
 	if (!rdev->ops->connect) {
-		return -EOPNOTSUPP;
+		if (!rdev->ops->auth || !rdev->ops->assoc)
+			return -EOPNOTSUPP;
+
+		/*
+		 * Keep the IEs, BSSID, SSID
+		 */
+		memcpy(&wdev->conn.params, connect, sizeof(*connect));
+		if (connect->bssid) {
+			wdev->conn.params.bssid = wdev->conn.bssid;
+			memcpy(wdev->conn.bssid, connect->bssid, ETH_ALEN);
+		}
+
+		if (connect->ie) {
+			wdev->conn.ie = kmemdup(connect->ie, connect->ie_len,
+						GFP_KERNEL);
+			wdev->conn.params.ie = wdev->conn.ie;
+			if (!wdev->conn.ie)
+				return -ENOMEM;
+		}
+
+		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
+		wdev->ssid_len = connect->ssid_len;
+		wdev->conn.params.ssid = wdev->ssid;
+		wdev->conn.params.ssid_len = connect->ssid_len;
+
+		/* don't care about result -- but fill bssid & channel */
+		if (!wdev->conn.params.bssid || !wdev->conn.params.channel)
+			cfg80211_get_conn_bss(wdev);
+
+		/* we're good if we have both BSSID and channel */
+		if (wdev->conn.params.bssid && wdev->conn.params.channel) {
+			wdev->conn.state = CFG80211_CONN_AUTHENTICATE_NEXT;
+			err = cfg80211_conn_do_work(wdev);
+		} else {
+			/* otherwise we'll need to scan for the AP first */
+			err = cfg80211_conn_scan(wdev);
+			/*
+			 * If we can't scan right now, then we need to scan again
+			 * after the current scan finished, since the parameters
+			 * changed (unless we find a good AP anyway).
+			 */
+			if (err == -EBUSY) {
+				err = 0;
+				wdev->conn.state = CFG80211_CONN_SCAN_AGAIN;
+			}
+		}
+		if (!err)
+			wdev->sme_state = CFG80211_SME_CONNECTING;
+
+		return err;
 	} else {
 		err = rdev->ops->connect(&rdev->wiphy, dev, connect);
 		if (err)
 			return err;
-		wdev->sme_state = CFG80211_SME_CONNECTING;
-	}
 
-	memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
-	wdev->ssid_len = connect->ssid_len;
+		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
+		wdev->ssid_len = connect->ssid_len;
 
-	return 0;
+		return 0;
+	}
 }
 
 int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
 			struct net_device *dev, u16 reason)
 {
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
 	int err;
 
+	if (wdev->sme_state == CFG80211_SME_IDLE)
+		return -EINVAL;
+
 	if (!rdev->ops->disconnect) {
-		return -EOPNOTSUPP;
+		struct cfg80211_deauth_request deauth;
+		u8 bssid[ETH_ALEN];
+
+		if (wdev->sme_state == CFG80211_SME_CONNECTING &&
+		    (wdev->conn.state == CFG80211_CONN_SCANNING ||
+		     wdev->conn.state == CFG80211_CONN_SCAN_AGAIN)) {
+			wdev->sme_state = CFG80211_SME_IDLE;
+			return 0;
+		}
+
+		if (!rdev->ops->deauth)
+			return -EOPNOTSUPP;
+
+		memset(&deauth, 0, sizeof(deauth));
+
+		/* wdev->conn.params.bssid must be set if > SCANNING */
+		memcpy(bssid, wdev->conn.params.bssid, ETH_ALEN);
+		deauth.peer_addr = bssid;
+		deauth.reason_code = reason;
+
+		err = rdev->ops->deauth(&rdev->wiphy, dev, &deauth);
+		if (err)
+			return err;
 	} else {
 		err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
 		if (err)
 			return err;
 	}
 
-	__cfg80211_disconnected(dev, 0, NULL, 0, false, GFP_KERNEL);
+	if (wdev->sme_state == CFG80211_SME_CONNECTED)
+		__cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, 0, false);
+	else if (wdev->sme_state == CFG80211_SME_CONNECTING)
+		cfg80211_connect_result(dev, NULL, NULL, 0,
+					WLAN_STATUS_UNSPECIFIED_FAILURE,
+					GFP_KERNEL);
 
 	return 0;
 }
--- wireless-testing.orig/net/wireless/mlme.c	2009-06-24 13:54:04.000000000 +0200
+++ wireless-testing/net/wireless/mlme.c	2009-06-24 13:54:06.000000000 +0200
@@ -14,51 +14,110 @@
 
 void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp)
 {
-	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+	u16 status_code;
+	struct ieee80211_mgmt *mgmt;
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct wiphy *wiphy = wdev->wiphy;
 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+	mgmt = (struct ieee80211_mgmt *)buf;
+	status_code = le16_to_cpu(mgmt->u.auth.status_code);
+
 	nl80211_send_rx_auth(rdev, dev, buf, len, gfp);
+
+	if (status_code != WLAN_STATUS_SUCCESS)
+		wdev->sme_state = CFG80211_SME_IDLE;
+	else if (wdev->sme_state == CFG80211_SME_CONNECTING &&
+		 wdev->conn.state == CFG80211_CONN_AUTHENTICATING) {
+		wdev->conn.state = CFG80211_CONN_ASSOCIATE_NEXT;
+		schedule_work(&rdev->conn_work);
+	}
 }
 EXPORT_SYMBOL(cfg80211_send_rx_auth);
 
 void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp)
 {
-	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
-	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+	u16 status_code;
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct wiphy *wiphy = wdev->wiphy;
+	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
+	u8 *ie = mgmt->u.assoc_resp.variable;
+	int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+
+	status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+
 	nl80211_send_rx_assoc(rdev, dev, buf, len, gfp);
+
+	if (wdev->sme_state == CFG80211_SME_CONNECTING)
+		cfg80211_connect_result(dev, mgmt->bssid, ie, len - ieoffs,
+					status_code, gfp);
+	if (status_code != WLAN_STATUS_SUCCESS) {
+		/* XXX: try other auth algs? */
+	}
 }
 EXPORT_SYMBOL(cfg80211_send_rx_assoc);
 
 void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp)
 {
-	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct wiphy *wiphy = wdev->wiphy;
 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
+
 	nl80211_send_deauth(rdev, dev, buf, len, gfp);
+
+	if (wdev->sme_state == CFG80211_SME_CONNECTED) {
+		u16 reason_code;
+		bool from_ap;
+
+		reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+
+		from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0;
+		__cfg80211_disconnected(dev, gfp, NULL, 0,
+					reason_code, from_ap);
+	} else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
+		cfg80211_connect_result(dev, mgmt->bssid, NULL, 0,
+					WLAN_STATUS_UNSPECIFIED_FAILURE, gfp);
+	}
 }
 EXPORT_SYMBOL(cfg80211_send_deauth);
 
 void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp)
 {
-	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct wiphy *wiphy = wdev->wiphy;
 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
+	u8 *ie = mgmt->u.assoc_resp.variable;
+	int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+
 	nl80211_send_disassoc(rdev, dev, buf, len, gfp);
-}
-EXPORT_SYMBOL(cfg80211_send_disassoc);
 
-static void cfg80211_wext_disconnected(struct net_device *dev)
-{
-#ifdef CONFIG_WIRELESS_EXT
-	union iwreq_data wrqu;
-	memset(&wrqu, 0, sizeof(wrqu));
-	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
-#endif
+	if (wdev->sme_state == CFG80211_SME_CONNECTED) {
+		u16 reason_code;
+		bool from_ap;
+
+		reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+
+		from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0;
+		__cfg80211_disconnected(dev, gfp, ie, len - ieoffs,
+					reason_code, from_ap);
+
+		wdev->sme_state = CFG80211_SME_IDLE;
+	}
 }
+EXPORT_SYMBOL(cfg80211_send_disassoc);
 
 void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp)
 {
 	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
 	nl80211_send_auth_timeout(rdev, dev, addr, gfp);
-	cfg80211_wext_disconnected(dev);
+	if (dev->ieee80211_ptr->sme_state == CFG80211_SME_CONNECTING)
+		cfg80211_connect_result(dev, addr, NULL, 0,
+					WLAN_STATUS_UNSPECIFIED_FAILURE, gfp);
+	dev->ieee80211_ptr->sme_state = CFG80211_SME_IDLE;
 }
 EXPORT_SYMBOL(cfg80211_send_auth_timeout);
 
@@ -67,7 +126,10 @@ void cfg80211_send_assoc_timeout(struct 
 	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
 	nl80211_send_assoc_timeout(rdev, dev, addr, gfp);
-	cfg80211_wext_disconnected(dev);
+	if (dev->ieee80211_ptr->sme_state == CFG80211_SME_CONNECTING)
+		cfg80211_connect_result(dev, addr, NULL, 0,
+					WLAN_STATUS_UNSPECIFIED_FAILURE, gfp);
+	dev->ieee80211_ptr->sme_state = CFG80211_SME_IDLE;
 }
 EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
 
--- wireless-testing.orig/net/wireless/core.c	2009-06-24 13:54:05.000000000 +0200
+++ wireless-testing/net/wireless/core.c	2009-06-24 13:54:06.000000000 +0200
@@ -321,6 +321,7 @@ struct wiphy *wiphy_new(const struct cfg
 	}
 
 	INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work);
+	INIT_WORK(&drv->conn_work, cfg80211_conn_work);
 
 	/*
 	 * Initialize wiphy parameters to IEEE 802.11 MIB default values.
@@ -481,6 +482,8 @@ void wiphy_unregister(struct wiphy *wiph
 	/* unlock again before freeing */
 	mutex_unlock(&drv->mtx);
 
+	cancel_work_sync(&drv->conn_work);
+
 	cfg80211_debugfs_drv_del(drv);
 
 	/* If this device got a regulatory hint tell core its
--- wireless-testing.orig/include/net/cfg80211.h	2009-06-24 13:54:05.000000000 +0200
+++ wireless-testing/include/net/cfg80211.h	2009-06-24 13:54:06.000000000 +0200
@@ -1218,9 +1218,24 @@ struct wireless_dev {
 	u8 ssid_len;
 	enum {
 		CFG80211_SME_IDLE,
-		CFG80211_SME_CONNECTING, /* ->connect called */
+		CFG80211_SME_CONNECTING,
 		CFG80211_SME_CONNECTED,
 	} sme_state;
+	struct {
+		struct cfg80211_connect_params params;
+		/* these are sub-states of the _CONNECTING sme_state */
+		enum {
+			CFG80211_CONN_SCANNING,
+			CFG80211_CONN_SCAN_AGAIN,
+			CFG80211_CONN_AUTHENTICATE_NEXT,
+			CFG80211_CONN_AUTHENTICATING,
+			CFG80211_CONN_ASSOCIATE_NEXT,
+			CFG80211_CONN_ASSOCIATING,
+		} state;
+		u8 bssid[ETH_ALEN];
+		u8 *ie;
+		size_t ie_len;
+	} conn;
 
 #ifdef CONFIG_WIRELESS_EXT
 	/* wext data */
--- wireless-testing.orig/net/wireless/scan.c	2009-06-24 13:53:13.000000000 +0200
+++ wireless-testing/net/wireless/scan.c	2009-06-24 13:54:06.000000000 +0200
@@ -30,6 +30,13 @@ void cfg80211_scan_done(struct cfg80211_
 
 	WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
 
+	/*
+	 * This must be before sending the other events!
+	 * Otherwise, wpa_supplicant gets completely confused with
+	 * wext events.
+	 */
+	cfg80211_sme_scan_done(dev);
+
 	if (aborted)
 		nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev);
 	else

-- 


^ permalink raw reply

* [RFC 01/11] cfg80211: connect/disconnect API
From: Johannes Berg @ 2009-06-24 12:07 UTC (permalink / raw)
  To: linux-wireless; +Cc: Samuel Ortiz
In-Reply-To: <20090624120745.239294066@sipsolutions.net>

From: Samuel Ortiz <sameo@linux.intel.com>

This patch introduces the cfg80211 connect/disconnect API.
The goal here is to run the AUTH and ASSOC steps in one call.
This is needed for some fullmac cards that run both steps
directly from the target, after the host driver sends a
connect command.

Signed-off-by: Samuel Ortiz <samuel.ortiz@intel.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
v2: * everything changed/split up differently

 include/linux/nl80211.h |   32 +++++
 include/net/cfg80211.h  |  100 ++++++++++++++++++
 net/wireless/Makefile   |    2 
 net/wireless/core.c     |   16 ++
 net/wireless/core.h     |    7 +
 net/wireless/nl80211.c  |  264 ++++++++++++++++++++++++++++++++++++++++++++++++
 net/wireless/nl80211.h  |   11 ++
 net/wireless/sme.c      |  206 +++++++++++++++++++++++++++++++++++++
 8 files changed, 633 insertions(+), 5 deletions(-)

--- wireless-testing.orig/include/linux/nl80211.h	2009-06-24 13:53:14.000000000 +0200
+++ wireless-testing/include/linux/nl80211.h	2009-06-24 13:54:05.000000000 +0200
@@ -242,6 +242,22 @@
  * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is
  *	determined by the network interface.
  *
+ * @NL80211_CMD_CONNECT: connection request and notification; this command
+ *	requests to connect to a specified network but without separating
+ *	auth and assoc steps. For this, you need to specify the SSID in a
+ *	%NL80211_ATTR_SSID attribute, and can optionally specify the association
+ *	IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC,
+ *	%NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_CONTROL_PORT.
+ *	It is also sent as an event, with the BSSID and response IEs when the
+ *	connection is established or failed to be established. This can be
+ *	determined by the STATUS_CODE attribute.
+ * @NL80211_CMD_ROAM: request that the card roam (currently not implemented),
+ *	sent as an event when the card/driver roamed by itself.
+ * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
+ *	userspace that a connection was dropped by the AP or due to other
+ *	reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
+ *	%NL80211_ATTR_REASON_CODE attributes are used.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -310,6 +326,10 @@ enum nl80211_commands {
 	NL80211_CMD_JOIN_IBSS,
 	NL80211_CMD_LEAVE_IBSS,
 
+	NL80211_CMD_CONNECT,
+	NL80211_CMD_ROAM,
+	NL80211_CMD_DISCONNECT,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -511,6 +531,14 @@ enum nl80211_commands {
  *	authorized by user space. Otherwise, port is marked authorized by
  *	default in station mode.
  *
+ * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT
+ *	event was due to the AP disconnecting the station, and not due to
+ *	a local disconnect request.
+ * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT
+ *	event (u16)
+ * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating
+ *	that protected APs should be used.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -618,6 +646,10 @@ enum nl80211_attrs {
 	NL80211_ATTR_STA_FLAGS2,
 
 	NL80211_ATTR_CONTROL_PORT,
+	NL80211_ATTR_PRIVACY,
+
+	NL80211_ATTR_DISCONNECTED_BY_AP,
+	NL80211_ATTR_STATUS_CODE,
 
 	/* add attributes here, update the policy in nl80211.c */
 
--- wireless-testing.orig/include/net/cfg80211.h	2009-06-24 13:54:05.000000000 +0200
+++ wireless-testing/include/net/cfg80211.h	2009-06-24 13:54:05.000000000 +0200
@@ -738,6 +738,39 @@ struct cfg80211_ibss_params {
 };
 
 /**
+ * struct cfg80211_connect_params - Connection parameters
+ *
+ * This structure provides information needed to complete IEEE 802.11
+ * authentication and association.
+ *
+ * @channel: The channel to use or %NULL if not specified (auto-select based
+ *	on scan results)
+ * @bssid: The AP BSSID or %NULL if not specified (auto-select based on scan
+ *	results)
+ * @ssid: SSID
+ * @ssid_len: Length of ssid in octets
+ * @auth_type: Authentication type (algorithm)
+ * @assoc_ie: IEs for association request
+ * @assoc_ie_len: Length of assoc_ie in octets
+ * @control_port: Whether user space controls IEEE 802.1X port, i.e.,
+ *	sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is
+ *	required to assume that the port is unauthorized until authorized by
+ *	user space. Otherwise, port is marked authorized by default.
+ * @privacy: indicates whether privacy-enabled APs should be used
+ */
+struct cfg80211_connect_params {
+	struct ieee80211_channel *channel;
+	u8 *bssid;
+	u8 *ssid;
+	size_t ssid_len;
+	enum nl80211_auth_type auth_type;
+	u8 *ie;
+	size_t ie_len;
+	bool control_port;
+	bool privacy;
+};
+
+/**
  * enum wiphy_params_flags - set_wiphy_params bitfield values
  * WIPHY_PARAM_RETRY_SHORT: wiphy->retry_short has changed
  * WIPHY_PARAM_RETRY_LONG: wiphy->retry_long has changed
@@ -841,6 +874,12 @@ enum tx_power_setting {
  * @deauth: Request to deauthenticate from the specified peer
  * @disassoc: Request to disassociate from the specified peer
  *
+ * @connect: Connect to the ESS with the specified parameters. When connected,
+ *	call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS.
+ *	If the connection fails for some reason, call cfg80211_connect_result()
+ *	with the status from the AP.
+ * @disconnect: Disconnect from the BSS/ESS.
+ *
  * @join_ibss: Join the specified IBSS (or create if necessary). Once done, call
  *	cfg80211_ibss_joined(), also call that function when changing BSSID due
  *	to a merge.
@@ -944,6 +983,11 @@ struct cfg80211_ops {
 	int	(*disassoc)(struct wiphy *wiphy, struct net_device *dev,
 			    struct cfg80211_disassoc_request *req);
 
+	int	(*connect)(struct wiphy *wiphy, struct net_device *dev,
+			   struct cfg80211_connect_params *sme);
+	int	(*disconnect)(struct wiphy *wiphy, struct net_device *dev,
+			      u16 reason_code);
+
 	int	(*join_ibss)(struct wiphy *wiphy, struct net_device *dev,
 			     struct cfg80211_ibss_params *params);
 	int	(*leave_ibss)(struct wiphy *wiphy, struct net_device *dev);
@@ -1168,10 +1212,15 @@ struct wireless_dev {
 	struct list_head list;
 	struct net_device *netdev;
 
-	/* currently used for IBSS - might be rearranged in the future */
+	/* currently used for IBSS and SME - might be rearranged later */
 	struct cfg80211_bss *current_bss;
 	u8 ssid[IEEE80211_MAX_SSID_LEN];
 	u8 ssid_len;
+	enum {
+		CFG80211_SME_IDLE,
+		CFG80211_SME_CONNECTING, /* ->connect called */
+		CFG80211_SME_CONNECTED,
+	} sme_state;
 
 #ifdef CONFIG_WIRELESS_EXT
 	/* wext data */
@@ -1705,4 +1754,53 @@ void wiphy_rfkill_start_polling(struct w
  */
 void wiphy_rfkill_stop_polling(struct wiphy *wiphy);
 
+/**
+ * cfg80211_connect_result - notify cfg80211 of connection result
+ *
+ * @dev: network device
+ * @bssid: the BSSID of the AP
+ * @ie: association response IEs (may be %NULL)
+ * @ie_len: assoc response IEs length
+ * @status: status code, 0 for successful connection, use
+ *	%WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
+ *	the real status code for failures.
+ * @gfp: allocation flags
+ *
+ * It should be called by the underlying driver whenever connect() has
+ * succeeded.
+ */
+void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
+			     u8 *ie, size_t ie_len, u16 status, gfp_t gfp);
+
+/**
+ * cfg80211_roamed - notify cfg80211 of roaming
+ *
+ * @dev: network device
+ * @bssid: the BSSID of the new AP
+ * @ie: association response IEs (may be %NULL)
+ * @ie_len: assoc response IEs length
+ * @gfp: allocation flags
+ *
+ * It should be called by the underlying driver whenever it roamed
+ * from one AP to another while connected.
+ */
+void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
+		     u8 *ie, size_t ie_len, gfp_t gfp);
+
+/**
+ * cfg80211_disconnected - notify cfg80211 that connection was dropped
+ *
+ * @dev: network device
+ * @ie: information elements of the deauth/disassoc frame (may be %NULL)
+ * @ie_len: length of IEs
+ * @reason: reason code for the disconnection, set it to 0 if unknown
+ * @gfp: allocation flags
+ *
+ * After it calls this function, the driver should enter an idle state
+ * and not try to connect to any AP any more.
+ */
+void cfg80211_disconnected(struct net_device *dev, u16 reason,
+			   u8 *ie, size_t ie_len, gfp_t gfp);
+
+
 #endif /* __NET_CFG80211_H */
--- wireless-testing.orig/net/wireless/Makefile	2009-06-24 13:53:14.000000000 +0200
+++ wireless-testing/net/wireless/Makefile	2009-06-24 13:54:05.000000000 +0200
@@ -5,7 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib8
 obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
 obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
 
-cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o
+cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o sme.o
 cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
 cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o
 
--- wireless-testing.orig/net/wireless/core.h	2009-06-24 13:53:14.000000000 +0200
+++ wireless-testing/net/wireless/core.h	2009-06-24 13:54:05.000000000 +0200
@@ -170,6 +170,13 @@ void cfg80211_clear_ibss(struct net_devi
 int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
 			struct net_device *dev, bool nowext);
 
+/* SME */
+int cfg80211_connect(struct cfg80211_registered_device *rdev,
+		     struct net_device *dev,
+		     struct cfg80211_connect_params *connect);
+int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
+			struct net_device *dev, u16 reason);
+
 /* internal helpers */
 int cfg80211_validate_key_settings(struct key_params *params, int key_idx,
 				   const u8 *mac_addr);
--- wireless-testing.orig/net/wireless/nl80211.c	2009-06-24 13:54:04.000000000 +0200
+++ wireless-testing/net/wireless/nl80211.c	2009-06-24 13:54:05.000000000 +0200
@@ -128,6 +128,7 @@ static struct nla_policy nl80211_policy[
 		.len = sizeof(struct nl80211_sta_flag_update),
 	},
 	[NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
+	[NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
 };
 
 /* IE validation */
@@ -347,6 +348,17 @@ static int nl80211_send_wiphy(struct sk_
 	CMD(join_ibss, JOIN_IBSS);
 
 #undef CMD
+
+	if (dev->ops->connect) {
+		i++;
+		NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
+	}
+
+	if (dev->ops->disconnect) {
+		i++;
+		NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
+	}
+
 	nla_nest_end(msg, nl_cmds);
 
 	return genlmsg_end(msg, hdr);
@@ -3415,6 +3427,129 @@ unlock_rtnl:
 	return err;
 }
 
+static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_device *drv;
+	struct net_device *dev;
+	struct cfg80211_connect_params connect;
+	struct wiphy *wiphy;
+	int err;
+
+	memset(&connect, 0, sizeof(connect));
+
+	if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+		return -EINVAL;
+
+	if (!info->attrs[NL80211_ATTR_SSID] ||
+	    !nla_len(info->attrs[NL80211_ATTR_SSID]))
+		return -EINVAL;
+
+	rtnl_lock();
+
+	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+	if (err)
+		goto unlock_rtnl;
+
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	if (!netif_running(dev)) {
+		err = -ENETDOWN;
+		goto out;
+	}
+
+	wiphy = &drv->wiphy;
+
+	connect.bssid = NULL;
+	connect.channel = NULL;
+	connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+
+	if (info->attrs[NL80211_ATTR_MAC])
+		connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+	connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+	connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+
+	if (info->attrs[NL80211_ATTR_IE]) {
+		connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+		connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+	}
+
+	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+		connect.channel =
+			ieee80211_get_channel(wiphy,
+			    nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+		if (!connect.channel ||
+		    connect.channel->flags & IEEE80211_CHAN_DISABLED) {
+			err = -EINVAL;
+			goto out;
+		}
+	}
+
+	if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
+		connect.auth_type =
+			nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+		if (!nl80211_valid_auth_type(connect.auth_type)) {
+			err = -EINVAL;
+			goto out;
+		}
+	}
+
+	connect.control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
+	connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
+
+	err = cfg80211_connect(drv, dev, &connect);
+
+out:
+	cfg80211_put_dev(drv);
+	dev_put(dev);
+unlock_rtnl:
+	rtnl_unlock();
+	return err;
+}
+
+static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_device *drv;
+	struct net_device *dev;
+	int err;
+	u16 reason;
+
+	if (!info->attrs[NL80211_ATTR_REASON_CODE])
+		reason = WLAN_REASON_DEAUTH_LEAVING;
+	else
+		reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+
+	if (reason == 0)
+		return -EINVAL;
+
+	rtnl_lock();
+
+	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+	if (err)
+		goto unlock_rtnl;
+
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	if (!netif_running(dev)) {
+		err = -ENETDOWN;
+		goto out;
+	}
+
+	err = cfg80211_disconnect(drv, dev, reason);
+
+out:
+	cfg80211_put_dev(drv);
+	dev_put(dev);
+unlock_rtnl:
+	rtnl_unlock();
+	return err;
+}
+
 static struct genl_ops nl80211_ops[] = {
 	{
 		.cmd = NL80211_CMD_GET_WIPHY,
@@ -3628,6 +3763,18 @@ static struct genl_ops nl80211_ops[] = {
 		.policy = nl80211_policy,
 		.flags = GENL_ADMIN_PERM,
 	},
+	{
+		.cmd = NL80211_CMD_CONNECT,
+		.doit = nl80211_connect,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_DISCONNECT,
+		.doit = nl80211_disconnect,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
 };
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
 	.name = "mlme",
@@ -3946,6 +4093,123 @@ void nl80211_send_assoc_timeout(struct c
 				  addr, gfp);
 }
 
+void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
+				 struct net_device *netdev, const u8 *bssid,
+				 const u8 *ie, size_t ie_len, u16 status,
+				 gfp_t gfp)
+{
+	struct sk_buff *msg;
+	void *hdr;
+
+	msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+	if (!msg)
+		return;
+
+	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
+	if (!hdr) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+	if (bssid)
+		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+	NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
+	if (ie)
+		NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
+
+	if (genlmsg_end(msg, hdr) < 0) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+	return;
+
+ nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	nlmsg_free(msg);
+
+}
+
+void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
+			 struct net_device *netdev, const u8 *bssid,
+			 const u8 *ie, size_t ie_len, gfp_t gfp)
+{
+	struct sk_buff *msg;
+	void *hdr;
+
+	msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+	if (!msg)
+		return;
+
+	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
+	if (!hdr) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+	if (ie)
+		NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
+
+	if (genlmsg_end(msg, hdr) < 0) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+	return;
+
+ nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	nlmsg_free(msg);
+
+}
+
+void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
+			       struct net_device *netdev, u16 reason,
+			       u8 *ie, size_t ie_len, bool from_ap, gfp_t gfp)
+{
+	struct sk_buff *msg;
+	void *hdr;
+
+	msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+	if (!msg)
+		return;
+
+	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
+	if (!hdr) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+	if (from_ap && reason)
+		NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
+	if (from_ap)
+		NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
+	if (ie)
+		NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
+
+	if (genlmsg_end(msg, hdr) < 0) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+	return;
+
+ nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	nlmsg_free(msg);
+
+}
+
 void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
 			     struct net_device *netdev, const u8 *bssid,
 			     gfp_t gfp)
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/wireless/sme.c	2009-06-24 13:54:05.000000000 +0200
@@ -0,0 +1,206 @@
+/*
+ * SME code for cfg80211's connect emulation.
+ *
+ * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (C) 2009   Intel Corporation. All rights reserved.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/workqueue.h>
+#include <net/cfg80211.h>
+#include <net/rtnetlink.h>
+#include "nl80211.h"
+
+
+void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
+			     u8 *ie, size_t ie_len, u16 status, gfp_t gfp)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_bss *bss;
+#ifdef CONFIG_WIRELESS_EXT
+	union iwreq_data wrqu;
+#endif
+
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+		return;
+
+	if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING))
+		return;
+
+	if (wdev->current_bss) {
+		cfg80211_unhold_bss(wdev->current_bss);
+		cfg80211_put_bss(wdev->current_bss);
+		wdev->current_bss = NULL;
+	}
+
+	if (status == WLAN_STATUS_SUCCESS) {
+		bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
+				       wdev->ssid, wdev->ssid_len,
+				       WLAN_CAPABILITY_ESS,
+				       WLAN_CAPABILITY_ESS);
+
+		if (WARN_ON(!bss))
+			return;
+
+		cfg80211_hold_bss(bss);
+		wdev->current_bss = bss;
+
+		wdev->sme_state = CFG80211_SME_CONNECTED;
+	} else {
+		wdev->sme_state = CFG80211_SME_IDLE;
+	}
+
+	nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid,
+				    ie, ie_len, status, gfp);
+
+#ifdef CONFIG_WIRELESS_EXT
+	if (ie && status == WLAN_STATUS_SUCCESS) {
+		memset(&wrqu, 0, sizeof(wrqu));
+		wrqu.data.length = ie_len;
+		wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie);
+	}
+
+	memset(&wrqu, 0, sizeof(wrqu));
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	if (bssid)
+		memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
+	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+#endif
+}
+EXPORT_SYMBOL(cfg80211_connect_result);
+
+void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
+		     u8 *ie, size_t ie_len, gfp_t gfp)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_bss *bss;
+#ifdef CONFIG_WIRELESS_EXT
+	union iwreq_data wrqu;
+#endif
+
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+		return;
+
+	if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
+		return;
+
+	/* internal error -- how did we get to CONNECTED w/o BSS? */
+	if (WARN_ON(!wdev->current_bss)) {
+		return;
+	}
+
+	cfg80211_unhold_bss(wdev->current_bss);
+	cfg80211_put_bss(wdev->current_bss);
+	wdev->current_bss = NULL;
+
+	bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
+			       wdev->ssid, wdev->ssid_len,
+			       WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+
+	if (WARN_ON(!bss))
+		return;
+
+	cfg80211_hold_bss(bss);
+	wdev->current_bss = bss;
+
+	nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid,
+			    ie, ie_len, gfp);
+
+#ifdef CONFIG_WIRELESS_EXT
+	if (ie) {
+		memset(&wrqu, 0, sizeof(wrqu));
+		wrqu.data.length = ie_len;
+		wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie);
+	}
+
+	memset(&wrqu, 0, sizeof(wrqu));
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
+	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+#endif
+}
+EXPORT_SYMBOL(cfg80211_roamed);
+
+static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp,
+				    u8 *ie, size_t ie_len, u16 reason,
+				    bool from_ap)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+#ifdef CONFIG_WIRELESS_EXT
+	union iwreq_data wrqu;
+#endif
+
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+		return;
+
+	if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
+		return;
+
+	if (wdev->current_bss) {
+		cfg80211_unhold_bss(wdev->current_bss);
+		cfg80211_put_bss(wdev->current_bss);
+	}
+
+	wdev->current_bss = NULL;
+	wdev->sme_state = CFG80211_SME_IDLE;
+
+	nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev,
+				  reason, ie, ie_len, from_ap, gfp);
+
+#ifdef CONFIG_WIRELESS_EXT
+	memset(&wrqu, 0, sizeof(wrqu));
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+#endif
+}
+
+void cfg80211_disconnected(struct net_device *dev, u16 reason,
+			   u8 *ie, size_t ie_len, gfp_t gfp)
+{
+	__cfg80211_disconnected(dev, reason, ie, ie_len, true, gfp);
+}
+EXPORT_SYMBOL(cfg80211_disconnected);
+
+int cfg80211_connect(struct cfg80211_registered_device *rdev,
+		     struct net_device *dev,
+		     struct cfg80211_connect_params *connect)
+{
+	int err;
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+	if (wdev->sme_state != CFG80211_SME_IDLE)
+		return -EALREADY;
+
+	if (!rdev->ops->connect) {
+		return -EOPNOTSUPP;
+	} else {
+		err = rdev->ops->connect(&rdev->wiphy, dev, connect);
+		if (err)
+			return err;
+		wdev->sme_state = CFG80211_SME_CONNECTING;
+	}
+
+	memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
+	wdev->ssid_len = connect->ssid_len;
+
+	return 0;
+}
+
+int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
+			struct net_device *dev, u16 reason)
+{
+	int err;
+
+	if (!rdev->ops->disconnect) {
+		return -EOPNOTSUPP;
+	} else {
+		err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
+		if (err)
+			return err;
+	}
+
+	__cfg80211_disconnected(dev, 0, NULL, 0, false, GFP_KERNEL);
+
+	return 0;
+}
--- wireless-testing.orig/net/wireless/core.c	2009-06-24 13:54:04.000000000 +0200
+++ wireless-testing/net/wireless/core.c	2009-06-24 13:54:05.000000000 +0200
@@ -546,6 +546,7 @@ static int cfg80211_netdev_notifier_call
 				"symlink to netdev!\n");
 		}
 		wdev->netdev = dev;
+		wdev->sme_state = CFG80211_SME_IDLE;
 #ifdef CONFIG_WIRELESS_EXT
 		wdev->wext.default_key = -1;
 		wdev->wext.default_mgmt_key = -1;
@@ -553,11 +554,20 @@ static int cfg80211_netdev_notifier_call
 		mutex_unlock(&rdev->devlist_mtx);
 		break;
 	case NETDEV_GOING_DOWN:
-		if (wdev->iftype != NL80211_IFTYPE_ADHOC)
-			break;
 		if (!wdev->ssid_len)
 			break;
-		cfg80211_leave_ibss(rdev, dev, true);
+
+		switch (wdev->iftype) {
+		case NL80211_IFTYPE_ADHOC:
+			cfg80211_leave_ibss(rdev, dev, true);
+			break;
+		case NL80211_IFTYPE_STATION:
+			cfg80211_disconnect(rdev, dev,
+					    WLAN_REASON_DEAUTH_LEAVING);
+			break;
+		default:
+			break;
+		}
 		break;
 	case NETDEV_UP:
 #ifdef CONFIG_WIRELESS_EXT
--- wireless-testing.orig/net/wireless/nl80211.h	2009-06-24 13:54:04.000000000 +0200
+++ wireless-testing/net/wireless/nl80211.h	2009-06-24 13:54:05.000000000 +0200
@@ -31,6 +31,17 @@ void nl80211_send_auth_timeout(struct cf
 void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
 				struct net_device *netdev,
 				const u8 *addr, gfp_t gfp);
+void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
+				 struct net_device *netdev, const u8 *bssid,
+				 const u8 *ie, size_t ie_len, u16 status,
+				 gfp_t gfp);
+void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
+			 struct net_device *netdev, const u8 *bssid,
+			 const u8 *ie, size_t ie_len, gfp_t gfp);
+void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
+			       struct net_device *netdev, u16 reason,
+			       u8 *ie, size_t ie_len, bool from_ap, gfp_t gfp);
+
 void
 nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
 			    struct net_device *netdev, const u8 *addr,

-- 


^ permalink raw reply

* [RFC 00/11] cfg80211 connect API + wireless extension move
From: Johannes Berg @ 2009-06-24 12:07 UTC (permalink / raw)
  To: linux-wireless

This finally introduces nl80211 and cfg80211 connect API,
and makes wireless extensions entirely self-contained in
cfg80211.

YES.

The last four patches still need more work:
 * the combine patches require getting iwm ported
 * the internalise patch requires getting orinoco
   and rndis ported.

Samuel, who also worked a lot on the first three bits of   
this series, is working on getting iwm ported, and then    
we can stick this into the tree except for the last patch, 
and gradually work on the rest. Maybe we even decide to    
not internalise it this way because orinoco wants to keep  
some ioctls that we'll not offer in cfg80211, not sure yet.

Also, this still needs a bit of work for auth algorithms.  
Currently, the series breaks shared key auth. For iwm, the 
device will only try the auth algorithm you ask it to use, 
but mac80211 is able to select the algorithm automatically.
It might make most sense to make cfg80211 try different 
algorithms if userspace didn't specify which one to use.

Anyway, comments welcome!

johannes


^ permalink raw reply

* Re: [PATCH 07/10] decnet: Use rcu_barrier() on module unload.
From: Jesper Dangaard Brouer @ 2009-06-24 12:09 UTC (permalink / raw)
  To: Chrissie Caulfield
  Cc: David S. Miller, Paul E. McKenney, netdev, linux-kernel,
	dougthompson, bluesmoke-devel, axboe, Patrick McHardy,
	Trond.Myklebust, linux-wireless, johannes, yoshfuji, shemminger,
	linux-nfs, bfields, neilb, linux-ext4, tytso, adilger,
	netfilter-devel
In-Reply-To: <1245843884.6695.54.camel@localhost.localdomain>

On Wed, 2009-06-24 at 13:44 +0200, Jesper Dangaard Brouer wrote:
> On Wed, 2009-06-24 at 07:23 +0100, Chrissie Caulfield wrote:
> > The issues with DECnet module unloading are a little more than just an
> > RCU leak I think!
> > 
> > Though that area does need reviewing ... when I get some time.
> 
> Fine.  Now you have read my comment in the code, then there is a updated
> patch below.  Will you ack-that?

Sorry wrong patch... forgot save the code and 'stg refresh'... 

[PATCH 07/10] decnet: Use rcu_barrier() on module unload.

From: Jesper Dangaard Brouer <hawk@comx.dk>

The decnet module unloading as been disabled with a '#if 0' statement,
because it have had issues.

We add a rcu_barrier() anyhow for correctness.

The maintainer (Chrissie Caulfield) will look into the unload issue
when time permits.

Signed-off-by: Jesper Dangaard Brouer <hawk@comx.dk>
---

 net/decnet/af_decnet.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)


diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
index d351b8d..77d4028 100644
--- a/net/decnet/af_decnet.c
+++ b/net/decnet/af_decnet.c
@@ -2413,6 +2413,8 @@ static void __exit decnet_exit(void)
 	proc_net_remove(&init_net, "decnet");
 
 	proto_unregister(&dn_proto);
+
+	rcu_barrier_bh(); /* Wait for completion of call_rcu_bh()'s */
 }
 module_exit(decnet_exit);
 #endif


^ permalink raw reply related

* Linux wireless mini-summit -- Berlin, June 2009
From: Kevin Wilson @ 2009-06-24 11:47 UTC (permalink / raw)
  To: linux-wireless

Hello,
  Facing the upcoming summit, I want to say here that it would be a
great idea if some of the  wireless
summit lectures (or all) will be videoed and put on the web
afterwards. I think this can be a great idea
for the benefit of the Linux wireless community.

Rgs,
Kevin

^ permalink raw reply

* Re: [PATCH 07/10] decnet: Use rcu_barrier() on module unload.
From: Jesper Dangaard Brouer @ 2009-06-24 11:44 UTC (permalink / raw)
  To: Chrissie Caulfield
  Cc: David S. Miller, Paul E. McKenney, netdev, linux-kernel,
	dougthompson, bluesmoke-devel, axboe, Patrick McHardy,
	Trond.Myklebust, linux-wireless, johannes, yoshfuji, shemminger,
	linux-nfs, bfields, neilb, linux-ext4, tytso, adilger,
	netfilter-devel
In-Reply-To: <4A41C67E.8070301@googlemail.com>

On Wed, 2009-06-24 at 07:23 +0100, Chrissie Caulfield wrote:
> The issues with DECnet module unloading are a little more than just an
> RCU leak I think!
> 
> Though that area does need reviewing ... when I get some time.

Fine.  Now you have read my comment in the code, then there is a updated
patch below.  Will you ack-that?

-- 
Med venlig hilsen / Best regards
  Jesper Brouer
  ComX Networks A/S
  Linux Network developer
  Cand. Scient Datalog / MSc.
  Author of http://adsl-optimizer.dk
  LinkedIn: http://www.linkedin.com/in/brouer


[PATCH 07/10] decnet: Use rcu_barrier() on module unload.

From: Jesper Dangaard Brouer <hawk@comx.dk>

The decnet module unloading as been disabled with a '#if 0' statement,
because it have had issues.

We add a rcu_barrier() anyhow for correctness.

The maintainer (Chrissie Caulfield) will look into the unload issue
when time permits.

Signed-off-by: Jesper Dangaard Brouer <hawk@comx.dk>
---

 net/decnet/af_decnet.c |    6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)


diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
index d351b8d..bff12da 100644
--- a/net/decnet/af_decnet.c
+++ b/net/decnet/af_decnet.c
@@ -2393,6 +2393,10 @@ module_init(decnet_init);
  * Prevent DECnet module unloading until its fixed properly.
  * Requires an audit of the code to check for memory leaks and
  * initialisation problems etc.
+ *
+ * hawk@comx.dk 2009-06-19:
+ *  I have added a rcu_barrier() which should plug some of your
+ *  module unload issues.  Maintainers please try it out...
  */
 #if 0
 static void __exit decnet_exit(void)
@@ -2413,6 +2417,8 @@ static void __exit decnet_exit(void)
 	proc_net_remove(&init_net, "decnet");
 
 	proto_unregister(&dn_proto);
+
+	rcu_barrier_bh(); /* Wait for completion of call_rcu_bh()'s */
 }
 module_exit(decnet_exit);
 #endif



^ permalink raw reply related

* Re: [PATCH 03/10] mac80211: Use rcu_barrier() on unload.
From: Johannes Berg @ 2009-06-24 11:39 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: David S. Miller, Paul E. McKenney, netdev, linux-kernel,
	dougthompson, axboe, Patrick McHardy, christine.caulfield,
	Trond.Myklebust, linux-wireless, yoshfuji, shemminger, linux-nfs,
	bfields, neilb, linux-ext4, tytso, adilger, netfilter-devel
In-Reply-To: <1245843137.6695.48.camel@localhost.localdomain>

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

On Wed, 2009-06-24 at 13:32 +0200, Jesper Dangaard Brouer wrote:

> > > Should it then not be in mesh.c ieee80211_stop_mesh().  We can replace
> > > the synchronize_rcu() in this function with a rcu_barrier().
> > 
> > Yes, this seems correct.
> > 
> > johannes
> 
> Can I consider this a:
> 
> Acked-by: Johannes Berg <johannes@sipsolutions.net>

Yeah, 
Acked-by: Johannes Berg <johannes@sipsolutions.net>

johannes

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

^ permalink raw reply

* [PATCH 3/3] net/compat/wext: send different messages to compat tasks
From: Johannes Berg @ 2009-06-24 11:34 UTC (permalink / raw)
  To: netdev; +Cc: linux-wireless
In-Reply-To: <20090624113447.441667549@sipsolutions.net>

Wireless extensions have the unfortunate problem that events
are multicast netlink messages, and are not independent of
pointer size. Thus, currently 32-bit tasks on 64-bit platforms
cannot properly receive events and fail with all kinds of
strange problems, for instance wpa_supplicant never notices
disassociations, due to the way the 64-bit event looks (to a
32-bit process), the fact that the address is all zeroes is
lost, it thinks instead it is 00:00:00:00:01:00.

The same problem existed with the ioctls, until David Miller
fixed those some time ago in an heroic effort.

A different problem caused by this is that we cannot send the
ASSOCREQIE/ASSOCRESPIE events because sending them causes a
32-bit wpa_supplicant on a 64-bit system to overwrite its
internal information, which is worse than it not getting the
information at all -- so we currently resort to sending a
custom string event that it then parses. This, however, has a
severe size limitation we are frequently hitting with modern
access points; this limitation would can be lifted after this
patch by sending the correct binary, not custom, event.

A similar problem apparently happens for some other netlink
users on x86_64 with 32-bit tasks due to the alignment for
64-bit quantities.

In order to fix these problems, I have implemented a way to
send compat messages to tasks. When sending an event, we send
the non-compat event data together with a compat event data in
skb_shinfo(main_skb)->frag_list. Then, when the event is read
from the socket, the netlink code makes sure to pass out only
the skb that is compatible with the task. This approach was
suggested by David Miller, my original approach required
always sending two skbs but that had various small problems.

To determine whether compat is needed or not, I have used the
MSG_CMSG_COMPAT flag, and adjusted the call path for recv and
recvfrom to include it, even if those calls do not have a cmsg
parameter.

I have not solved one small part of the problem, and I don't
think it is necessary to: if a 32-bit application uses read()
rather than any form of recvmsg() it will still get the wrong
(64-bit) event. However, neither do applications actually do
this, nor would it be a regression.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
v2: * after some discussion with davem, use frag_list as noted,
      and note that there are more possible uses for this
    * add another Kconfig symbol for code that can use select
      for the feature
    * add help, in Kconfig, describing the feature

 arch/mips/kernel/scall64-n32.S |    2 -
 arch/mips/kernel/scall64-o32.S |    4 +-
 arch/sparc/kernel/sys32.S      |    2 -
 include/linux/wireless.h       |    8 ++++
 net/Kconfig                    |   20 ++++++++++
 net/compat.c                   |   17 +++++++-
 net/netlink/af_netlink.c       |   21 +++++++++++
 net/wireless/wext.c            |   78 +++++++++++++++++++++++++++++++++++++++++
 8 files changed, 146 insertions(+), 6 deletions(-)

--- wireless-testing.orig/net/wireless/wext.c	2009-06-24 13:31:59.000000000 +0200
+++ wireless-testing/net/wireless/wext.c	2009-06-24 13:32:02.000000000 +0200
@@ -417,6 +417,21 @@ static const int event_type_size[] = {
 	IW_EV_QUAL_LEN,			/* IW_HEADER_TYPE_QUAL */
 };
 
+#ifdef CONFIG_COMPAT
+static const int compat_event_type_size[] = {
+	IW_EV_COMPAT_LCP_LEN,		/* IW_HEADER_TYPE_NULL */
+	0,
+	IW_EV_COMPAT_CHAR_LEN,		/* IW_HEADER_TYPE_CHAR */
+	0,
+	IW_EV_COMPAT_UINT_LEN,		/* IW_HEADER_TYPE_UINT */
+	IW_EV_COMPAT_FREQ_LEN,		/* IW_HEADER_TYPE_FREQ */
+	IW_EV_COMPAT_ADDR_LEN,		/* IW_HEADER_TYPE_ADDR */
+	0,
+	IW_EV_COMPAT_POINT_LEN,		/* Without variable payload */
+	IW_EV_COMPAT_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */
+	IW_EV_COMPAT_QUAL_LEN,		/* IW_HEADER_TYPE_QUAL */
+};
+#endif
 
 /************************ COMMON SUBROUTINES ************************/
 /*
@@ -1341,6 +1356,22 @@ void wireless_send_event(struct net_devi
 	struct sk_buff *skb;
 	struct nlmsghdr *nlh;
 	struct nlattr *nla;
+#ifdef CONFIG_COMPAT
+	struct __compat_iw_event *compat_event;
+	struct compat_iw_point compat_wrqu;
+	struct sk_buff *compskb;
+#endif
+
+	/*
+	 * Nothing in the kernel sends scan events with data, be safe.
+	 * This is necessary because we cannot fix up scan event data
+	 * for compat, due to being contained in 'extra', but normally
+	 * applications are required to retrieve the scan data anyway
+	 * and no data is included in the event, this codifies that
+	 * practice.
+	 */
+	if (WARN_ON(cmd == SIOCGIWSCAN && extra))
+		extra = NULL;
 
 	/* Get the description of the Event */
 	if (cmd <= SIOCIWLAST) {
@@ -1439,7 +1470,54 @@ void wireless_send_event(struct net_devi
 		memcpy(((char *) event) + hdr_len, extra, extra_len);
 
 	nlmsg_end(skb, nlh);
+#ifdef CONFIG_COMPAT
+	hdr_len = compat_event_type_size[descr->header_type];
+	event_len = hdr_len + extra_len;
+
+	compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+	if (!compskb) {
+		kfree_skb(skb);
+		return;
+	}
+
+	/* Send via the RtNetlink event channel */
+	nlh = rtnetlink_ifinfo_prep(dev, compskb);
+	if (WARN_ON(!nlh)) {
+		kfree_skb(skb);
+		kfree_skb(compskb);
+		return;
+	}
+
+	/* Add the wireless events in the netlink packet */
+	nla = nla_reserve(compskb, IFLA_WIRELESS, event_len);
+	if (!nla) {
+		kfree_skb(skb);
+		kfree_skb(compskb);
+		return;
+	}
+	compat_event = nla_data(nla);
+
+	compat_event->len = event_len;
+	compat_event->cmd = cmd;
+	if (descr->header_type == IW_HEADER_TYPE_POINT) {
+		compat_wrqu.length = wrqu->data.length;
+		compat_wrqu.flags = wrqu->data.flags;
+		memcpy(&compat_event->pointer,
+			((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF,
+			hdr_len - IW_EV_COMPAT_LCP_LEN);
+		if (extra_len)
+			memcpy(((char *) compat_event) + hdr_len,
+				extra, extra_len);
+	} else {
+		/* extra_len must be zero, so no if (extra) needed */
+		memcpy(&compat_event->pointer, wrqu,
+			hdr_len - IW_EV_COMPAT_LCP_LEN);
+	}
 
+	nlmsg_end(compskb, nlh);
+
+	skb_shinfo(skb)->frag_list = compskb;
+#endif
 	skb_queue_tail(&dev_net(dev)->wext_nlevents, skb);
 	schedule_work(&wireless_nlevent_work);
 }
--- wireless-testing.orig/net/Kconfig	2009-06-24 13:30:31.000000000 +0200
+++ wireless-testing/net/Kconfig	2009-06-24 13:32:02.000000000 +0200
@@ -23,6 +23,26 @@ menuconfig NET
 
 if NET
 
+config WANT_COMPAT_NETLINK_MESSAGES
+	bool
+	help
+	  This option can be selected by other options that need compat
+	  netlink messages.
+
+config COMPAT_NETLINK_MESSAGES
+	def_bool y
+	depends on COMPAT
+	depends on WIRELESS_EXT || WANT_COMPAT_NETLINK_MESSAGES
+	help
+	  This option makes it possible to send different netlink messages
+	  to tasks depending on whether the task is a compat task or not. To
+	  achieve this, you need to set skb_shinfo(skb)->frag_list to the
+	  compat skb before sending the skb, the netlink code will sort out
+	  which message to actually pass to the task.
+
+	  Newly written code should NEVER need this option but do
+	  compat-independent messages instead!
+
 menu "Networking options"
 
 source "net/packet/Kconfig"
--- wireless-testing.orig/net/netlink/af_netlink.c	2009-06-24 13:30:31.000000000 +0200
+++ wireless-testing/net/netlink/af_netlink.c	2009-06-24 13:32:02.000000000 +0200
@@ -1368,6 +1368,27 @@ static int netlink_recvmsg(struct kiocb 
 	if (skb == NULL)
 		goto out;
 
+#ifdef CONFIG_COMPAT_NETLINK_MESSAGES
+	if (unlikely(skb_shinfo(skb)->frag_list)) {
+		bool need_compat = !!(flags & MSG_CMSG_COMPAT);
+
+		/*
+		 * If this skb has a frag_list, then here that means that
+		 * we will have to use the frag_list skb for compat tasks
+		 * and the regular skb for non-compat tasks.
+		 */
+		if (need_compat) {
+			struct sk_buff *compskb = skb_shinfo(skb)->frag_list;
+			skb_shinfo(skb)->frag_list = NULL;
+			kfree_skb(skb);
+			skb = compskb;
+		} else {
+			kfree_skb(skb_shinfo(skb)->frag_list);
+			skb_shinfo(skb)->frag_list = NULL;
+		}
+	}
+#endif
+
 	msg->msg_namelen = 0;
 
 	copied = skb->len;
--- wireless-testing.orig/net/compat.c	2009-06-24 13:30:31.000000000 +0200
+++ wireless-testing/net/compat.c	2009-06-24 13:32:02.000000000 +0200
@@ -743,6 +743,18 @@ asmlinkage long compat_sys_recvmsg(int f
 	return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
 }
 
+asmlinkage long compat_sys_recv(int fd, void __user *buf, size_t len, unsigned flags)
+{
+	return sys_recv(fd, buf, len, flags | MSG_CMSG_COMPAT);
+}
+
+asmlinkage long compat_sys_recvfrom(int fd, void __user *buf, size_t len,
+				    unsigned flags, struct sockaddr __user *addr,
+				    int __user *addrlen)
+{
+	return sys_recvfrom(fd, buf, len, flags | MSG_CMSG_COMPAT, addr, addrlen);
+}
+
 asmlinkage long compat_sys_socketcall(int call, u32 __user *args)
 {
 	int ret;
@@ -788,10 +800,11 @@ asmlinkage long compat_sys_socketcall(in
 		ret = sys_sendto(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), a[5]);
 		break;
 	case SYS_RECV:
-		ret = sys_recv(a0, compat_ptr(a1), a[2], a[3]);
+		ret = compat_sys_recv(a0, compat_ptr(a1), a[2], a[3]);
 		break;
 	case SYS_RECVFROM:
-		ret = sys_recvfrom(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), compat_ptr(a[5]));
+		ret = compat_sys_recvfrom(a0, compat_ptr(a1), a[2], a[3],
+					  compat_ptr(a[4]), compat_ptr(a[5]));
 		break;
 	case SYS_SHUTDOWN:
 		ret = sys_shutdown(a0,a1);
--- wireless-testing.orig/include/linux/wireless.h	2009-06-24 13:30:31.000000000 +0200
+++ wireless-testing/include/linux/wireless.h	2009-06-24 13:32:02.000000000 +0200
@@ -1132,6 +1132,14 @@ struct __compat_iw_event {
 };
 #define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer)
 #define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length)
+
+/* Size of the various events for compat */
+#define IW_EV_COMPAT_CHAR_LEN	(IW_EV_COMPAT_LCP_LEN + IFNAMSIZ)
+#define IW_EV_COMPAT_UINT_LEN	(IW_EV_COMPAT_LCP_LEN + sizeof(__u32))
+#define IW_EV_COMPAT_FREQ_LEN	(IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_freq))
+#define IW_EV_COMPAT_PARAM_LEN	(IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_param))
+#define IW_EV_COMPAT_ADDR_LEN	(IW_EV_COMPAT_LCP_LEN + sizeof(struct sockaddr))
+#define IW_EV_COMPAT_QUAL_LEN	(IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_quality))
 #define IW_EV_COMPAT_POINT_LEN	\
 	(IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \
 	 IW_EV_COMPAT_POINT_OFF)
--- wireless-testing.orig/arch/sparc/kernel/sys32.S	2009-06-24 13:30:31.000000000 +0200
+++ wireless-testing/arch/sparc/kernel/sys32.S	2009-06-24 13:32:02.000000000 +0200
@@ -121,7 +121,7 @@ SIGN2(sys32_syslog, sys_syslog, %o0, %o2
 SIGN1(sys32_umask, sys_umask, %o0)
 SIGN3(sys32_tgkill, sys_tgkill, %o0, %o1, %o2)
 SIGN1(sys32_sendto, sys_sendto, %o0)
-SIGN1(sys32_recvfrom, sys_recvfrom, %o0)
+SIGN1(sys32_recvfrom, compat_sys_recvfrom, %o0)
 SIGN3(sys32_socket, sys_socket, %o0, %o1, %o2)
 SIGN2(sys32_connect, sys_connect, %o0, %o2)
 SIGN2(sys32_bind, sys_bind, %o0, %o2)
--- wireless-testing.orig/arch/mips/kernel/scall64-n32.S	2009-06-24 13:30:31.000000000 +0200
+++ wireless-testing/arch/mips/kernel/scall64-n32.S	2009-06-24 13:32:02.000000000 +0200
@@ -164,7 +164,7 @@ EXPORT(sysn32_call_table)
 	PTR	sys_connect
 	PTR	sys_accept
 	PTR	sys_sendto
-	PTR	sys_recvfrom
+	PTR	compat_sys_recvfrom
 	PTR	compat_sys_sendmsg		/* 6045 */
 	PTR	compat_sys_recvmsg
 	PTR	sys_shutdown
--- wireless-testing.orig/arch/mips/kernel/scall64-o32.S	2009-06-24 13:30:31.000000000 +0200
+++ wireless-testing/arch/mips/kernel/scall64-o32.S	2009-06-24 13:32:02.000000000 +0200
@@ -378,8 +378,8 @@ sys_call_table:
 	PTR	sys_getsockname
 	PTR	sys_getsockopt
 	PTR	sys_listen
-	PTR	sys_recv			/* 4175 */
-	PTR	sys_recvfrom
+	PTR	compat_sys_recv			/* 4175 */
+	PTR	compat_sys_recvfrom
 	PTR	compat_sys_recvmsg
 	PTR	sys_send
 	PTR	compat_sys_sendmsg

-- 


^ permalink raw reply

* [PATCH 2/3] wext: optimise, comment and fix event sending
From: Johannes Berg @ 2009-06-24 11:34 UTC (permalink / raw)
  To: netdev; +Cc: linux-wireless
In-Reply-To: <20090624113447.441667549@sipsolutions.net>

The current function for sending events first allocates the
event stream buffer, and then an skb to copy the event stream
into. This can be done in one go. Also, the current function
leaks kernel data to userspace in a 4 uninitialised bytes,
initialise those explicitly. Finally also add a few useful
comments, as opposed to the current comments.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/wireless/wext.c |  114 ++++++++++++++++++++++++++--------------------------
 1 file changed, 57 insertions(+), 57 deletions(-)

--- wireless-testing.orig/net/wireless/wext.c	2009-06-24 13:31:15.000000000 +0200
+++ wireless-testing/net/wireless/wext.c	2009-06-24 13:31:17.000000000 +0200
@@ -1293,22 +1293,15 @@ static void wireless_nlevent_process(str
 
 static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process);
 
-/* ---------------------------------------------------------------- */
-/*
- * 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 int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev,
-				 int type, char *event, int event_len)
+static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev,
+					      struct sk_buff *skb)
 {
 	struct ifinfomsg *r;
 	struct nlmsghdr  *nlh;
 
-	nlh = nlmsg_put(skb, 0, 0, type, sizeof(*r), 0);
-	if (nlh == NULL)
-		return -EMSGSIZE;
+	nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0);
+	if (!nlh)
+		return NULL;
 
 	r = nlmsg_data(nlh);
 	r->ifi_family = AF_UNSPEC;
@@ -1319,45 +1312,14 @@ static int rtnetlink_fill_iwinfo(struct 
 	r->ifi_change = 0;	/* Wireless changes don't affect those flags */
 
 	NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name);
-	/* Add the wireless events in the netlink packet */
-	NLA_PUT(skb, IFLA_WIRELESS, event_len, event);
-
-	return nlmsg_end(skb, nlh);
 
-nla_put_failure:
+	return nlh;
+ nla_put_failure:
 	nlmsg_cancel(skb, nlh);
-	return -EMSGSIZE;
+	return NULL;
 }
 
-/* ---------------------------------------------------------------- */
-/*
- * 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 void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len)
-{
-	struct sk_buff *skb;
-	int err;
-
-	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
-	if (!skb)
-		return;
-
-	err = rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, event, event_len);
-	if (err < 0) {
-		WARN_ON(err == -EMSGSIZE);
-		kfree_skb(skb);
-		return;
-	}
-
-	NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
-	skb_queue_tail(&dev_net(dev)->wext_nlevents, skb);
-	schedule_work(&wireless_nlevent_work);
-}
 
-/* ---------------------------------------------------------------- */
 /*
  * Main event dispatcher. Called from other parts and drivers.
  * Send the event on the appropriate channels.
@@ -1376,6 +1338,9 @@ void wireless_send_event(struct net_devi
 	int wrqu_off = 0;			/* Offset in wrqu */
 	/* Don't "optimise" the following variable, it will crash */
 	unsigned	cmd_index;		/* *MUST* be unsigned */
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	struct nlattr *nla;
 
 	/* Get the description of the Event */
 	if (cmd <= SIOCIWLAST) {
@@ -1423,25 +1388,60 @@ void wireless_send_event(struct net_devi
 	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)
+	/*
+	 * The problem for 64/32 bit.
+	 *
+	 * On 64-bit, a regular event is laid out as follows:
+	 *      |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |
+	 *      | event.len | event.cmd |     p a d d i n g     |
+	 *      | wrqu data ... (with the correct size)         |
+	 *
+	 * This padding exists because we manipulate event->u,
+	 * and 'event' is not packed.
+	 *
+	 * An iw_point event is laid out like this instead:
+	 *      |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |
+	 *      | event.len | event.cmd |     p a d d i n g     |
+	 *      | iwpnt.len | iwpnt.flg |     p a d d i n g     |
+	 *      | extra data  ...
+	 *
+	 * The second padding exists because struct iw_point is extended,
+	 * but this depends on the platform...
+	 *
+	 * On 32-bit, all the padding shouldn't be there.
+	 */
+
+	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+	if (!skb)
 		return;
 
-	/* Fill event */
+	/* Send via the RtNetlink event channel */
+	nlh = rtnetlink_ifinfo_prep(dev, skb);
+	if (WARN_ON(!nlh)) {
+		kfree_skb(skb);
+		return;
+	}
+
+	/* Add the wireless events in the netlink packet */
+	nla = nla_reserve(skb, IFLA_WIRELESS, event_len);
+	if (!nla) {
+		kfree_skb(skb);
+		return;
+	}
+	event = nla_data(nla);
+
+	/* Fill event - first clear to avoid data leaking */
+	memset(event, 0, hdr_len);
 	event->len = event_len;
 	event->cmd = cmd;
 	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
-	if (extra)
+	if (extra_len)
 		memcpy(((char *) event) + hdr_len, extra, extra_len);
 
-	/* Send via the RtNetlink event channel */
-	rtmsg_iwinfo(dev, (char *) event, event_len);
-
-	/* Cleanup */
-	kfree(event);
+	nlmsg_end(skb, nlh);
 
-	return;		/* Always success, I guess ;-) */
+	skb_queue_tail(&dev_net(dev)->wext_nlevents, skb);
+	schedule_work(&wireless_nlevent_work);
 }
 EXPORT_SYMBOL(wireless_send_event);
 

-- 


^ permalink raw reply

* [PATCH 1/3] wireless extensions: make netns aware
From: Johannes Berg @ 2009-06-24 11:34 UTC (permalink / raw)
  To: netdev; +Cc: linux-wireless
In-Reply-To: <20090624113447.441667549@sipsolutions.net>

This makes wireless extensions netns aware. The
tasklet sending the events is converted to a work
struct so that we can rtnl_lock() in it.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 include/net/net_namespace.h |    3 ++
 net/wireless/wext.c         |   61 ++++++++++++++++++++------------------------
 2 files changed, 32 insertions(+), 32 deletions(-)

--- wireless-testing.orig/include/net/net_namespace.h	2009-06-22 14:44:06.000000000 +0200
+++ wireless-testing/include/net/net_namespace.h	2009-06-22 14:46:22.000000000 +0200
@@ -78,6 +78,9 @@ struct net {
 #ifdef CONFIG_XFRM
 	struct netns_xfrm	xfrm;
 #endif
+#ifdef CONFIG_WIRELESS_EXT
+	struct sk_buff_head	wext_nlevents;
+#endif
 	struct net_generic	*gen;
 };
 
--- wireless-testing.orig/net/wireless/wext.c	2009-06-22 14:44:06.000000000 +0200
+++ wireless-testing/net/wireless/wext.c	2009-06-22 14:46:22.000000000 +0200
@@ -1250,48 +1250,48 @@ int compat_wext_handle_ioctl(struct net 
 }
 #endif
 
-/************************* EVENT PROCESSING *************************/
-/*
- * Process events generated by the wireless layer or the driver.
- * Most often, the event will be propagated through rtnetlink
- */
+static int __net_init wext_pernet_init(struct net *net)
+{
+	skb_queue_head_init(&net->wext_nlevents);
+	return 0;
+}
 
-/* ---------------------------------------------------------------- */
-/*
- * 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 void __net_exit wext_pernet_exit(struct net *net)
+{
+	skb_queue_purge(&net->wext_nlevents);
+}
 
-static struct sk_buff_head wireless_nlevent_queue;
+static struct pernet_operations wext_pernet_ops = {
+	.init = wext_pernet_init,
+	.exit = wext_pernet_exit,
+};
 
 static int __init wireless_nlevent_init(void)
 {
-	skb_queue_head_init(&wireless_nlevent_queue);
+	return register_pernet_subsys(&wext_pernet_ops);
 	return 0;
 }
 
 subsys_initcall(wireless_nlevent_init);
 
-static void wireless_nlevent_process(unsigned long data)
+/* Process events generated by the wireless layer or the driver. */
+static void wireless_nlevent_process(struct work_struct *work)
 {
 	struct sk_buff *skb;
+	struct net *net;
 
-	while ((skb = skb_dequeue(&wireless_nlevent_queue)))
-		rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
+	rtnl_lock();
+
+	for_each_net(net) {
+		while ((skb = skb_dequeue(&net->wext_nlevents)))
+			rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL,
+				    GFP_KERNEL);
+	}
+
+	rtnl_unlock();
 }
 
-static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
+static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process);
 
 /* ---------------------------------------------------------------- */
 /*
@@ -1341,9 +1341,6 @@ static void rtmsg_iwinfo(struct net_devi
 	struct sk_buff *skb;
 	int err;
 
-	if (!net_eq(dev_net(dev), &init_net))
-		return;
-
 	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
 	if (!skb)
 		return;
@@ -1356,8 +1353,8 @@ static void rtmsg_iwinfo(struct net_devi
 	}
 
 	NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
-	skb_queue_tail(&wireless_nlevent_queue, skb);
-	tasklet_schedule(&wireless_nlevent_tasklet);
+	skb_queue_tail(&dev_net(dev)->wext_nlevents, skb);
+	schedule_work(&wireless_nlevent_work);
 }
 
 /* ---------------------------------------------------------------- */

-- 


^ permalink raw reply

* [PATCH 0/3] wireless extensions improvements
From: Johannes Berg @ 2009-06-24 11:34 UTC (permalink / raw)
  To: netdev; +Cc: linux-wireless

Hi,

This series contains three improvements to wireless
extensions:
 1) make them network namespace aware, as discussed
    previously
 2) optimise the event sending code and fix a small
    heap information leak
 3) do some generic work and make it possible to send
    compat netlink events, and make wext do this

Because the third patch depends on the first two (it
could be made not to, if you wish) but touches generic
networking stuff, I'd like to have all of these included
in net-next instead of John's wireless tree, all the
other wireless work can be done independently of these.

johannes


^ permalink raw reply

* Re: [PATCH 03/10] mac80211: Use rcu_barrier() on unload.
From: Jesper Dangaard Brouer @ 2009-06-24 11:32 UTC (permalink / raw)
  To: Johannes Berg
  Cc: David S. Miller, Paul E. McKenney, netdev, linux-kernel,
	dougthompson, bluesmoke-devel, axboe, Patrick McHardy,
	christine.caulfield, Trond.Myklebust, linux-wireless, yoshfuji,
	shemminger, linux-nfs, bfields, neilb, linux-ext4, tytso, adilger,
	netfilter-devel
In-Reply-To: <1245838862.21314.48.camel@johannes.local>

On Wed, 2009-06-24 at 12:21 +0200, Johannes Berg wrote:
> On Wed, 2009-06-24 at 12:06 +0200, Jesper Dangaard Brouer wrote:
> > On Tue, 2009-06-23 at 17:15 +0200, Johannes Berg wrote:
> > > On Tue, 2009-06-23 at 17:04 +0200, Jesper Dangaard Brouer wrote:
> > > > The mac80211 module uses rcu_call() thus it should use rcu_barrier()
> > > > on module unload.
> > > > 
> > > > I'm having a hardtime verifying that no more call_rcu() callbacks can
> > > > be schedules in the module unload path.  Could a maintainer please
> > > > look into this?
> > > > 
> > > > Signed-off-by: Jesper Dangaard Brouer <hawk@comx.dk>
> > > > ---
> > > > 
> > > >  net/mac80211/main.c |    2 ++
> > > >  1 files changed, 2 insertions(+), 0 deletions(-)
> > > > 
> > > > diff --git a/net/mac80211/main.c b/net/mac80211/main.c
> > > > index 092a017..e9f70ce 100644
> > > > --- a/net/mac80211/main.c
> > > > +++ b/net/mac80211/main.c
> > > > @@ -1100,6 +1100,8 @@ static void __exit ieee80211_exit(void)
> > > >  		ieee80211s_stop();
> > > >  
> > > >  	ieee80211_debugfs_netdev_exit();
> > > > +
> > > > +	rcu_barrier(); /* Wait for completion of call_rcu()'s */
> > > >  }
> > > 
> > > I don't think this is correct at all -- note that call_rcu() is done in
> > > some of the mesh code, so I would think you need to do this in
> > > ieee80211_stop() since the call_rcu() code requires the interface to
> > > still be around. And when it's stopped everything should be idle.
> > 
> > Should it then not be in mesh.c ieee80211_stop_mesh().  We can replace
> > the synchronize_rcu() in this function with a rcu_barrier().
> 
> Yes, this seems correct.
> 
> johannes

Can I consider this a:

Acked-by: Johannes Berg <johannes@sipsolutions.net>

???

DaveM seems to like this as patchwork.ozlabs.org picks up this
automatically...

-- 
Med venlig hilsen / Best regards
  Jesper Brouer
  ComX Networks A/S
  Linux Network developer
  Cand. Scient Datalog / MSc.
  Author of http://adsl-optimizer.dk
  LinkedIn: http://www.linkedin.com/in/brouer


^ permalink raw reply

* Re: [PATCH 03/10] mac80211: Use rcu_barrier() on unload.
From: Johannes Berg @ 2009-06-24 10:21 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: David S. Miller, Paul E. McKenney, netdev, linux-kernel,
	dougthompson, bluesmoke-devel, axboe, Patrick McHardy,
	christine.caulfield, Trond.Myklebust, linux-wireless, yoshfuji,
	shemminger, linux-nfs, bfields, neilb, linux-ext4, tytso, adilger,
	netfilter-devel
In-Reply-To: <1245837965.6695.45.camel@localhost.localdomain>

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

On Wed, 2009-06-24 at 12:06 +0200, Jesper Dangaard Brouer wrote:
> On Tue, 2009-06-23 at 17:15 +0200, Johannes Berg wrote:
> > On Tue, 2009-06-23 at 17:04 +0200, Jesper Dangaard Brouer wrote:
> > > The mac80211 module uses rcu_call() thus it should use rcu_barrier()
> > > on module unload.
> > > 
> > > I'm having a hardtime verifying that no more call_rcu() callbacks can
> > > be schedules in the module unload path.  Could a maintainer please
> > > look into this?
> > > 
> > > Signed-off-by: Jesper Dangaard Brouer <hawk@comx.dk>
> > > ---
> > > 
> > >  net/mac80211/main.c |    2 ++
> > >  1 files changed, 2 insertions(+), 0 deletions(-)
> > > 
> > > diff --git a/net/mac80211/main.c b/net/mac80211/main.c
> > > index 092a017..e9f70ce 100644
> > > --- a/net/mac80211/main.c
> > > +++ b/net/mac80211/main.c
> > > @@ -1100,6 +1100,8 @@ static void __exit ieee80211_exit(void)
> > >  		ieee80211s_stop();
> > >  
> > >  	ieee80211_debugfs_netdev_exit();
> > > +
> > > +	rcu_barrier(); /* Wait for completion of call_rcu()'s */
> > >  }
> > 
> > I don't think this is correct at all -- note that call_rcu() is done in
> > some of the mesh code, so I would think you need to do this in
> > ieee80211_stop() since the call_rcu() code requires the interface to
> > still be around. And when it's stopped everything should be idle.
> 
> Should it then not be in mesh.c ieee80211_stop_mesh().  We can replace
> the synchronize_rcu() in this function with a rcu_barrier().

Yes, this seems correct.

johannes

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

^ permalink raw reply

* Re: Linux wireless mini-summit -- Berlin, June 2009
From: Nick Kossifidis @ 2009-06-24 10:19 UTC (permalink / raw)
  To: John W. Linville; +Cc: linux-wireless, netdev, linux-kernel
In-Reply-To: <40f31dec0906030535k3af1f6e7u68c82a4f984e7029@mail.gmail.com>

2009/6/3 Nick Kossifidis <mickflemm@gmail.com>:
>
>
> Can we book a room on Holiday in
> (http://fedoraproject.org/wiki/FUDCon:Berlin_2009_lodging) or is it
> only for Fedora people ? What's the process ? I've managed to find
> some funding for travel expenses but most hotels are really expensive
> (i plan to come on 24 and leave on 29).
>

Just arrived, looking forward to see you all ;-)


-- 
GPG ID: 0xD21DB2DB
As you read this post global entropy rises. Have Fun ;-)
Nick

^ permalink raw reply

* Re: [PATCH 03/10] mac80211: Use rcu_barrier() on unload.
From: Jesper Dangaard Brouer @ 2009-06-24 10:06 UTC (permalink / raw)
  To: Johannes Berg
  Cc: David S. Miller, Paul E. McKenney, netdev, linux-kernel,
	dougthompson, bluesmoke-devel, axboe, Patrick McHardy,
	christine.caulfield, Trond.Myklebust, linux-wireless, yoshfuji,
	shemminger, linux-nfs, bfields, neilb, linux-ext4, tytso, adilger,
	netfilter-devel
In-Reply-To: <1245770155.21314.38.camel@johannes.local>

On Tue, 2009-06-23 at 17:15 +0200, Johannes Berg wrote:
> On Tue, 2009-06-23 at 17:04 +0200, Jesper Dangaard Brouer wrote:
> > The mac80211 module uses rcu_call() thus it should use rcu_barrier()
> > on module unload.
> > 
> > I'm having a hardtime verifying that no more call_rcu() callbacks can
> > be schedules in the module unload path.  Could a maintainer please
> > look into this?
> > 
> > Signed-off-by: Jesper Dangaard Brouer <hawk@comx.dk>
> > ---
> > 
> >  net/mac80211/main.c |    2 ++
> >  1 files changed, 2 insertions(+), 0 deletions(-)
> > 
> > diff --git a/net/mac80211/main.c b/net/mac80211/main.c
> > index 092a017..e9f70ce 100644
> > --- a/net/mac80211/main.c
> > +++ b/net/mac80211/main.c
> > @@ -1100,6 +1100,8 @@ static void __exit ieee80211_exit(void)
> >  		ieee80211s_stop();
> >  
> >  	ieee80211_debugfs_netdev_exit();
> > +
> > +	rcu_barrier(); /* Wait for completion of call_rcu()'s */
> >  }
> 
> I don't think this is correct at all -- note that call_rcu() is done in
> some of the mesh code, so I would think you need to do this in
> ieee80211_stop() since the call_rcu() code requires the interface to
> still be around. And when it's stopped everything should be idle.

Should it then not be in mesh.c ieee80211_stop_mesh().  We can replace
the synchronize_rcu() in this function with a rcu_barrier().

> I can fix it later, but I'm deep in some other stuff right now.

Yes, I noticed you seem quite active :-)
I can also do a repost... what about the patch below?

-- 
Med venlig hilsen / Best regards
  Jesper Brouer
  ComX Networks A/S
  Linux Network developer
  Cand. Scient Datalog / MSc.
  Author of http://adsl-optimizer.dk
  LinkedIn: http://www.linkedin.com/in/brouer


[PATCH v2 03/10] mac80211: Use rcu_barrier() on unload.

From: Jesper Dangaard Brouer <hawk@comx.dk>

The mac80211 module uses rcu_call() thus it should use rcu_barrier()
on module unload.

The rcu_barrier() is placed in mech.c ieee80211_stop_mesh() which is
invoked from ieee80211_stop() in case vif.type == NL80211_IFTYPE_MESH_POINT.

Signed-off-by: Jesper Dangaard Brouer <hawk@comx.dk>
---

 net/mac80211/mesh.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)


diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index fc712e6..11cf45b 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -494,7 +494,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
 	 * should it be using the interface and enqueuing
 	 * frames at this very time on another CPU.
 	 */
-	synchronize_rcu();
+	rcu_barrier(); /* Wait for RX path and call_rcu()'s */
 	skb_queue_purge(&sdata->u.mesh.skb_queue);
 }
 



^ permalink raw reply related

* [PATCH v2 10/10] nf_conntrack: Use rcu_barrier() and fix kmem_cache_create flags
From: Jesper Dangaard Brouer @ 2009-06-24  9:40 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: David S. Miller, Paul E. McKenney, netdev, linux-kernel,
	dougthompson, bluesmoke-devel, axboe, christine.caulfield,
	Trond.Myklebust, linux-wireless, johannes, yoshfuji, shemminger,
	linux-nfs, bfields, neilb, linux-ext4, tytso, adilger,
	netfilter-devel
In-Reply-To: <1245834139.6695.31.camel@localhost.localdomain>


Adjusting SLAB_DESTROY_BY_RCU flags.

 kmem_cache_create("nf_conntrack", ...) does not need the
 SLAB_DESTROY_BY_RCU flag.  But the
 kmem_cache_create("nf_conntrack_expect", ...) should use the
 SLAB_DESTROY_BY_RCU flag, because it uses a call_rcu() callback to
 invoke kmem_cache_free().

RCU barriers, rcu_barrier(), is inserted two places.

 In nf_conntrack_expect.c nf_conntrack_expect_fini() before the
 kmem_cache_destroy(), even though the use of the SLAB_DESTROY_BY_RCU
 flag, because slub does not (currently) handle rcu sync correctly.

 And in nf_conntrack_extend.c nf_ct_extend_unregister(), inorder to
 wait for completion of callbacks to __nf_ct_ext_free_rcu(), which is
 invoked by __nf_ct_ext_add().  It might be more efficient to call
 rcu_barrier() in nf_conntrack_core.c nf_conntrack_cleanup_net(), but
 thats make it more difficult to read the code (as the callback code
 in located in nf_conntrack_extend.c).

Signed-off-by: Jesper Dangaard Brouer <hawk@comx.dk>
---

 net/netfilter/nf_conntrack_core.c   |    2 +-
 net/netfilter/nf_conntrack_expect.c |   11 +++++++++--
 net/netfilter/nf_conntrack_extend.c |    2 +-
 3 files changed, 11 insertions(+), 4 deletions(-)


diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 5f72b94..438ce84 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -1242,7 +1242,7 @@ static int nf_conntrack_init_init_net(void)
 
 	nf_conntrack_cachep = kmem_cache_create("nf_conntrack",
 						sizeof(struct nf_conn),
-						0, SLAB_DESTROY_BY_RCU, NULL);
+						0, 0, NULL);
 	if (!nf_conntrack_cachep) {
 		printk(KERN_ERR "Unable to create nf_conn slab cache\n");
 		ret = -ENOMEM;
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index afde8f9..56227c2 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -593,7 +593,7 @@ int nf_conntrack_expect_init(struct net *net)
 	if (net_eq(net, &init_net)) {
 		nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
 					sizeof(struct nf_conntrack_expect),
-					0, 0, NULL);
+					0, SLAB_DESTROY_BY_RCU, NULL);
 		if (!nf_ct_expect_cachep)
 			goto err2;
 	}
@@ -617,8 +617,15 @@ err1:
 void nf_conntrack_expect_fini(struct net *net)
 {
 	exp_proc_remove(net);
-	if (net_eq(net, &init_net))
+	if (net_eq(net, &init_net)) {
+		/* hawk@comx.dk 2009-06-24: The rcu_barrier() can be
+		 * removed once the sl*b allocators has been fixed
+		 * regarding handling the SLAB_DESTROY_BY_RCU flag
+		 * correctly.
+		 */
+		rcu_barrier(); /* Wait for call_rcu() before destroy */
 		kmem_cache_destroy(nf_ct_expect_cachep);
+	}
 	nf_ct_free_hashtable(net->ct.expect_hash, net->ct.expect_vmalloc,
 			     nf_ct_expect_hsize);
 }
diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c
index 4b2c769..fef95be 100644
--- a/net/netfilter/nf_conntrack_extend.c
+++ b/net/netfilter/nf_conntrack_extend.c
@@ -186,6 +186,6 @@ void nf_ct_extend_unregister(struct nf_ct_ext_type *type)
 	rcu_assign_pointer(nf_ct_ext_types[type->id], NULL);
 	update_alloc_size(type);
 	mutex_unlock(&nf_ct_ext_type_mutex);
-	synchronize_rcu();
+	rcu_barrier(); /* Wait for completion of call_rcu()'s */
 }
 EXPORT_SYMBOL_GPL(nf_ct_extend_unregister);


^ permalink raw reply related

* Re: [PATCH v3] mac80211: fix todo lock
From: Gabor Juhos @ 2009-06-24  9:35 UTC (permalink / raw)
  To: Johannes Berg; +Cc: Bob Copeland, John Linville, linux-wireless
In-Reply-To: <1245783408.21314.43.camel@johannes.local>

Johannes Berg írta:
> Subject: mac80211: fix todo lock
> 
> The key todo lock can be taken from different locks
> that require it to be _bh to avoid lock inversion
> due to (soft)irqs.
> 
> This should fix the two problems reported by Bob and
> Gabor:
> http://mid.gmane.org/20090619113049.GB18956@hash.localnet
> http://mid.gmane.org/4A3FA376.8020307@openwrt.org
> 
> Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
> Cc: Bob Copeland <me@bobcopeland.com>
> Cc: Gabor Juhos <juhosg@openwrt.org>
> ---
>  net/mac80211/key.c |   28 +++++++++++++++-------------
>  1 file changed, 15 insertions(+), 13 deletions(-)

Yeah, this works. Thanks!

-Gabor



^ permalink raw reply

* Re: [PATCH 10/10] nf_conntrack: Use rcu_barrier().
From: Jesper Dangaard Brouer @ 2009-06-24  9:02 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: David S. Miller, Paul E. McKenney, netdev, linux-kernel,
	dougthompson, bluesmoke-devel, axboe, christine.caulfield,
	Trond.Myklebust, linux-wireless, johannes, yoshfuji, shemminger,
	linux-nfs, bfields, neilb, linux-ext4, tytso, adilger,
	netfilter-devel
In-Reply-To: <4A410185.3090706@trash.net>

On Tue, 2009-06-23 at 18:23 +0200, Patrick McHardy wrote:
> Jesper Dangaard Brouer wrote:
> > I'm not sure which is are most optimal place to call rcu_barrier().
> > The patch probably calls rcu_barrier() too much, but its a better
> > safe than sorry approach.
> > 
> > There is embedded some comments that I would like Patrick McHardy
> > to look at.
> > 
> > diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
> > index 5f72b94..cea4537 100644
> > --- a/net/netfilter/nf_conntrack_core.c
> > +++ b/net/netfilter/nf_conntrack_core.c
> > @@ -1084,6 +1084,8 @@ static void nf_conntrack_cleanup_init_net(void)
> >  {
> >  	nf_conntrack_helper_fini();
> >  	nf_conntrack_proto_fini();
> > +	rcu_barrier();
> > +	/* Need to wait for call_rcu() before dealloc the kmem_cache */
> >  	kmem_cache_destroy(nf_conntrack_cachep);
> 
> Which call_rcu() is this referring to? 

It is the call_rcu() in nf_conntrack_expect.c (which is linked into
nf_conntrack.ko).  But that also means that it should have been the slab
cache called "nf_ct_expect_cachep" we should have waited for... (and I
also notice that "nf_ct_expect_cachep" is missing the
SLAB_DESTROY_BY_RCU flag, and the SLAB_DESTROY_BY_RCU flag should be
removed from "nf_conntrack_cachep")

> If its the conntrack destruction,
> that one is gone in the current kernel and I think destruction is
> handled properly by the sl*b-allocators (SLAB_DESTROY_BY_RCU).

Just dived into the slab.c code and noticed that it also is flawed,
ARGH!.  When the SLAB_DESTROY_BY_RCU flags is set, it only calls
synchronize_rcu() and not rcu_barrier() as it should!

I'll fix that up in another patch series... 

Looking into slub and slob at the moment, and it seems that they
schedule another call_rcu callback for freeing when the
SLAB_DESTROY_BY_RCU flags is set.  That seems racy to me... Paul?


> > @@ -1118,6 +1120,9 @@ void nf_conntrack_cleanup(struct net *net)
> >  	/* This makes sure all current packets have passed through
> >  	   netfilter framework.  Roll on, two-stage module
> >  	   delete... */
> > +	/* hawk@comx.dk 2009-06-20: Think this should be replaced by a
> > +          rcu_barrier() ???
> > +	*/
> >  	synchronize_net();
> 
> AFAICT this one is used to make sure the old value of the ip_ct_attach
> hook is not visible anymore before beginning cleanup and is not needed
> for anything else.

Fine!

> >  	nf_conntrack_cleanup_net(net);
> > diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
> > index 1935153..29c6cd0 100644
> > --- a/net/netfilter/nf_conntrack_standalone.c
> > +++ b/net/netfilter/nf_conntrack_standalone.c
> > @@ -500,6 +500,8 @@ static void nf_conntrack_net_exit(struct net *net)
> >  	nf_conntrack_standalone_fini_sysctl(net);
> >  	nf_conntrack_standalone_fini_proc(net);
> >  	nf_conntrack_cleanup(net);
> > +	/* hawk@comx.dk: Think rcu_barrier() should to be called earlier? */
> > +	rcu_barrier(); /* Wait for completion of call_rcu()'s */
> >  }
> 
> Which call_rcu() is this referring to? We should place them in
> the cleanup sub-functions to make this clearly visible.

This call_rcu() is the one done in nf_conntrack_extend.c:114  (notice
"_extend" NOT "_expect"), which calls __nf_ct_ext_free_rcu().

Guess this rcu_barrier() should then be move to
nf_ct_extend_unregister() right? (it already invokes a
synchronize_rcu() that should be replaced by rcu_barrier()).
Although this means the nf_ct_extend_unregister() will be called three
times in nf_conntrack_cleanup_net() when unregistering ecache, acct and
expect.


Thank you for your feedback :-) ... I'll post a new v2 patch...
-- 
Med venlig hilsen / Best regards
  Jesper Brouer
  ComX Networks A/S
  Linux Network developer
  Cand. Scient Datalog / MSc.
  Author of http://adsl-optimizer.dk
  LinkedIn: http://www.linkedin.com/in/brouer


^ permalink raw reply

* Re: [RFC/HACK] net/compat/wext: allow sending different messages to compat tasks
From: Johannes Berg @ 2009-06-24  8:48 UTC (permalink / raw)
  To: linux-wireless; +Cc: netdev, Arnd Bergmann
In-Reply-To: <1245589835.5003.2.camel@johannes.local>

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

On Sun, 2009-06-21 at 15:10 +0200, Johannes Berg wrote:
> Wireless extensions have the unfortunate problem that events
> are multicast, and are not independent of pointer size. Thus,
> currently 32-bit tasks on 64-bit platforms cannot properly
> receive events and fail with all kinds of strange problems,
> for instance wpa_supplicant never notices disassociations, due
> to the way the 64-bit event looks to a 32-bit process the fact
> that the address is all zeroes is lost, it thinks instead it
> is 00:01:00:00:00:00 (actually I'm not entirely sure about the
> position of the 1 but it's like that).

No further comments on this? If I fix sparc and mips, can I get it
merged?

johannes

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

^ permalink raw reply

* Re: [PATCH 00/10] We must use rcu_barrier() on module unload
From: David Miller @ 2009-06-24  7:02 UTC (permalink / raw)
  To: hawk
  Cc: paulmck, netdev, linux-kernel, dougthompson, bluesmoke-devel,
	axboe, kaber, christine.caulfield, Trond.Myklebust,
	linux-wireless, johannes, yoshfuji, shemminger, linux-nfs,
	bfields, neilb, linux-ext4, tytso, adilger, netfilter-devel
In-Reply-To: <20090623150330.22490.87327.stgit@localhost>

From: Jesper Dangaard Brouer <hawk@comx.dk>
Date: Tue, 23 Jun 2009 17:03:53 +0200

> This patch series is an attempt to cleanup the entire tree, for
> potential oops'es during module unload, due to outstanding RCU
> callbacks. (My last rcu_barrier patch series only addressed net/).

This series has net/ stuff too :-)

I'll let the networking cases sit for a while and get review before
I apply them.

^ permalink raw reply

* Re: [PATCH 09/10] cfq-iosched: Uses its own open-coded rcu_barrier.
From: Jens Axboe @ 2009-06-24  6:42 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: David S. Miller, Paul E. McKenney, netdev, linux-kernel,
	dougthompson, bluesmoke-devel, Patrick McHardy,
	christine.caulfield, Trond.Myklebust, linux-wireless, johannes,
	yoshfuji, shemminger, linux-nfs, bfields, neilb, linux-ext4,
	tytso, adilger, netfilter-devel
In-Reply-To: <20090623150439.22490.14657.stgit@localhost>

On Tue, Jun 23 2009, Jesper Dangaard Brouer wrote:
> This module cfq-iosched, has discovered the value of waiting for
> call_rcu() completion, but its has its own open-coded implementation
> of rcu_barrier(), which I don't think is 'strong' enough.
> 
> This patch only leaves a comment for the maintainers to consider.

We need a stronger primitive and rcu_barrier(), since we also need to
wait for the rcu calls to even be scheduled. So I don't think the below
can be improved, it's already fine.

> 
> Signed-off-by: Jesper Dangaard Brouer <hawk@comx.dk>
> ---
> 
>  block/cfq-iosched.c |    6 ++++++
>  1 files changed, 6 insertions(+), 0 deletions(-)
> 
> diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
> index 833ec18..c15555b 100644
> --- a/block/cfq-iosched.c
> +++ b/block/cfq-iosched.c
> @@ -2657,6 +2657,12 @@ static void __exit cfq_exit(void)
>  	/*
>  	 * this also protects us from entering cfq_slab_kill() with
>  	 * pending RCU callbacks
> +	 *
> +	 * hawk@comx.dk 2009-06-18: Maintainer please consider using
> +	 *  rcu_barrier() instead of this open-coded wait for
> +	 *  completion implementation.  I think it provides a better
> +	 *  guarantee that all CPUs are finished, although
> +	 *  elv_ioc_count_read() do consider all CPUs.
>  	 */
>  	if (elv_ioc_count_read(ioc_count))
>  		wait_for_completion(&all_gone);
> 

-- 
Jens Axboe


^ permalink raw reply

* Re: [PATCH 07/10] decnet: Use rcu_barrier() on module unload.
From: Chrissie Caulfield @ 2009-06-24  6:23 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: David S. Miller, Paul E. McKenney, netdev, linux-kernel,
	dougthompson, bluesmoke-devel, axboe, Patrick McHardy,
	Trond.Myklebust, linux-wireless, johannes, yoshfuji, shemminger,
	linux-nfs, bfields, neilb, linux-ext4, tytso, adilger,
	netfilter-devel
In-Reply-To: <20090623150429.22490.15113.stgit@localhost>

Jesper Dangaard Brouer wrote:
> The decnet module unloading as been disabled with a '#if 0' statement,
> because it have had issues.  Perhaps using rcu_barrier() will fix
> these issues?
> 
> Any maintainers with input?
> 
> Signed-off-by: Jesper Dangaard Brouer <hawk@comx.dk>
> ---
> 
>  net/decnet/af_decnet.c |    6 ++++++
>  1 files changed, 6 insertions(+), 0 deletions(-)
> 
> diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
> index d351b8d..bff12da 100644
> --- a/net/decnet/af_decnet.c
> +++ b/net/decnet/af_decnet.c
> @@ -2393,6 +2393,10 @@ module_init(decnet_init);
>   * Prevent DECnet module unloading until its fixed properly.
>   * Requires an audit of the code to check for memory leaks and
>   * initialisation problems etc.
> + *
> + * hawk@comx.dk 2009-06-19:
> + *  I have added a rcu_barrier() which should plug some of your
> + *  module unload issues.  Maintainers please try it out...
>   */
>  #if 0
>  static void __exit decnet_exit(void)
> @@ -2413,6 +2417,8 @@ static void __exit decnet_exit(void)
>  	proc_net_remove(&init_net, "decnet");
>  
>  	proto_unregister(&dn_proto);
> +
> +	rcu_barrier_bh(); /* Wait for completion of call_rcu_bh()'s */
>  }
>  module_exit(decnet_exit);
>  #endif
> 

The issues with DECnet module unloading are a little more than just an
RCU leak I think!

Though that area does need reviewing ... when I get some time.

-- 

Chrissie

^ permalink raw reply

* Re: [PATCH 00/10] We must use rcu_barrier() on module unload
From: Paul E. McKenney @ 2009-06-24  1:44 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: David S. Miller, netdev, linux-kernel, dougthompson,
	bluesmoke-devel, axboe, Patrick McHardy, christine.caulfield,
	Trond.Myklebust, linux-wireless, johannes, yoshfuji, shemminger,
	linux-nfs, bfields, neilb, linux-ext4, tytso, adilger,
	netfilter-devel
In-Reply-To: <20090623150330.22490.87327.stgit@localhost>

On Tue, Jun 23, 2009 at 05:03:53PM +0200, Jesper Dangaard Brouer wrote:
> This patch series is an attempt to cleanup the entire tree, for
> potential oops'es during module unload, due to outstanding RCU
> callbacks. (My last rcu_barrier patch series only addressed net/).
> 
> If an unloadable module uses RCU callbacks, it need to use
> rcu_barrier() so that the module may be safely unloaded.
> 
> For documentation see:
> 
>  Paul E. McKenney's Blog
>  http://paulmck.livejournal.com/7314.html
> 
>  http://lwn.net/Articles/217484/
> 
>  Documentation/RCU/rcubarrier.txt
> 
> 
> Looking through the Linux kernel for call_rcu() users and unloadable
> modules I found 10 modules that didn't behave correctly.

These look good from an RCU viewpoint, but I am in no better position
than is Jesper to analyze the individual modules.  From an RCU
viewpoint:

Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>

> Please: MAINTAINERS needs to verify that the module exit code prevent
> any new RCU callbacks from being posted (before rcu_barrier() is
> called). (I have tried to do this verification, but most of these
> module are simply too large and complex for me to verify this within
> reasonable time)
> 
> [Overview description, following patch ordering]
> 
> The modules ext4, bridge, mac80211, sunrpc, nfs and ipv6 are fairly
> straight forward (maintainers still needs to check for prevent of new
> RCU callbacks).
> 
> The module decnet, has disabled its module_exit() (since ^1da177e) but
> it still seems relevant to keep the code updated.
> 
> The modules edac_core and cfq-iosched, has implemented their own
> open-coded wait_for_completion() scheme, in order to wait for
> call_rcu() calls.  Maintainers needs to look into removing this code
> and using rcu_barrier() instead.
> 
> The module nf_conntrack, has embedded some comments that I would like
> Patrick McHardy to look at.  As I'm not sure which is are most optimal
> place to call rcu_barrier().  The patch probably calls rcu_barrier()
> too much, but its a better safe than sorry approach.
> 
> 
> I have made a patch for each individual module, so objections can be
> made on a per module basis.  I have Cc'ed all of the patches to the
> maintainers of each module (according to the MAINTAINERS file).
> 
> 
> The patchset is made on top of Linus Torvalds tree (starting on top of
> commit f234012f52a3).
> 
> Who wants to pickup these patches? (I usually go through DaveM, but
> this also touches subsystems that are not (yet?) under DaveM's
> maintainer ship)
> 
> 
> ---
> Jesper Dangaard Brouer (10):
>       nf_conntrack: Use rcu_barrier().
>       cfq-iosched: Uses its own open-coded rcu_barrier.
>       edac_core: Uses call_rcu() and its own wait_for_completion scheme.
>       decnet: Use rcu_barrier() on module unload.
>       ipv6: Use rcu_barrier() on module unload.
>       nfs: Use rcu_barrier() on module unload.
>       sunrpc: Use rcu_barrier() on unload.
>       mac80211: Use rcu_barrier() on unload.
>       bridge: Use rcu_barrier() instead of syncronize_net() on unload.
>       ext4: Use rcu_barrier() on module unload.
> 
> 
>  block/cfq-iosched.c                     |    6 ++++++
>  drivers/edac/edac_device.c              |    5 +++++
>  drivers/edac/edac_mc.c                  |    5 +++++
>  drivers/edac/edac_pci.c                 |    5 +++++
>  fs/ext4/mballoc.c                       |    4 +++-
>  fs/nfs/inode.c                          |    1 +
>  net/bridge/br.c                         |    2 +-
>  net/decnet/af_decnet.c                  |    6 ++++++
>  net/ipv6/af_inet6.c                     |    2 ++
>  net/mac80211/main.c                     |    2 ++
>  net/netfilter/nf_conntrack_core.c       |    5 +++++
>  net/netfilter/nf_conntrack_standalone.c |    2 ++
>  net/sunrpc/sunrpc_syms.c                |    1 +
>  13 files changed, 44 insertions(+), 2 deletions(-)
> 
> 
> --
> Best regards,
>   Jesper Brouer
>   ComX Networks A/S
>   Linux Network developer
>   Cand. Scient Datalog / MSc.
>   Author of http://adsl-optimizer.dk
>   LinkedIn: http://www.linkedin.com/in/brouer
> 

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox