aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2010-05-05 09:25:02 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-05-07 14:55:50 -0400
commitf444de05d20e27cdd960c13fcbcfca3099f03143 (patch)
treea7fbef60420d88dda5840e06094be21ee3eb1dc0 /net
parentac8dd506e40ee2c7fcc61654a44c32555a0a8d6c (diff)
cfg80211/mac80211: better channel handling
Currently (all tested with hwsim) you can do stupid things like setting up an AP on a certain channel, then adding another virtual interface and making that associate on another channel -- this will make the beaconing to move channel but obviously without the necessary IEs data update. In order to improve this situation, first make the configuration APIs (cfg80211 and nl80211) aware of multi-channel operation -- we'll eventually need that in the future anyway. There's one userland API change and one API addition. The API change is that now SET_WIPHY must be called with virtual interface index rather than only wiphy index in order to take effect for that interface -- luckily all current users (hostapd) do that. For monitor interfaces, the old setting is preserved, but monitors are always slaved to other devices anyway so no guarantees. The second userland API change is the introduction of a per virtual interface SET_CHANNEL command, that hostapd should use going forward to make it easier to understand what's going on (it can automatically detect a kernel with this command). Other than mac80211, no existing cfg80211 drivers are affected by this change because they only allow a single virtual interface. mac80211, however, now needs to be aware that the channel settings are per interface now, and needs to disallow (for now) real multi-channel operation, which is another important part of this patch. One of the immediate benefits is that you can now start hostapd to operate on a hardware that already has a connection on another virtual interface, as long as you specify the same channel. Note that two things are left unhandled (this is an improvement -- not a complete fix): * different HT/no-HT modes currently you could start an HT AP and then connect to a non-HT network on the same channel which would configure the hardware for no HT; that can be fixed fairly easily * CSA An AP we're connected to on a virtual interface might indicate switching channels, and in that case we would follow it, regardless of how many other interfaces are operating; this requires more effort to fix but is pretty rare after all Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/Makefile3
-rw-r--r--net/mac80211/cfg.c41
-rw-r--r--net/mac80211/chan.c57
-rw-r--r--net/mac80211/ieee80211_i.h11
-rw-r--r--net/wireless/chan.c56
-rw-r--r--net/wireless/core.h12
-rw-r--r--net/wireless/ibss.c5
-rw-r--r--net/wireless/nl80211.c171
-rw-r--r--net/wireless/sme.c5
-rw-r--r--net/wireless/wext-compat.c15
-rw-r--r--net/wireless/wext-sme.c2
11 files changed, 274 insertions, 104 deletions
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 04420291e7ad..84b48ba8a77e 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -23,7 +23,8 @@ mac80211-y := \
23 key.o \ 23 key.o \
24 util.o \ 24 util.o \
25 wme.o \ 25 wme.o \
26 event.o 26 event.o \
27 chan.o
27 28
28mac80211-$(CONFIG_MAC80211_LEDS) += led.o 29mac80211-$(CONFIG_MAC80211_LEDS) += led.o
29mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ 30mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index b575a5066219..414b7dd7d7fd 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1161,11 +1161,24 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy,
1161} 1161}
1162 1162
1163static int ieee80211_set_channel(struct wiphy *wiphy, 1163static int ieee80211_set_channel(struct wiphy *wiphy,
1164 struct net_device *netdev,
1164 struct ieee80211_channel *chan, 1165 struct ieee80211_channel *chan,
1165 enum nl80211_channel_type channel_type) 1166 enum nl80211_channel_type channel_type)
1166{ 1167{
1167 struct ieee80211_local *local = wiphy_priv(wiphy); 1168 struct ieee80211_local *local = wiphy_priv(wiphy);
1168 1169
1170 switch (ieee80211_get_channel_mode(local, NULL)) {
1171 case CHAN_MODE_HOPPING:
1172 return -EBUSY;
1173 case CHAN_MODE_FIXED:
1174 if (local->oper_channel == chan &&
1175 local->oper_channel_type == channel_type)
1176 return 0;
1177 return -EBUSY;
1178 case CHAN_MODE_UNDEFINED:
1179 break;
1180 }
1181
1169 local->oper_channel = chan; 1182 local->oper_channel = chan;
1170 local->oper_channel_type = channel_type; 1183 local->oper_channel_type = channel_type;
1171 1184
@@ -1213,6 +1226,20 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
1213static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, 1226static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
1214 struct cfg80211_assoc_request *req) 1227 struct cfg80211_assoc_request *req)
1215{ 1228{
1229 struct ieee80211_local *local = wiphy_priv(wiphy);
1230 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1231
1232 switch (ieee80211_get_channel_mode(local, sdata)) {
1233 case CHAN_MODE_HOPPING:
1234 return -EBUSY;
1235 case CHAN_MODE_FIXED:
1236 if (local->oper_channel == req->bss->channel)
1237 break;
1238 return -EBUSY;
1239 case CHAN_MODE_UNDEFINED:
1240 break;
1241 }
1242
1216 return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req); 1243 return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
1217} 1244}
1218 1245
@@ -1235,8 +1262,22 @@ static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev,
1235static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, 1262static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
1236 struct cfg80211_ibss_params *params) 1263 struct cfg80211_ibss_params *params)
1237{ 1264{
1265 struct ieee80211_local *local = wiphy_priv(wiphy);
1238 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 1266 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1239 1267
1268 switch (ieee80211_get_channel_mode(local, sdata)) {
1269 case CHAN_MODE_HOPPING:
1270 return -EBUSY;
1271 case CHAN_MODE_FIXED:
1272 if (!params->channel_fixed)
1273 return -EBUSY;
1274 if (local->oper_channel == params->channel)
1275 break;
1276 return -EBUSY;
1277 case CHAN_MODE_UNDEFINED:
1278 break;
1279 }
1280
1240 return ieee80211_ibss_join(sdata, params); 1281 return ieee80211_ibss_join(sdata, params);
1241} 1282}
1242 1283
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
new file mode 100644
index 000000000000..08f3832661a5
--- /dev/null
+++ b/net/mac80211/chan.c
@@ -0,0 +1,57 @@
1/*
2 * mac80211 - channel management
3 */
4
5#include "ieee80211_i.h"
6
7enum ieee80211_chan_mode
8__ieee80211_get_channel_mode(struct ieee80211_local *local,
9 struct ieee80211_sub_if_data *ignore)
10{
11 struct ieee80211_sub_if_data *sdata;
12
13 WARN_ON(!mutex_is_locked(&local->iflist_mtx));
14
15 list_for_each_entry(sdata, &local->interfaces, list) {
16 if (sdata == ignore)
17 continue;
18
19 if (!ieee80211_sdata_running(sdata))
20 continue;
21
22 if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
23 continue;
24
25 if (sdata->vif.type == NL80211_IFTYPE_STATION &&
26 !sdata->u.mgd.associated)
27 continue;
28
29 if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
30 if (!sdata->u.ibss.ssid_len)
31 continue;
32 if (!sdata->u.ibss.fixed_channel)
33 return CHAN_MODE_HOPPING;
34 }
35
36 if (sdata->vif.type == NL80211_IFTYPE_AP &&
37 !sdata->u.ap.beacon)
38 continue;
39
40 return CHAN_MODE_FIXED;
41 }
42
43 return CHAN_MODE_UNDEFINED;
44}
45
46enum ieee80211_chan_mode
47ieee80211_get_channel_mode(struct ieee80211_local *local,
48 struct ieee80211_sub_if_data *ignore)
49{
50 enum ieee80211_chan_mode mode;
51
52 mutex_lock(&local->iflist_mtx);
53 mode = __ieee80211_get_channel_mode(local, ignore);
54 mutex_unlock(&local->iflist_mtx);
55
56 return mode;
57}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index c8077a3647c6..359edff31471 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1229,6 +1229,17 @@ int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata,
1229int ieee80211_wk_cancel_remain_on_channel( 1229int ieee80211_wk_cancel_remain_on_channel(
1230 struct ieee80211_sub_if_data *sdata, u64 cookie); 1230 struct ieee80211_sub_if_data *sdata, u64 cookie);
1231 1231
1232/* channel management */
1233enum ieee80211_chan_mode {
1234 CHAN_MODE_UNDEFINED,
1235 CHAN_MODE_HOPPING,
1236 CHAN_MODE_FIXED,
1237};
1238
1239enum ieee80211_chan_mode
1240ieee80211_get_channel_mode(struct ieee80211_local *local,
1241 struct ieee80211_sub_if_data *ignore);
1242
1232#ifdef CONFIG_MAC80211_NOINLINE 1243#ifdef CONFIG_MAC80211_NOINLINE
1233#define debug_noinline noinline 1244#define debug_noinline noinline
1234#else 1245#else
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index bf1737fc9a7e..d92d088026bf 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -10,38 +10,6 @@
10#include "core.h" 10#include "core.h"
11 11
12struct ieee80211_channel * 12struct ieee80211_channel *
13rdev_fixed_channel(struct cfg80211_registered_device *rdev,
14 struct wireless_dev *for_wdev)
15{
16 struct wireless_dev *wdev;
17 struct ieee80211_channel *result = NULL;
18
19 WARN_ON(!mutex_is_locked(&rdev->devlist_mtx));
20
21 list_for_each_entry(wdev, &rdev->netdev_list, list) {
22 if (wdev == for_wdev)
23 continue;
24
25 /*
26 * Lock manually to tell lockdep about allowed
27 * nesting here if for_wdev->mtx is held already.
28 * This is ok as it's all under the rdev devlist
29 * mutex and as such can only be done once at any
30 * given time.
31 */
32 mutex_lock_nested(&wdev->mtx, SINGLE_DEPTH_NESTING);
33 if (wdev->current_bss)
34 result = wdev->current_bss->pub.channel;
35 wdev_unlock(wdev);
36
37 if (result)
38 break;
39 }
40
41 return result;
42}
43
44struct ieee80211_channel *
45rdev_freq_to_chan(struct cfg80211_registered_device *rdev, 13rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
46 int freq, enum nl80211_channel_type channel_type) 14 int freq, enum nl80211_channel_type channel_type)
47{ 15{
@@ -75,15 +43,22 @@ rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
75 return chan; 43 return chan;
76} 44}
77 45
78int rdev_set_freq(struct cfg80211_registered_device *rdev, 46int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
79 struct wireless_dev *for_wdev, 47 struct wireless_dev *wdev, int freq,
80 int freq, enum nl80211_channel_type channel_type) 48 enum nl80211_channel_type channel_type)
81{ 49{
82 struct ieee80211_channel *chan; 50 struct ieee80211_channel *chan;
83 int result; 51 int result;
84 52
85 if (rdev_fixed_channel(rdev, for_wdev)) 53 if (wdev->iftype == NL80211_IFTYPE_MONITOR)
86 return -EBUSY; 54 wdev = NULL;
55
56 if (wdev) {
57 ASSERT_WDEV_LOCK(wdev);
58
59 if (!netif_running(wdev->netdev))
60 return -ENETDOWN;
61 }
87 62
88 if (!rdev->ops->set_channel) 63 if (!rdev->ops->set_channel)
89 return -EOPNOTSUPP; 64 return -EOPNOTSUPP;
@@ -92,11 +67,14 @@ int rdev_set_freq(struct cfg80211_registered_device *rdev,
92 if (!chan) 67 if (!chan)
93 return -EINVAL; 68 return -EINVAL;
94 69
95 result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type); 70 result = rdev->ops->set_channel(&rdev->wiphy,
71 wdev ? wdev->netdev : NULL,
72 chan, channel_type);
96 if (result) 73 if (result)
97 return result; 74 return result;
98 75
99 rdev->channel = chan; 76 if (wdev)
77 wdev->channel = chan;
100 78
101 return 0; 79 return 0;
102} 80}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index b2234b436ead..ae930acf75e9 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -70,9 +70,6 @@ struct cfg80211_registered_device {
70 struct work_struct conn_work; 70 struct work_struct conn_work;
71 struct work_struct event_work; 71 struct work_struct event_work;
72 72
73 /* current channel */
74 struct ieee80211_channel *channel;
75
76 /* must be last because of the way we do wiphy_priv(), 73 /* must be last because of the way we do wiphy_priv(),
77 * and it should at least be aligned to NETDEV_ALIGN */ 74 * and it should at least be aligned to NETDEV_ALIGN */
78 struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); 75 struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));
@@ -388,14 +385,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
388void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); 385void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
389 386
390struct ieee80211_channel * 387struct ieee80211_channel *
391rdev_fixed_channel(struct cfg80211_registered_device *rdev,
392 struct wireless_dev *for_wdev);
393struct ieee80211_channel *
394rdev_freq_to_chan(struct cfg80211_registered_device *rdev, 388rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
395 int freq, enum nl80211_channel_type channel_type); 389 int freq, enum nl80211_channel_type channel_type);
396int rdev_set_freq(struct cfg80211_registered_device *rdev, 390int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
397 struct wireless_dev *for_wdev, 391 struct wireless_dev *wdev, int freq,
398 int freq, enum nl80211_channel_type channel_type); 392 enum nl80211_channel_type channel_type);
399 393
400u16 cfg80211_calculate_bitrate(struct rate_info *rate); 394u16 cfg80211_calculate_bitrate(struct rate_info *rate);
401 395
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index 6ef5a491fb4b..9825317e653a 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -80,15 +80,10 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
80 struct cfg80211_cached_keys *connkeys) 80 struct cfg80211_cached_keys *connkeys)
81{ 81{
82 struct wireless_dev *wdev = dev->ieee80211_ptr; 82 struct wireless_dev *wdev = dev->ieee80211_ptr;
83 struct ieee80211_channel *chan;
84 int err; 83 int err;
85 84
86 ASSERT_WDEV_LOCK(wdev); 85 ASSERT_WDEV_LOCK(wdev);
87 86
88 chan = rdev_fixed_channel(rdev, wdev);
89 if (chan && chan != params->channel)
90 return -EBUSY;
91
92 if (wdev->ssid_len) 87 if (wdev->ssid_len)
93 return -EALREADY; 88 return -EALREADY;
94 89
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index c27bef8e0c11..ec1b4a896c6e 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -588,6 +588,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
588 i++; 588 i++;
589 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); 589 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
590 } 590 }
591 CMD(set_channel, SET_CHANNEL);
591 592
592#undef CMD 593#undef CMD
593 594
@@ -688,10 +689,90 @@ static int parse_txq_params(struct nlattr *tb[],
688 return 0; 689 return 0;
689} 690}
690 691
692static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
693{
694 /*
695 * You can only set the channel explicitly for AP, mesh
696 * and WDS type interfaces; all others have their channel
697 * managed via their respective "establish a connection"
698 * command (connect, join, ...)
699 *
700 * Monitors are special as they are normally slaved to
701 * whatever else is going on, so they behave as though
702 * you tried setting the wiphy channel itself.
703 */
704 return !wdev ||
705 wdev->iftype == NL80211_IFTYPE_AP ||
706 wdev->iftype == NL80211_IFTYPE_WDS ||
707 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
708 wdev->iftype == NL80211_IFTYPE_MONITOR;
709}
710
711static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
712 struct wireless_dev *wdev,
713 struct genl_info *info)
714{
715 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
716 u32 freq;
717 int result;
718
719 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
720 return -EINVAL;
721
722 if (!nl80211_can_set_dev_channel(wdev))
723 return -EOPNOTSUPP;
724
725 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
726 channel_type = nla_get_u32(info->attrs[
727 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
728 if (channel_type != NL80211_CHAN_NO_HT &&
729 channel_type != NL80211_CHAN_HT20 &&
730 channel_type != NL80211_CHAN_HT40PLUS &&
731 channel_type != NL80211_CHAN_HT40MINUS)
732 return -EINVAL;
733 }
734
735 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
736
737 mutex_lock(&rdev->devlist_mtx);
738 if (wdev) {
739 wdev_lock(wdev);
740 result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
741 wdev_unlock(wdev);
742 } else {
743 result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
744 }
745 mutex_unlock(&rdev->devlist_mtx);
746
747 return result;
748}
749
750static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
751{
752 struct cfg80211_registered_device *rdev;
753 struct net_device *netdev;
754 int result;
755
756 rtnl_lock();
757
758 result = get_rdev_dev_by_info_ifindex(info, &rdev, &netdev);
759 if (result)
760 goto unlock;
761
762 result = __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
763
764 unlock:
765 rtnl_unlock();
766
767 return result;
768}
769
691static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) 770static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
692{ 771{
693 struct cfg80211_registered_device *rdev; 772 struct cfg80211_registered_device *rdev;
694 int result = 0, rem_txq_params = 0; 773 struct net_device *netdev = NULL;
774 struct wireless_dev *wdev;
775 int result, rem_txq_params = 0;
695 struct nlattr *nl_txq_params; 776 struct nlattr *nl_txq_params;
696 u32 changed; 777 u32 changed;
697 u8 retry_short = 0, retry_long = 0; 778 u8 retry_short = 0, retry_long = 0;
@@ -700,16 +781,50 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
700 781
701 rtnl_lock(); 782 rtnl_lock();
702 783
784 /*
785 * Try to find the wiphy and netdev. Normally this
786 * function shouldn't need the netdev, but this is
787 * done for backward compatibility -- previously
788 * setting the channel was done per wiphy, but now
789 * it is per netdev. Previous userland like hostapd
790 * also passed a netdev to set_wiphy, so that it is
791 * possible to let that go to the right netdev!
792 */
703 mutex_lock(&cfg80211_mutex); 793 mutex_lock(&cfg80211_mutex);
704 794
705 rdev = __cfg80211_rdev_from_info(info); 795 if (info->attrs[NL80211_ATTR_IFINDEX]) {
706 if (IS_ERR(rdev)) { 796 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
707 mutex_unlock(&cfg80211_mutex); 797
708 result = PTR_ERR(rdev); 798 netdev = dev_get_by_index(genl_info_net(info), ifindex);
709 goto unlock; 799 if (netdev && netdev->ieee80211_ptr) {
800 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
801 mutex_lock(&rdev->mtx);
802 } else
803 netdev = NULL;
710 } 804 }
711 805
712 mutex_lock(&rdev->mtx); 806 if (!netdev) {
807 rdev = __cfg80211_rdev_from_info(info);
808 if (IS_ERR(rdev)) {
809 mutex_unlock(&cfg80211_mutex);
810 result = PTR_ERR(rdev);
811 goto unlock;
812 }
813 wdev = NULL;
814 netdev = NULL;
815 result = 0;
816
817 mutex_lock(&rdev->mtx);
818 } else if (netif_running(netdev) &&
819 nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
820 wdev = netdev->ieee80211_ptr;
821 else
822 wdev = NULL;
823
824 /*
825 * end workaround code, by now the rdev is available
826 * and locked, and wdev may or may not be NULL.
827 */
713 828
714 if (info->attrs[NL80211_ATTR_WIPHY_NAME]) 829 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
715 result = cfg80211_dev_rename( 830 result = cfg80211_dev_rename(
@@ -748,26 +863,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
748 } 863 }
749 864
750 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { 865 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
751 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; 866 result = __nl80211_set_channel(rdev, wdev, info);
752 u32 freq;
753
754 result = -EINVAL;
755
756 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
757 channel_type = nla_get_u32(info->attrs[
758 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
759 if (channel_type != NL80211_CHAN_NO_HT &&
760 channel_type != NL80211_CHAN_HT20 &&
761 channel_type != NL80211_CHAN_HT40PLUS &&
762 channel_type != NL80211_CHAN_HT40MINUS)
763 goto bad_res;
764 }
765
766 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
767
768 mutex_lock(&rdev->devlist_mtx);
769 result = rdev_set_freq(rdev, NULL, freq, channel_type);
770 mutex_unlock(&rdev->devlist_mtx);
771 if (result) 867 if (result)
772 goto bad_res; 868 goto bad_res;
773 } 869 }
@@ -864,6 +960,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
864 960
865 bad_res: 961 bad_res:
866 mutex_unlock(&rdev->mtx); 962 mutex_unlock(&rdev->mtx);
963 if (netdev)
964 dev_put(netdev);
867 unlock: 965 unlock:
868 rtnl_unlock(); 966 rtnl_unlock();
869 return result; 967 return result;
@@ -3561,9 +3659,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3561{ 3659{
3562 struct cfg80211_registered_device *rdev; 3660 struct cfg80211_registered_device *rdev;
3563 struct net_device *dev; 3661 struct net_device *dev;
3564 struct wireless_dev *wdev;
3565 struct cfg80211_crypto_settings crypto; 3662 struct cfg80211_crypto_settings crypto;
3566 struct ieee80211_channel *chan, *fixedchan; 3663 struct ieee80211_channel *chan;
3567 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; 3664 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
3568 int err, ssid_len, ie_len = 0; 3665 int err, ssid_len, ie_len = 0;
3569 bool use_mfp = false; 3666 bool use_mfp = false;
@@ -3606,16 +3703,6 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3606 goto out; 3703 goto out;
3607 } 3704 }
3608 3705
3609 mutex_lock(&rdev->devlist_mtx);
3610 wdev = dev->ieee80211_ptr;
3611 fixedchan = rdev_fixed_channel(rdev, wdev);
3612 if (fixedchan && chan != fixedchan) {
3613 err = -EBUSY;
3614 mutex_unlock(&rdev->devlist_mtx);
3615 goto out;
3616 }
3617 mutex_unlock(&rdev->devlist_mtx);
3618
3619 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); 3706 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3620 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); 3707 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3621 3708
@@ -5185,6 +5272,12 @@ static struct genl_ops nl80211_ops[] = {
5185 .policy = nl80211_policy, 5272 .policy = nl80211_policy,
5186 .flags = GENL_ADMIN_PERM, 5273 .flags = GENL_ADMIN_PERM,
5187 }, 5274 },
5275 {
5276 .cmd = NL80211_CMD_SET_CHANNEL,
5277 .doit = nl80211_set_channel,
5278 .policy = nl80211_policy,
5279 .flags = GENL_ADMIN_PERM,
5280 },
5188}; 5281};
5189 5282
5190static struct genl_multicast_group nl80211_mlme_mcgrp = { 5283static struct genl_multicast_group nl80211_mlme_mcgrp = {
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index dcd7685242f7..14cf8163912a 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -740,7 +740,6 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
740 const u8 *prev_bssid) 740 const u8 *prev_bssid)
741{ 741{
742 struct wireless_dev *wdev = dev->ieee80211_ptr; 742 struct wireless_dev *wdev = dev->ieee80211_ptr;
743 struct ieee80211_channel *chan;
744 struct cfg80211_bss *bss = NULL; 743 struct cfg80211_bss *bss = NULL;
745 int err; 744 int err;
746 745
@@ -749,10 +748,6 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
749 if (wdev->sme_state != CFG80211_SME_IDLE) 748 if (wdev->sme_state != CFG80211_SME_IDLE)
750 return -EALREADY; 749 return -EALREADY;
751 750
752 chan = rdev_fixed_channel(rdev, wdev);
753 if (chan && chan != connect->channel)
754 return -EBUSY;
755
756 if (WARN_ON(wdev->connect_keys)) { 751 if (WARN_ON(wdev->connect_keys)) {
757 kfree(wdev->connect_keys); 752 kfree(wdev->connect_keys);
758 wdev->connect_keys = NULL; 753 wdev->connect_keys = NULL;
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 9ab51838849e..75848c6cb22a 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -781,16 +781,22 @@ int cfg80211_wext_siwfreq(struct net_device *dev,
781 return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra); 781 return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra);
782 case NL80211_IFTYPE_ADHOC: 782 case NL80211_IFTYPE_ADHOC:
783 return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); 783 return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra);
784 default: 784 case NL80211_IFTYPE_MONITOR:
785 case NL80211_IFTYPE_WDS:
786 case NL80211_IFTYPE_MESH_POINT:
785 freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); 787 freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
786 if (freq < 0) 788 if (freq < 0)
787 return freq; 789 return freq;
788 if (freq == 0) 790 if (freq == 0)
789 return -EINVAL; 791 return -EINVAL;
792 wdev_lock(wdev);
790 mutex_lock(&rdev->devlist_mtx); 793 mutex_lock(&rdev->devlist_mtx);
791 err = rdev_set_freq(rdev, NULL, freq, NL80211_CHAN_NO_HT); 794 err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT);
792 mutex_unlock(&rdev->devlist_mtx); 795 mutex_unlock(&rdev->devlist_mtx);
796 wdev_unlock(wdev);
793 return err; 797 return err;
798 default:
799 return -EOPNOTSUPP;
794 } 800 }
795} 801}
796EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq); 802EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq);
@@ -800,7 +806,6 @@ int cfg80211_wext_giwfreq(struct net_device *dev,
800 struct iw_freq *freq, char *extra) 806 struct iw_freq *freq, char *extra)
801{ 807{
802 struct wireless_dev *wdev = dev->ieee80211_ptr; 808 struct wireless_dev *wdev = dev->ieee80211_ptr;
803 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
804 809
805 switch (wdev->iftype) { 810 switch (wdev->iftype) {
806 case NL80211_IFTYPE_STATION: 811 case NL80211_IFTYPE_STATION:
@@ -808,9 +813,9 @@ int cfg80211_wext_giwfreq(struct net_device *dev,
808 case NL80211_IFTYPE_ADHOC: 813 case NL80211_IFTYPE_ADHOC:
809 return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); 814 return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
810 default: 815 default:
811 if (!rdev->channel) 816 if (!wdev->channel)
812 return -EINVAL; 817 return -EINVAL;
813 freq->m = rdev->channel->center_freq; 818 freq->m = wdev->channel->center_freq;
814 freq->e = 6; 819 freq->e = 6;
815 return 0; 820 return 0;
816 } 821 }
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
index 5615a8802536..8e5ab4f4e9c4 100644
--- a/net/wireless/wext-sme.c
+++ b/net/wireless/wext-sme.c
@@ -107,7 +107,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
107 107
108 /* SSID is not set, we just want to switch channel */ 108 /* SSID is not set, we just want to switch channel */
109 if (chan && !wdev->wext.connect.ssid_len) { 109 if (chan && !wdev->wext.connect.ssid_len) {
110 err = rdev_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); 110 err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT);
111 goto out; 111 goto out;
112 } 112 }
113 113