aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/libertas/cfg.c1
-rw-r--r--drivers/net/wireless/orinoco/cfg.c1
-rw-r--r--drivers/net/wireless/rndis_wlan.c4
-rw-r--r--include/linux/nl80211.h13
-rw-r--r--include/net/cfg80211.h11
-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
16 files changed, 300 insertions, 108 deletions
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index 82ebe1461a77..ea9d0b2ea0d7 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -78,6 +78,7 @@ static const u32 cipher_suites[] = {
78 78
79 79
80static int lbs_cfg_set_channel(struct wiphy *wiphy, 80static int lbs_cfg_set_channel(struct wiphy *wiphy,
81 struct net_device *netdev,
81 struct ieee80211_channel *chan, 82 struct ieee80211_channel *chan,
82 enum nl80211_channel_type channel_type) 83 enum nl80211_channel_type channel_type)
83{ 84{
diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c
index 81d228de9e5d..8c4169c227ae 100644
--- a/drivers/net/wireless/orinoco/cfg.c
+++ b/drivers/net/wireless/orinoco/cfg.c
@@ -159,6 +159,7 @@ static int orinoco_scan(struct wiphy *wiphy, struct net_device *dev,
159} 159}
160 160
161static int orinoco_set_channel(struct wiphy *wiphy, 161static int orinoco_set_channel(struct wiphy *wiphy,
162 struct net_device *netdev,
162 struct ieee80211_channel *chan, 163 struct ieee80211_channel *chan,
163 enum nl80211_channel_type channel_type) 164 enum nl80211_channel_type channel_type)
164{ 165{
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index aceb95ef7274..99d4f0de77ca 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -534,7 +534,7 @@ static int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev,
534 534
535static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev); 535static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev);
536 536
537static int rndis_set_channel(struct wiphy *wiphy, 537static int rndis_set_channel(struct wiphy *wiphy, struct net_device *dev,
538 struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); 538 struct ieee80211_channel *chan, enum nl80211_channel_type channel_type);
539 539
540static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, 540static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev,
@@ -2290,7 +2290,7 @@ static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
2290 return deauthenticate(usbdev); 2290 return deauthenticate(usbdev);
2291} 2291}
2292 2292
2293static int rndis_set_channel(struct wiphy *wiphy, 2293static int rndis_set_channel(struct wiphy *wiphy, struct net_device *netdev,
2294 struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) 2294 struct ieee80211_channel *chan, enum nl80211_channel_type channel_type)
2295{ 2295{
2296 struct rndis_wlan_private *priv = wiphy_priv(wiphy); 2296 struct rndis_wlan_private *priv = wiphy_priv(wiphy);
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index f8750f9a65b8..b7c77f9712f4 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -52,6 +52,8 @@
52 * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, 52 * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT,
53 * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, 53 * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
54 * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. 54 * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD.
55 * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL
56 * instead, the support here is for backward compatibility only.
55 * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request 57 * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
56 * or rename notification. Has attributes %NL80211_ATTR_WIPHY and 58 * or rename notification. Has attributes %NL80211_ATTR_WIPHY and
57 * %NL80211_ATTR_WIPHY_NAME. 59 * %NL80211_ATTR_WIPHY_NAME.
@@ -329,6 +331,15 @@
329 * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This 331 * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This
330 * command is used as an event to indicate the that a trigger level was 332 * command is used as an event to indicate the that a trigger level was
331 * reached. 333 * reached.
334 * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
335 * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed
336 * by %NL80211_ATTR_IFINDEX) shall operate on.
337 * In case multiple channels are supported by the device, the mechanism
338 * with which it switches channels is implementation-defined.
339 * When a monitor interface is given, it can only switch channel while
340 * no other interfaces are operating to avoid disturbing the operation
341 * of any other interfaces, and other interfaces will again take
342 * precedence when they are used.
332 * 343 *
333 * @NL80211_CMD_MAX: highest used command number 344 * @NL80211_CMD_MAX: highest used command number
334 * @__NL80211_CMD_AFTER_LAST: internal use 345 * @__NL80211_CMD_AFTER_LAST: internal use
@@ -428,6 +439,8 @@ enum nl80211_commands {
428 NL80211_CMD_SET_CQM, 439 NL80211_CMD_SET_CQM,
429 NL80211_CMD_NOTIFY_CQM, 440 NL80211_CMD_NOTIFY_CQM,
430 441
442 NL80211_CMD_SET_CHANNEL,
443
431 /* add new commands above here */ 444 /* add new commands above here */
432 445
433 /* used to define NL80211_CMD_MAX below */ 446 /* used to define NL80211_CMD_MAX below */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 7d10c0182f53..b44a2e5321a3 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -966,7 +966,11 @@ struct cfg80211_pmksa {
966 * 966 *
967 * @set_txq_params: Set TX queue parameters 967 * @set_txq_params: Set TX queue parameters
968 * 968 *
969 * @set_channel: Set channel 969 * @set_channel: Set channel for a given wireless interface. Some devices
970 * may support multi-channel operation (by channel hopping) so cfg80211
971 * doesn't verify much. Note, however, that the passed netdev may be
972 * %NULL as well if the user requested changing the channel for the
973 * device itself, or for a monitor interface.
970 * 974 *
971 * @scan: Request to do a scan. If returning zero, the scan request is given 975 * @scan: Request to do a scan. If returning zero, the scan request is given
972 * the driver, and will be valid until passed to cfg80211_scan_done(). 976 * the driver, and will be valid until passed to cfg80211_scan_done().
@@ -1095,7 +1099,7 @@ struct cfg80211_ops {
1095 int (*set_txq_params)(struct wiphy *wiphy, 1099 int (*set_txq_params)(struct wiphy *wiphy,
1096 struct ieee80211_txq_params *params); 1100 struct ieee80211_txq_params *params);
1097 1101
1098 int (*set_channel)(struct wiphy *wiphy, 1102 int (*set_channel)(struct wiphy *wiphy, struct net_device *dev,
1099 struct ieee80211_channel *chan, 1103 struct ieee80211_channel *chan,
1100 enum nl80211_channel_type channel_type); 1104 enum nl80211_channel_type channel_type);
1101 1105
@@ -1461,6 +1465,8 @@ struct cfg80211_cached_keys;
1461 * @list: (private) Used to collect the interfaces 1465 * @list: (private) Used to collect the interfaces
1462 * @netdev: (private) Used to reference back to the netdev 1466 * @netdev: (private) Used to reference back to the netdev
1463 * @current_bss: (private) Used by the internal configuration code 1467 * @current_bss: (private) Used by the internal configuration code
1468 * @channel: (private) Used by the internal configuration code to track
1469 * user-set AP, monitor and WDS channels for wireless extensions
1464 * @bssid: (private) Used by the internal configuration code 1470 * @bssid: (private) Used by the internal configuration code
1465 * @ssid: (private) Used by the internal configuration code 1471 * @ssid: (private) Used by the internal configuration code
1466 * @ssid_len: (private) Used by the internal configuration code 1472 * @ssid_len: (private) Used by the internal configuration code
@@ -1507,6 +1513,7 @@ struct wireless_dev {
1507 struct cfg80211_internal_bss *authtry_bsses[MAX_AUTH_BSSES]; 1513 struct cfg80211_internal_bss *authtry_bsses[MAX_AUTH_BSSES];
1508 struct cfg80211_internal_bss *auth_bsses[MAX_AUTH_BSSES]; 1514 struct cfg80211_internal_bss *auth_bsses[MAX_AUTH_BSSES];
1509 struct cfg80211_internal_bss *current_bss; /* associated / joined */ 1515 struct cfg80211_internal_bss *current_bss; /* associated / joined */
1516 struct ieee80211_channel *channel;
1510 1517
1511 bool ps; 1518 bool ps;
1512 int ps_timeout; 1519 int ps_timeout;
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