diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index d74cc77fa57a9f49113255b479bc7c8a49532e41..3ecaa9179977c4576dfad88ccb5646056b7b2a9b 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -5,7 +5,8 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o
 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 sme.o
+cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
+cfg80211-y += mlme.o ibss.o sme.o chan.o
 cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
 cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o wext-sme.o
 
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
new file mode 100644
index 0000000000000000000000000000000000000000..bc00c9a06b3d4ded2e1110e9140da09930c2c755
--- /dev/null
+++ b/net/wireless/chan.c
@@ -0,0 +1,88 @@
+/*
+ * This file contains helper code to handle channel
+ * settings and keeping track of what is possible at
+ * any point in time.
+ *
+ * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include <net/cfg80211.h>
+#include "core.h"
+
+struct ieee80211_channel *
+rdev_fixed_channel(struct cfg80211_registered_device *rdev,
+		   struct wireless_dev *for_wdev)
+{
+	struct wireless_dev *wdev;
+	struct ieee80211_channel *result = NULL;
+
+	WARN_ON(!mutex_is_locked(&rdev->devlist_mtx));
+
+	list_for_each_entry(wdev, &rdev->netdev_list, list) {
+		if (wdev == for_wdev)
+			continue;
+
+		/*
+		 * Lock manually to tell lockdep about allowed
+		 * nesting here if for_wdev->mtx is held already.
+		 * This is ok as it's all under the rdev devlist
+		 * mutex and as such can only be done once at any
+		 * given time.
+		 */
+		mutex_lock_nested(&wdev->mtx, SINGLE_DEPTH_NESTING);
+		if (wdev->current_bss)
+			result = wdev->current_bss->pub.channel;
+		wdev_unlock(wdev);
+
+		if (result)
+			break;
+	}
+
+	return result;
+}
+
+int rdev_set_freq(struct cfg80211_registered_device *rdev,
+		  int freq, enum nl80211_channel_type channel_type)
+{
+	struct ieee80211_channel *chan;
+	struct ieee80211_sta_ht_cap *ht_cap;
+	int result;
+
+	if (rdev_fixed_channel(rdev, NULL))
+		return -EBUSY;
+
+	if (!rdev->ops->set_channel)
+		return -EOPNOTSUPP;
+
+	chan = ieee80211_get_channel(&rdev->wiphy, freq);
+
+	/* Primary channel not allowed */
+	if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
+		return -EINVAL;
+
+	if (channel_type == NL80211_CHAN_HT40MINUS &&
+	    chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
+		return -EINVAL;
+	else if (channel_type == NL80211_CHAN_HT40PLUS &&
+		 chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
+		return -EINVAL;
+
+	ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
+
+	if (channel_type != NL80211_CHAN_NO_HT) {
+		if (!ht_cap->ht_supported)
+			return -EINVAL;
+
+		if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
+		    ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
+			return -EINVAL;
+	}
+
+	result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type);
+	if (result)
+		return result;
+
+	rdev->channel = chan;
+
+	return 0;
+}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 325c17e6198c456ecc38f791b54b7636245cbca1..5696b95af9bedcee7cdd0175efd0807eacacf7ca 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -366,4 +366,10 @@ void cfg80211_sme_disassoc(struct net_device *dev, int idx);
 void __cfg80211_scan_done(struct work_struct *wk);
 void cfg80211_upload_connect_keys(struct wireless_dev *wdev);
 
+struct ieee80211_channel *
+rdev_fixed_channel(struct cfg80211_registered_device *rdev,
+		   struct wireless_dev *for_wdev);
+int rdev_set_freq(struct cfg80211_registered_device *rdev,
+		  int freq, enum nl80211_channel_type channel_type);
+
 #endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index 4d7a084b35e2e1d2e625e68dba241241fdb33dab..42840a01be7405185b5e00785837d5ed09e9cac8 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -78,10 +78,15 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 			 struct cfg80211_cached_keys *connkeys)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct ieee80211_channel *chan;
 	int err;
 
 	ASSERT_WDEV_LOCK(wdev);
 
+	chan = rdev_fixed_channel(rdev, wdev);
+	if (chan && chan != params->channel)
+		return -EBUSY;
+
 	if (wdev->ssid_len)
 		return -EALREADY;
 
@@ -112,9 +117,11 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
 	int err;
 
+	mutex_lock(&rdev->devlist_mtx);
 	wdev_lock(wdev);
 	err = __cfg80211_join_ibss(rdev, dev, params, connkeys);
 	wdev_unlock(wdev);
+	mutex_unlock(&rdev->devlist_mtx);
 
 	return err;
 }
@@ -264,27 +271,32 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
 
 int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
 			       struct iw_request_info *info,
-			       struct iw_freq *freq, char *extra)
+			       struct iw_freq *wextfreq, char *extra)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
-	struct ieee80211_channel *chan;
-	int err;
+	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+	struct ieee80211_channel *chan = NULL;
+	int err, freq;
 
 	/* call only for ibss! */
 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
 		return -EINVAL;
 
-	if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
+	if (!rdev->ops->join_ibss)
 		return -EOPNOTSUPP;
 
-	chan = cfg80211_wext_freq(wdev->wiphy, freq);
-	if (chan && IS_ERR(chan))
-		return PTR_ERR(chan);
+	freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
+	if (freq < 0)
+		return freq;
 
-	if (chan &&
-	    (chan->flags & IEEE80211_CHAN_NO_IBSS ||
-	     chan->flags & IEEE80211_CHAN_DISABLED))
-		return -EINVAL;
+	if (freq) {
+		chan = ieee80211_get_channel(wdev->wiphy, freq);
+		if (!chan)
+			return -EINVAL;
+		if (chan->flags & IEEE80211_CHAN_NO_IBSS ||
+		    chan->flags & IEEE80211_CHAN_DISABLED)
+			return -EINVAL;
+	}
 
 	if (wdev->wext.ibss.channel == chan)
 		return 0;
@@ -292,8 +304,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
 	wdev_lock(wdev);
 	err = 0;
 	if (wdev->ssid_len)
-		err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
-					    dev, true);
+		err = __cfg80211_leave_ibss(rdev, dev, true);
 	wdev_unlock(wdev);
 
 	if (err)
@@ -307,9 +318,11 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
 		wdev->wext.ibss.channel_fixed = false;
 	}
 
+	mutex_lock(&rdev->devlist_mtx);
 	wdev_lock(wdev);
-	err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
+	err = cfg80211_ibss_wext_join(rdev, wdev);
 	wdev_unlock(wdev);
+	mutex_unlock(&rdev->devlist_mtx);
 
 	return err;
 }
@@ -347,6 +360,7 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,
 				struct iw_point *data, char *ssid)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 	size_t len = data->length;
 	int err;
 
@@ -354,14 +368,13 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,
 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
 		return -EINVAL;
 
-	if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
+	if (!rdev->ops->join_ibss)
 		return -EOPNOTSUPP;
 
 	wdev_lock(wdev);
 	err = 0;
 	if (wdev->ssid_len)
-		err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
-					    dev, true);
+		err = __cfg80211_leave_ibss(rdev, dev, true);
 	wdev_unlock(wdev);
 
 	if (err)
@@ -375,9 +388,11 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,
 	memcpy(wdev->wext.ibss.ssid, ssid, len);
 	wdev->wext.ibss.ssid_len = len;
 
+	mutex_lock(&rdev->devlist_mtx);
 	wdev_lock(wdev);
-	err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
+	err = cfg80211_ibss_wext_join(rdev, wdev);
 	wdev_unlock(wdev);
+	mutex_unlock(&rdev->devlist_mtx);
 
 	return err;
 }
@@ -414,6 +429,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
 			     struct sockaddr *ap_addr, char *extra)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 	u8 *bssid = ap_addr->sa_data;
 	int err;
 
@@ -421,7 +437,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
 		return -EINVAL;
 
-	if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
+	if (!rdev->ops->join_ibss)
 		return -EOPNOTSUPP;
 
 	if (ap_addr->sa_family != ARPHRD_ETHER)
@@ -443,8 +459,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
 	wdev_lock(wdev);
 	err = 0;
 	if (wdev->ssid_len)
-		err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
-					    dev, true);
+		err = __cfg80211_leave_ibss(rdev, dev, true);
 	wdev_unlock(wdev);
 
 	if (err)
@@ -456,9 +471,11 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
 	} else
 		wdev->wext.ibss.bssid = NULL;
 
+	mutex_lock(&rdev->devlist_mtx);
 	wdev_lock(wdev);
-	err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
+	err = cfg80211_ibss_wext_join(rdev, wdev);
 	wdev_unlock(wdev);
+	mutex_unlock(&rdev->devlist_mtx);
 
 	return err;
 }
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 0cd548267d4a717a11e636dce9d548dd03ebd7ea..2ff7376f35a3aa032fc07ec31e8928f5574e8b08 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -701,15 +701,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 
 	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
 		enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
-		struct ieee80211_channel *chan;
-		struct ieee80211_sta_ht_cap *ht_cap;
 		u32 freq;
 
-		if (!rdev->ops->set_channel) {
-			result = -EOPNOTSUPP;
-			goto bad_res;
-		}
-
 		result = -EINVAL;
 
 		if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
@@ -723,42 +716,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 		}
 
 		freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
-		chan = ieee80211_get_channel(&rdev->wiphy, freq);
-
-		/* Primary channel not allowed */
-		if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
-			goto bad_res;
-
-		if (channel_type == NL80211_CHAN_HT40MINUS &&
-		    (chan->flags & IEEE80211_CHAN_NO_HT40MINUS))
-			goto bad_res;
-		else if (channel_type == NL80211_CHAN_HT40PLUS &&
-			 (chan->flags & IEEE80211_CHAN_NO_HT40PLUS))
-			goto bad_res;
-
-		/*
-		 * At this point we know if that if HT40 was requested
-		 * we are allowed to use it and the extension channel
-		 * exists.
-		 */
 
-		ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
-
-		/* no HT capabilities or intolerant */
-		if (channel_type != NL80211_CHAN_NO_HT) {
-			if (!ht_cap->ht_supported)
-				goto bad_res;
-			if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
-			    (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
-				goto bad_res;
-		}
-
-		result = rdev->ops->set_channel(&rdev->wiphy, chan,
-						channel_type);
+		mutex_lock(&rdev->devlist_mtx);
+		result = rdev_set_freq(rdev, freq, channel_type);
+		mutex_unlock(&rdev->devlist_mtx);
 		if (result)
 			goto bad_res;
-
-		rdev->channel = chan;
 	}
 
 	changed = 0;
@@ -3453,7 +3416,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
 	struct cfg80211_registered_device *rdev;
 	struct net_device *dev;
 	struct cfg80211_crypto_settings crypto;
-	struct ieee80211_channel *chan;
+	struct ieee80211_channel *chan, *fixedchan;
 	const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
 	int err, ssid_len, ie_len = 0;
 	bool use_mfp = false;
@@ -3496,6 +3459,15 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
 		goto out;
 	}
 
+	mutex_lock(&rdev->devlist_mtx);
+	fixedchan = rdev_fixed_channel(rdev, NULL);
+	if (fixedchan && chan != fixedchan) {
+		err = -EBUSY;
+		mutex_unlock(&rdev->devlist_mtx);
+		goto out;
+	}
+	mutex_unlock(&rdev->devlist_mtx);
+
 	ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
 	ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
 
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 340934f714b2df619bddda570c013476ea760e8d..219c3bc2c37d3c1a3bf88a0b5aae1b11751d2602 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -256,9 +256,11 @@ void cfg80211_sme_scan_done(struct net_device *dev)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
 
+	mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
 	wdev_lock(wdev);
 	__cfg80211_sme_scan_done(dev);
 	wdev_unlock(wdev);
+	mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
 }
 
 void cfg80211_sme_rx_auth(struct net_device *dev,
@@ -644,6 +646,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
 		       struct cfg80211_cached_keys *connkeys)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct ieee80211_channel *chan;
 	int err;
 
 	ASSERT_WDEV_LOCK(wdev);
@@ -651,6 +654,10 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
 	if (wdev->sme_state != CFG80211_SME_IDLE)
 		return -EALREADY;
 
+	chan = rdev_fixed_channel(rdev, wdev);
+	if (chan && chan != connect->channel)
+		return -EBUSY;
+
 	if (WARN_ON(wdev->connect_keys)) {
 		kfree(wdev->connect_keys);
 		wdev->connect_keys = NULL;
@@ -785,9 +792,11 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
 {
 	int err;
 
+	mutex_lock(&rdev->devlist_mtx);
 	wdev_lock(dev->ieee80211_ptr);
 	err = __cfg80211_connect(rdev, dev, connect, connkeys);
 	wdev_unlock(dev->ieee80211_ptr);
+	mutex_unlock(&rdev->devlist_mtx);
 
 	return err;
 }
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index e4e90e249bab12a1fc0d6317cf356518b9b1428a..17648dc79867f98523be4f358bf93f0793194e08 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -267,39 +267,26 @@ EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange);
  * @wiphy: the wiphy
  * @freq: the wext freq encoding
  *
- * Returns a channel, %NULL for auto, or an ERR_PTR for errors!
+ * Returns a frequency, or a negative error code, or 0 for auto.
  */
-struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy,
-					     struct iw_freq *freq)
+int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq)
 {
-	struct ieee80211_channel *chan;
-	int f;
-
 	/*
-	 * Parse frequency - return NULL for auto and
+	 * Parse frequency - return 0 for auto and
 	 * -EINVAL for impossible things.
 	 */
 	if (freq->e == 0) {
 		if (freq->m < 0)
-			return NULL;
-		f = ieee80211_channel_to_frequency(freq->m);
+			return 0;
+		return ieee80211_channel_to_frequency(freq->m);
 	} else {
 		int i, div = 1000000;
 		for (i = 0; i < freq->e; i++)
 			div /= 10;
 		if (div <= 0)
-			return ERR_PTR(-EINVAL);
-		f = freq->m / div;
+			return -EINVAL;
+		return freq->m / div;
 	}
-
-	/*
-	 * Look up channel struct and return -EINVAL when
-	 * it cannot be found.
-	 */
-	chan = ieee80211_get_channel(wiphy, f);
-	if (!chan)
-		return ERR_PTR(-EINVAL);
-	return chan;
 }
 
 int cfg80211_wext_siwrts(struct net_device *dev,
@@ -761,33 +748,29 @@ EXPORT_SYMBOL_GPL(cfg80211_wext_giwencode);
 
 int cfg80211_wext_siwfreq(struct net_device *dev,
 			  struct iw_request_info *info,
-			  struct iw_freq *freq, char *extra)
+			  struct iw_freq *wextfreq, 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;
+	int freq, err;
 
 	switch (wdev->iftype) {
 	case NL80211_IFTYPE_STATION:
-		return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra);
+		return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra);
 	case NL80211_IFTYPE_ADHOC:
-		return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra);
+		return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra);
 	default:
-		chan = cfg80211_wext_freq(wdev->wiphy, freq);
-		if (!chan)
+		freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
+		if (freq < 0)
+			return freq;
+		if (freq == 0)
 			return -EINVAL;
-		if (IS_ERR(chan))
-			return PTR_ERR(chan);
-		err = rdev->ops->set_channel(wdev->wiphy, chan,
-					     NL80211_CHAN_NO_HT);
-		if (err)
-			return err;
-		rdev->channel = chan;
-		return 0;
+		mutex_lock(&rdev->devlist_mtx);
+		err = rdev_set_freq(rdev, freq, NL80211_CHAN_NO_HT);
+		mutex_unlock(&rdev->devlist_mtx);
+		return err;
 	}
 }
-EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq);
 
 int cfg80211_wext_giwfreq(struct net_device *dev,
 			  struct iw_request_info *info,
diff --git a/net/wireless/wext-compat.h b/net/wireless/wext-compat.h
index 9a37747495890548621dcf0c5a851e4e4fe66eb3..20b3daef69643586d2508deca0f1f52c1ebe6ac0 100644
--- a/net/wireless/wext-compat.h
+++ b/net/wireless/wext-compat.h
@@ -42,8 +42,7 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev,
 			       struct iw_request_info *info,
 			       struct iw_point *data, char *ssid);
 
-struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy,
-					     struct iw_freq *freq);
+int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq);
 
 
 extern const struct iw_handler_def cfg80211_wext_handler;
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
index e4a054aceb5a803400dc9218fd301f4885eff8a9..fe1a536391226acf727625aa8dba2be76f01497a 100644
--- a/net/wireless/wext-sme.c
+++ b/net/wireless/wext-sme.c
@@ -52,25 +52,31 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
 
 int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
 			      struct iw_request_info *info,
-			      struct iw_freq *freq, char *extra)
+			      struct iw_freq *wextfreq, 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;
+	struct ieee80211_channel *chan = NULL;
+	int err, freq;
 
 	/* 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);
+	freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
+	if (freq < 0)
+		return freq;
 
-	if (chan && (chan->flags & IEEE80211_CHAN_DISABLED))
-		return -EINVAL;
+	if (freq) {
+		chan = ieee80211_get_channel(wdev->wiphy, freq);
+		if (!chan)
+			return -EINVAL;
+		if (chan->flags & IEEE80211_CHAN_DISABLED)
+			return -EINVAL;
+	}
 
 	cfg80211_lock_rdev(rdev);
+	mutex_lock(&rdev->devlist_mtx);
 	wdev_lock(wdev);
 
 	if (wdev->sme_state != CFG80211_SME_IDLE) {
@@ -84,9 +90,8 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
 		/* 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);
+		err = __cfg80211_disconnect(rdev, dev,
+					    WLAN_REASON_DEAUTH_LEAVING, event);
 		if (err)
 			goto out;
 	}
@@ -95,17 +100,15 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
 	wdev->wext.connect.channel = chan;
 
 	/* SSID is not set, we just want to switch channel */
-	if (wdev->wext.connect.ssid_len && chan) {
-		err = -EOPNOTSUPP;
-		if (rdev->ops->set_channel)
-			err = rdev->ops->set_channel(wdev->wiphy, chan,
-						     NL80211_CHAN_NO_HT);
+	if (chan && !wdev->wext.connect.ssid_len) {
+		err = rdev_set_freq(rdev, freq, NL80211_CHAN_NO_HT);
 		goto out;
 	}
 
-	err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev);
+	err = cfg80211_mgd_wext_connect(rdev, wdev);
  out:
 	wdev_unlock(wdev);
+	mutex_unlock(&rdev->devlist_mtx);
 	cfg80211_unlock_rdev(rdev);
 	return err;
 }
@@ -143,6 +146,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
 			       struct iw_point *data, char *ssid)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 	size_t len = data->length;
 	int err;
 
@@ -157,7 +161,8 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
 	if (len > 0 && ssid[len - 1] == '\0')
 		len--;
 
-	cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy));
+	cfg80211_lock_rdev(rdev);
+	mutex_lock(&rdev->devlist_mtx);
 	wdev_lock(wdev);
 
 	err = 0;
@@ -173,9 +178,8 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
 		/* 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);
+		err = __cfg80211_disconnect(rdev, dev,
+					    WLAN_REASON_DEAUTH_LEAVING, event);
 		if (err)
 			goto out;
 	}
@@ -186,10 +190,11 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
 
 	wdev->wext.connect.crypto.control_port = false;
 
-	err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev);
+	err = cfg80211_mgd_wext_connect(rdev, wdev);
  out:
 	wdev_unlock(wdev);
-	cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy));
+	mutex_unlock(&rdev->devlist_mtx);
+	cfg80211_unlock_rdev(rdev);
 	return err;
 }
 
@@ -230,6 +235,7 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
 			    struct sockaddr *ap_addr, char *extra)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 	u8 *bssid = ap_addr->sa_data;
 	int err;
 
@@ -244,7 +250,8 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
 	if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
 		bssid = NULL;
 
-	cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy));
+	cfg80211_lock_rdev(rdev);
+	mutex_lock(&rdev->devlist_mtx);
 	wdev_lock(wdev);
 
 	if (wdev->sme_state != CFG80211_SME_IDLE) {
@@ -258,9 +265,8 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
 		    compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0)
 			goto out;
 
-		err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy),
-					    dev, WLAN_REASON_DEAUTH_LEAVING,
-					    false);
+		err = __cfg80211_disconnect(rdev, dev,
+					    WLAN_REASON_DEAUTH_LEAVING, false);
 		if (err)
 			goto out;
 	}
@@ -271,10 +277,11 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
 	} else
 		wdev->wext.connect.bssid = NULL;
 
-	err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev);
+	err = cfg80211_mgd_wext_connect(rdev, wdev);
  out:
 	wdev_unlock(wdev);
-	cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy));
+	mutex_unlock(&rdev->devlist_mtx);
+	cfg80211_unlock_rdev(rdev);
 	return err;
 }