diff options
author | John W. Linville <linville@tuxdriver.com> | 2010-05-11 14:24:55 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-05-11 14:24:55 -0400 |
commit | cc755896a4274f11283bca32d1d658203844057a (patch) | |
tree | 218970ece71df99f686b9416b7fd88b921690ebb /net | |
parent | d250fe91ae129bff0968e685cc9c466d3a5e3482 (diff) | |
parent | 9459d59fbf0bc82ff4c804679fa8bc22788eca63 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6 into for-davem
Conflicts:
drivers/net/wireless/ath/ar9170/main.c
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/Makefile | 3 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 58 | ||||
-rw-r--r-- | net/mac80211/chan.c | 127 | ||||
-rw-r--r-- | net/mac80211/ibss.c | 5 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 16 | ||||
-rw-r--r-- | net/mac80211/main.c | 2 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 44 | ||||
-rw-r--r-- | net/mac80211/tx.c | 5 | ||||
-rw-r--r-- | net/mac80211/util.c | 25 | ||||
-rw-r--r-- | net/wireless/chan.c | 56 | ||||
-rw-r--r-- | net/wireless/core.h | 12 | ||||
-rw-r--r-- | net/wireless/ibss.c | 5 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 171 | ||||
-rw-r--r-- | net/wireless/sme.c | 5 | ||||
-rw-r--r-- | net/wireless/wext-compat.c | 15 | ||||
-rw-r--r-- | net/wireless/wext-sme.c | 2 |
16 files changed, 412 insertions, 139 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 | ||
28 | mac80211-$(CONFIG_MAC80211_LEDS) += led.o | 29 | mac80211-$(CONFIG_MAC80211_LEDS) += led.o |
29 | mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ | 30 | mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ |
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ae37270a0633..c7000a6ca379 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -1162,15 +1162,39 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy, | |||
1162 | } | 1162 | } |
1163 | 1163 | ||
1164 | static int ieee80211_set_channel(struct wiphy *wiphy, | 1164 | static int ieee80211_set_channel(struct wiphy *wiphy, |
1165 | struct net_device *netdev, | ||
1165 | struct ieee80211_channel *chan, | 1166 | struct ieee80211_channel *chan, |
1166 | enum nl80211_channel_type channel_type) | 1167 | enum nl80211_channel_type channel_type) |
1167 | { | 1168 | { |
1168 | struct ieee80211_local *local = wiphy_priv(wiphy); | 1169 | struct ieee80211_local *local = wiphy_priv(wiphy); |
1170 | struct ieee80211_sub_if_data *sdata = NULL; | ||
1171 | |||
1172 | if (netdev) | ||
1173 | sdata = IEEE80211_DEV_TO_SUB_IF(netdev); | ||
1174 | |||
1175 | switch (ieee80211_get_channel_mode(local, NULL)) { | ||
1176 | case CHAN_MODE_HOPPING: | ||
1177 | return -EBUSY; | ||
1178 | case CHAN_MODE_FIXED: | ||
1179 | if (local->oper_channel != chan) | ||
1180 | return -EBUSY; | ||
1181 | if (!sdata && local->_oper_channel_type == channel_type) | ||
1182 | return 0; | ||
1183 | break; | ||
1184 | case CHAN_MODE_UNDEFINED: | ||
1185 | break; | ||
1186 | } | ||
1169 | 1187 | ||
1170 | local->oper_channel = chan; | 1188 | local->oper_channel = chan; |
1171 | local->oper_channel_type = channel_type; | ||
1172 | 1189 | ||
1173 | return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | 1190 | if (!ieee80211_set_channel_type(local, sdata, channel_type)) |
1191 | return -EBUSY; | ||
1192 | |||
1193 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | ||
1194 | if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR) | ||
1195 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT); | ||
1196 | |||
1197 | return 0; | ||
1174 | } | 1198 | } |
1175 | 1199 | ||
1176 | #ifdef CONFIG_PM | 1200 | #ifdef CONFIG_PM |
@@ -1214,6 +1238,20 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, | |||
1214 | static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, | 1238 | static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, |
1215 | struct cfg80211_assoc_request *req) | 1239 | struct cfg80211_assoc_request *req) |
1216 | { | 1240 | { |
1241 | struct ieee80211_local *local = wiphy_priv(wiphy); | ||
1242 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
1243 | |||
1244 | switch (ieee80211_get_channel_mode(local, sdata)) { | ||
1245 | case CHAN_MODE_HOPPING: | ||
1246 | return -EBUSY; | ||
1247 | case CHAN_MODE_FIXED: | ||
1248 | if (local->oper_channel == req->bss->channel) | ||
1249 | break; | ||
1250 | return -EBUSY; | ||
1251 | case CHAN_MODE_UNDEFINED: | ||
1252 | break; | ||
1253 | } | ||
1254 | |||
1217 | return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req); | 1255 | return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req); |
1218 | } | 1256 | } |
1219 | 1257 | ||
@@ -1236,8 +1274,22 @@ static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, | |||
1236 | static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, | 1274 | static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, |
1237 | struct cfg80211_ibss_params *params) | 1275 | struct cfg80211_ibss_params *params) |
1238 | { | 1276 | { |
1277 | struct ieee80211_local *local = wiphy_priv(wiphy); | ||
1239 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 1278 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
1240 | 1279 | ||
1280 | switch (ieee80211_get_channel_mode(local, sdata)) { | ||
1281 | case CHAN_MODE_HOPPING: | ||
1282 | return -EBUSY; | ||
1283 | case CHAN_MODE_FIXED: | ||
1284 | if (!params->channel_fixed) | ||
1285 | return -EBUSY; | ||
1286 | if (local->oper_channel == params->channel) | ||
1287 | break; | ||
1288 | return -EBUSY; | ||
1289 | case CHAN_MODE_UNDEFINED: | ||
1290 | break; | ||
1291 | } | ||
1292 | |||
1241 | return ieee80211_ibss_join(sdata, params); | 1293 | return ieee80211_ibss_join(sdata, params); |
1242 | } | 1294 | } |
1243 | 1295 | ||
@@ -1366,7 +1418,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | |||
1366 | * association, there's no need to send an action frame. | 1418 | * association, there's no need to send an action frame. |
1367 | */ | 1419 | */ |
1368 | if (!sdata->u.mgd.associated || | 1420 | if (!sdata->u.mgd.associated || |
1369 | sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) { | 1421 | sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) { |
1370 | mutex_lock(&sdata->local->iflist_mtx); | 1422 | mutex_lock(&sdata->local->iflist_mtx); |
1371 | ieee80211_recalc_smps(sdata->local, sdata); | 1423 | ieee80211_recalc_smps(sdata->local, sdata); |
1372 | mutex_unlock(&sdata->local->iflist_mtx); | 1424 | mutex_unlock(&sdata->local->iflist_mtx); |
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c new file mode 100644 index 000000000000..5d218c530a4e --- /dev/null +++ b/net/mac80211/chan.c | |||
@@ -0,0 +1,127 @@ | |||
1 | /* | ||
2 | * mac80211 - channel management | ||
3 | */ | ||
4 | |||
5 | #include <linux/nl80211.h> | ||
6 | #include "ieee80211_i.h" | ||
7 | |||
8 | enum ieee80211_chan_mode | ||
9 | __ieee80211_get_channel_mode(struct ieee80211_local *local, | ||
10 | struct ieee80211_sub_if_data *ignore) | ||
11 | { | ||
12 | struct ieee80211_sub_if_data *sdata; | ||
13 | |||
14 | WARN_ON(!mutex_is_locked(&local->iflist_mtx)); | ||
15 | |||
16 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
17 | if (sdata == ignore) | ||
18 | continue; | ||
19 | |||
20 | if (!ieee80211_sdata_running(sdata)) | ||
21 | continue; | ||
22 | |||
23 | if (sdata->vif.type == NL80211_IFTYPE_MONITOR) | ||
24 | continue; | ||
25 | |||
26 | if (sdata->vif.type == NL80211_IFTYPE_STATION && | ||
27 | !sdata->u.mgd.associated) | ||
28 | continue; | ||
29 | |||
30 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { | ||
31 | if (!sdata->u.ibss.ssid_len) | ||
32 | continue; | ||
33 | if (!sdata->u.ibss.fixed_channel) | ||
34 | return CHAN_MODE_HOPPING; | ||
35 | } | ||
36 | |||
37 | if (sdata->vif.type == NL80211_IFTYPE_AP && | ||
38 | !sdata->u.ap.beacon) | ||
39 | continue; | ||
40 | |||
41 | return CHAN_MODE_FIXED; | ||
42 | } | ||
43 | |||
44 | return CHAN_MODE_UNDEFINED; | ||
45 | } | ||
46 | |||
47 | enum ieee80211_chan_mode | ||
48 | ieee80211_get_channel_mode(struct ieee80211_local *local, | ||
49 | struct ieee80211_sub_if_data *ignore) | ||
50 | { | ||
51 | enum ieee80211_chan_mode mode; | ||
52 | |||
53 | mutex_lock(&local->iflist_mtx); | ||
54 | mode = __ieee80211_get_channel_mode(local, ignore); | ||
55 | mutex_unlock(&local->iflist_mtx); | ||
56 | |||
57 | return mode; | ||
58 | } | ||
59 | |||
60 | bool ieee80211_set_channel_type(struct ieee80211_local *local, | ||
61 | struct ieee80211_sub_if_data *sdata, | ||
62 | enum nl80211_channel_type chantype) | ||
63 | { | ||
64 | struct ieee80211_sub_if_data *tmp; | ||
65 | enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT; | ||
66 | bool result; | ||
67 | |||
68 | mutex_lock(&local->iflist_mtx); | ||
69 | |||
70 | list_for_each_entry(tmp, &local->interfaces, list) { | ||
71 | if (tmp == sdata) | ||
72 | continue; | ||
73 | |||
74 | if (!ieee80211_sdata_running(tmp)) | ||
75 | continue; | ||
76 | |||
77 | switch (tmp->vif.bss_conf.channel_type) { | ||
78 | case NL80211_CHAN_NO_HT: | ||
79 | case NL80211_CHAN_HT20: | ||
80 | superchan = tmp->vif.bss_conf.channel_type; | ||
81 | break; | ||
82 | case NL80211_CHAN_HT40PLUS: | ||
83 | WARN_ON(superchan == NL80211_CHAN_HT40MINUS); | ||
84 | superchan = NL80211_CHAN_HT40PLUS; | ||
85 | break; | ||
86 | case NL80211_CHAN_HT40MINUS: | ||
87 | WARN_ON(superchan == NL80211_CHAN_HT40PLUS); | ||
88 | superchan = NL80211_CHAN_HT40MINUS; | ||
89 | break; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | switch (superchan) { | ||
94 | case NL80211_CHAN_NO_HT: | ||
95 | case NL80211_CHAN_HT20: | ||
96 | /* | ||
97 | * allow any change that doesn't go to no-HT | ||
98 | * (if it already is no-HT no change is needed) | ||
99 | */ | ||
100 | if (chantype == NL80211_CHAN_NO_HT) | ||
101 | break; | ||
102 | superchan = chantype; | ||
103 | break; | ||
104 | case NL80211_CHAN_HT40PLUS: | ||
105 | case NL80211_CHAN_HT40MINUS: | ||
106 | /* allow smaller bandwidth and same */ | ||
107 | if (chantype == NL80211_CHAN_NO_HT) | ||
108 | break; | ||
109 | if (chantype == NL80211_CHAN_HT20) | ||
110 | break; | ||
111 | if (superchan == chantype) | ||
112 | break; | ||
113 | result = false; | ||
114 | goto out; | ||
115 | } | ||
116 | |||
117 | local->_oper_channel_type = superchan; | ||
118 | |||
119 | if (sdata) | ||
120 | sdata->vif.bss_conf.channel_type = chantype; | ||
121 | |||
122 | result = true; | ||
123 | out: | ||
124 | mutex_unlock(&local->iflist_mtx); | ||
125 | |||
126 | return result; | ||
127 | } | ||
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index b72ee6435fa3..b2cc1fda6cfd 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c | |||
@@ -103,7 +103,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
103 | sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; | 103 | sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; |
104 | 104 | ||
105 | local->oper_channel = chan; | 105 | local->oper_channel = chan; |
106 | local->oper_channel_type = NL80211_CHAN_NO_HT; | 106 | WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); |
107 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | 107 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); |
108 | 108 | ||
109 | sband = local->hw.wiphy->bands[chan->band]; | 109 | sband = local->hw.wiphy->bands[chan->band]; |
@@ -911,7 +911,8 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, | |||
911 | /* fix ourselves to that channel now already */ | 911 | /* fix ourselves to that channel now already */ |
912 | if (params->channel_fixed) { | 912 | if (params->channel_fixed) { |
913 | sdata->local->oper_channel = params->channel; | 913 | sdata->local->oper_channel = params->channel; |
914 | sdata->local->oper_channel_type = NL80211_CHAN_NO_HT; | 914 | WARN_ON(!ieee80211_set_channel_type(sdata->local, sdata, |
915 | NL80211_CHAN_NO_HT)); | ||
915 | } | 916 | } |
916 | 917 | ||
917 | if (params->ie) { | 918 | if (params->ie) { |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index cbaf4981e110..7ef7798d04cd 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -767,7 +767,7 @@ struct ieee80211_local { | |||
767 | enum mac80211_scan_state next_scan_state; | 767 | enum mac80211_scan_state next_scan_state; |
768 | struct delayed_work scan_work; | 768 | struct delayed_work scan_work; |
769 | struct ieee80211_sub_if_data *scan_sdata; | 769 | struct ieee80211_sub_if_data *scan_sdata; |
770 | enum nl80211_channel_type oper_channel_type; | 770 | enum nl80211_channel_type _oper_channel_type; |
771 | struct ieee80211_channel *oper_channel, *csa_channel; | 771 | struct ieee80211_channel *oper_channel, *csa_channel; |
772 | 772 | ||
773 | /* Temporary remain-on-channel for off-channel operations */ | 773 | /* Temporary remain-on-channel for off-channel operations */ |
@@ -1228,6 +1228,20 @@ int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, | |||
1228 | int ieee80211_wk_cancel_remain_on_channel( | 1228 | int ieee80211_wk_cancel_remain_on_channel( |
1229 | struct ieee80211_sub_if_data *sdata, u64 cookie); | 1229 | struct ieee80211_sub_if_data *sdata, u64 cookie); |
1230 | 1230 | ||
1231 | /* channel management */ | ||
1232 | enum ieee80211_chan_mode { | ||
1233 | CHAN_MODE_UNDEFINED, | ||
1234 | CHAN_MODE_HOPPING, | ||
1235 | CHAN_MODE_FIXED, | ||
1236 | }; | ||
1237 | |||
1238 | enum ieee80211_chan_mode | ||
1239 | ieee80211_get_channel_mode(struct ieee80211_local *local, | ||
1240 | struct ieee80211_sub_if_data *ignore); | ||
1241 | bool ieee80211_set_channel_type(struct ieee80211_local *local, | ||
1242 | struct ieee80211_sub_if_data *sdata, | ||
1243 | enum nl80211_channel_type chantype); | ||
1244 | |||
1231 | #ifdef CONFIG_MAC80211_NOINLINE | 1245 | #ifdef CONFIG_MAC80211_NOINLINE |
1232 | #define debug_noinline noinline | 1246 | #define debug_noinline noinline |
1233 | #else | 1247 | #else |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index bd632e1ee2c5..22a384dfab65 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -111,7 +111,7 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) | |||
111 | channel_type = local->tmp_channel_type; | 111 | channel_type = local->tmp_channel_type; |
112 | } else { | 112 | } else { |
113 | chan = local->oper_channel; | 113 | chan = local->oper_channel; |
114 | channel_type = local->oper_channel_type; | 114 | channel_type = local->_oper_channel_type; |
115 | } | 115 | } |
116 | 116 | ||
117 | if (chan != local->hw.conf.channel || | 117 | if (chan != local->hw.conf.channel || |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 358226f63b81..11783192a625 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -137,11 +137,14 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, | |||
137 | struct sta_info *sta; | 137 | struct sta_info *sta; |
138 | u32 changed = 0; | 138 | u32 changed = 0; |
139 | u16 ht_opmode; | 139 | u16 ht_opmode; |
140 | bool enable_ht = true, ht_changed; | 140 | bool enable_ht = true; |
141 | enum nl80211_channel_type prev_chantype; | ||
141 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | 142 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; |
142 | 143 | ||
143 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | 144 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; |
144 | 145 | ||
146 | prev_chantype = sdata->vif.bss_conf.channel_type; | ||
147 | |||
145 | /* HT is not supported */ | 148 | /* HT is not supported */ |
146 | if (!sband->ht_cap.ht_supported) | 149 | if (!sband->ht_cap.ht_supported) |
147 | enable_ht = false; | 150 | enable_ht = false; |
@@ -172,38 +175,37 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, | |||
172 | } | 175 | } |
173 | } | 176 | } |
174 | 177 | ||
175 | ht_changed = conf_is_ht(&local->hw.conf) != enable_ht || | ||
176 | channel_type != local->hw.conf.channel_type; | ||
177 | |||
178 | if (local->tmp_channel) | 178 | if (local->tmp_channel) |
179 | local->tmp_channel_type = channel_type; | 179 | local->tmp_channel_type = channel_type; |
180 | local->oper_channel_type = channel_type; | ||
181 | 180 | ||
182 | if (ht_changed) { | 181 | if (!ieee80211_set_channel_type(local, sdata, channel_type)) { |
183 | /* channel_type change automatically detected */ | 182 | /* can only fail due to HT40+/- mismatch */ |
184 | ieee80211_hw_config(local, 0); | 183 | channel_type = NL80211_CHAN_HT20; |
184 | WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type)); | ||
185 | } | ||
185 | 186 | ||
187 | /* channel_type change automatically detected */ | ||
188 | ieee80211_hw_config(local, 0); | ||
189 | |||
190 | if (prev_chantype != channel_type) { | ||
186 | rcu_read_lock(); | 191 | rcu_read_lock(); |
187 | sta = sta_info_get(sdata, bssid); | 192 | sta = sta_info_get(sdata, bssid); |
188 | if (sta) | 193 | if (sta) |
189 | rate_control_rate_update(local, sband, sta, | 194 | rate_control_rate_update(local, sband, sta, |
190 | IEEE80211_RC_HT_CHANGED, | 195 | IEEE80211_RC_HT_CHANGED, |
191 | local->oper_channel_type); | 196 | channel_type); |
192 | rcu_read_unlock(); | 197 | rcu_read_unlock(); |
193 | } | 198 | } |
194 | |||
195 | /* disable HT */ | ||
196 | if (!enable_ht) | ||
197 | return 0; | ||
198 | 199 | ||
199 | ht_opmode = le16_to_cpu(hti->operation_mode); | 200 | ht_opmode = le16_to_cpu(hti->operation_mode); |
200 | 201 | ||
201 | /* if bss configuration changed store the new one */ | 202 | /* if bss configuration changed store the new one */ |
202 | if (!sdata->ht_opmode_valid || | 203 | if (sdata->ht_opmode_valid != enable_ht || |
203 | sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { | 204 | sdata->vif.bss_conf.ht_operation_mode != ht_opmode || |
205 | prev_chantype != channel_type) { | ||
204 | changed |= BSS_CHANGED_HT; | 206 | changed |= BSS_CHANGED_HT; |
205 | sdata->vif.bss_conf.ht_operation_mode = ht_opmode; | 207 | sdata->vif.bss_conf.ht_operation_mode = ht_opmode; |
206 | sdata->ht_opmode_valid = true; | 208 | sdata->ht_opmode_valid = enable_ht; |
207 | } | 209 | } |
208 | 210 | ||
209 | return changed; | 211 | return changed; |
@@ -866,7 +868,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, | |||
866 | ieee80211_set_wmm_default(sdata); | 868 | ieee80211_set_wmm_default(sdata); |
867 | 869 | ||
868 | /* channel(_type) changes are handled by ieee80211_hw_config */ | 870 | /* channel(_type) changes are handled by ieee80211_hw_config */ |
869 | local->oper_channel_type = NL80211_CHAN_NO_HT; | 871 | WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); |
870 | 872 | ||
871 | /* on the next assoc, re-program HT parameters */ | 873 | /* on the next assoc, re-program HT parameters */ |
872 | sdata->ht_opmode_valid = false; | 874 | sdata->ht_opmode_valid = false; |
@@ -883,8 +885,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, | |||
883 | 885 | ||
884 | ieee80211_hw_config(local, config_changed); | 886 | ieee80211_hw_config(local, config_changed); |
885 | 887 | ||
886 | /* And the BSSID changed -- not very interesting here */ | 888 | /* The BSSID (not really interesting) and HT changed */ |
887 | changed |= BSS_CHANGED_BSSID; | 889 | changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; |
888 | ieee80211_bss_info_change_notify(sdata, changed); | 890 | ieee80211_bss_info_change_notify(sdata, changed); |
889 | 891 | ||
890 | if (remove_sta) | 892 | if (remove_sta) |
@@ -2266,7 +2268,7 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata, | |||
2266 | if ((chan != local->tmp_channel || | 2268 | if ((chan != local->tmp_channel || |
2267 | channel_type != local->tmp_channel_type) && | 2269 | channel_type != local->tmp_channel_type) && |
2268 | (chan != local->oper_channel || | 2270 | (chan != local->oper_channel || |
2269 | channel_type != local->oper_channel_type)) | 2271 | channel_type != local->_oper_channel_type)) |
2270 | return -EBUSY; | 2272 | return -EBUSY; |
2271 | 2273 | ||
2272 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); | 2274 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index f3841f43249e..680bcb7093db 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -2251,8 +2251,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, | |||
2251 | 2251 | ||
2252 | info->control.vif = vif; | 2252 | info->control.vif = vif; |
2253 | 2253 | ||
2254 | info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; | 2254 | info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT | |
2255 | info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; | 2255 | IEEE80211_TX_CTL_ASSIGN_SEQ | |
2256 | IEEE80211_TX_CTL_FIRST_FRAGMENT; | ||
2256 | out: | 2257 | out: |
2257 | rcu_read_unlock(); | 2258 | rcu_read_unlock(); |
2258 | return skb; | 2259 | return skb; |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 2b75b4fb68f4..5b79d552780a 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -1160,18 +1160,33 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1160 | 1160 | ||
1161 | /* Finally also reconfigure all the BSS information */ | 1161 | /* Finally also reconfigure all the BSS information */ |
1162 | list_for_each_entry(sdata, &local->interfaces, list) { | 1162 | list_for_each_entry(sdata, &local->interfaces, list) { |
1163 | u32 changed = ~0; | 1163 | u32 changed; |
1164 | |||
1164 | if (!ieee80211_sdata_running(sdata)) | 1165 | if (!ieee80211_sdata_running(sdata)) |
1165 | continue; | 1166 | continue; |
1167 | |||
1168 | /* common change flags for all interface types */ | ||
1169 | changed = BSS_CHANGED_ERP_CTS_PROT | | ||
1170 | BSS_CHANGED_ERP_PREAMBLE | | ||
1171 | BSS_CHANGED_ERP_SLOT | | ||
1172 | BSS_CHANGED_HT | | ||
1173 | BSS_CHANGED_BASIC_RATES | | ||
1174 | BSS_CHANGED_BEACON_INT | | ||
1175 | BSS_CHANGED_BSSID | | ||
1176 | BSS_CHANGED_CQM; | ||
1177 | |||
1166 | switch (sdata->vif.type) { | 1178 | switch (sdata->vif.type) { |
1167 | case NL80211_IFTYPE_STATION: | 1179 | case NL80211_IFTYPE_STATION: |
1168 | /* disable beacon change bits */ | 1180 | changed |= BSS_CHANGED_ASSOC; |
1169 | changed &= ~(BSS_CHANGED_BEACON | | 1181 | ieee80211_bss_info_change_notify(sdata, changed); |
1170 | BSS_CHANGED_BEACON_ENABLED); | 1182 | break; |
1171 | /* fall through */ | ||
1172 | case NL80211_IFTYPE_ADHOC: | 1183 | case NL80211_IFTYPE_ADHOC: |
1184 | changed |= BSS_CHANGED_IBSS; | ||
1185 | /* fall through */ | ||
1173 | case NL80211_IFTYPE_AP: | 1186 | case NL80211_IFTYPE_AP: |
1174 | case NL80211_IFTYPE_MESH_POINT: | 1187 | case NL80211_IFTYPE_MESH_POINT: |
1188 | changed |= BSS_CHANGED_BEACON | | ||
1189 | BSS_CHANGED_BEACON_ENABLED; | ||
1175 | ieee80211_bss_info_change_notify(sdata, changed); | 1190 | ieee80211_bss_info_change_notify(sdata, changed); |
1176 | break; | 1191 | break; |
1177 | case NL80211_IFTYPE_WDS: | 1192 | case NL80211_IFTYPE_WDS: |
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 | ||
12 | struct ieee80211_channel * | 12 | struct ieee80211_channel * |
13 | rdev_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 | |||
44 | struct ieee80211_channel * | ||
45 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, | 13 | rdev_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 | ||
78 | int rdev_set_freq(struct cfg80211_registered_device *rdev, | 46 | int 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, | |||
388 | void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); | 385 | void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); |
389 | 386 | ||
390 | struct ieee80211_channel * | 387 | struct ieee80211_channel * |
391 | rdev_fixed_channel(struct cfg80211_registered_device *rdev, | ||
392 | struct wireless_dev *for_wdev); | ||
393 | struct ieee80211_channel * | ||
394 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, | 388 | rdev_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); |
396 | int rdev_set_freq(struct cfg80211_registered_device *rdev, | 390 | int 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 | ||
400 | u16 cfg80211_calculate_bitrate(struct rate_info *rate); | 394 | u16 cfg80211_calculate_bitrate(struct rate_info *rate); |
401 | 395 | ||
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 6a5acf750174..adcabba02e20 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c | |||
@@ -81,15 +81,10 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, | |||
81 | struct cfg80211_cached_keys *connkeys) | 81 | struct cfg80211_cached_keys *connkeys) |
82 | { | 82 | { |
83 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 83 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
84 | struct ieee80211_channel *chan; | ||
85 | int err; | 84 | int err; |
86 | 85 | ||
87 | ASSERT_WDEV_LOCK(wdev); | 86 | ASSERT_WDEV_LOCK(wdev); |
88 | 87 | ||
89 | chan = rdev_fixed_channel(rdev, wdev); | ||
90 | if (chan && chan != params->channel) | ||
91 | return -EBUSY; | ||
92 | |||
93 | if (wdev->ssid_len) | 88 | if (wdev->ssid_len) |
94 | return -EALREADY; | 89 | return -EALREADY; |
95 | 90 | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 01da83ddcff7..aaa1aad566cd 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -589,6 +589,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
589 | i++; | 589 | i++; |
590 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 590 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
591 | } | 591 | } |
592 | CMD(set_channel, SET_CHANNEL); | ||
592 | 593 | ||
593 | #undef CMD | 594 | #undef CMD |
594 | 595 | ||
@@ -689,10 +690,90 @@ static int parse_txq_params(struct nlattr *tb[], | |||
689 | return 0; | 690 | return 0; |
690 | } | 691 | } |
691 | 692 | ||
693 | static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) | ||
694 | { | ||
695 | /* | ||
696 | * You can only set the channel explicitly for AP, mesh | ||
697 | * and WDS type interfaces; all others have their channel | ||
698 | * managed via their respective "establish a connection" | ||
699 | * command (connect, join, ...) | ||
700 | * | ||
701 | * Monitors are special as they are normally slaved to | ||
702 | * whatever else is going on, so they behave as though | ||
703 | * you tried setting the wiphy channel itself. | ||
704 | */ | ||
705 | return !wdev || | ||
706 | wdev->iftype == NL80211_IFTYPE_AP || | ||
707 | wdev->iftype == NL80211_IFTYPE_WDS || | ||
708 | wdev->iftype == NL80211_IFTYPE_MESH_POINT || | ||
709 | wdev->iftype == NL80211_IFTYPE_MONITOR; | ||
710 | } | ||
711 | |||
712 | static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, | ||
713 | struct wireless_dev *wdev, | ||
714 | struct genl_info *info) | ||
715 | { | ||
716 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
717 | u32 freq; | ||
718 | int result; | ||
719 | |||
720 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) | ||
721 | return -EINVAL; | ||
722 | |||
723 | if (!nl80211_can_set_dev_channel(wdev)) | ||
724 | return -EOPNOTSUPP; | ||
725 | |||
726 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
727 | channel_type = nla_get_u32(info->attrs[ | ||
728 | NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
729 | if (channel_type != NL80211_CHAN_NO_HT && | ||
730 | channel_type != NL80211_CHAN_HT20 && | ||
731 | channel_type != NL80211_CHAN_HT40PLUS && | ||
732 | channel_type != NL80211_CHAN_HT40MINUS) | ||
733 | return -EINVAL; | ||
734 | } | ||
735 | |||
736 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
737 | |||
738 | mutex_lock(&rdev->devlist_mtx); | ||
739 | if (wdev) { | ||
740 | wdev_lock(wdev); | ||
741 | result = cfg80211_set_freq(rdev, wdev, freq, channel_type); | ||
742 | wdev_unlock(wdev); | ||
743 | } else { | ||
744 | result = cfg80211_set_freq(rdev, NULL, freq, channel_type); | ||
745 | } | ||
746 | mutex_unlock(&rdev->devlist_mtx); | ||
747 | |||
748 | return result; | ||
749 | } | ||
750 | |||
751 | static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info) | ||
752 | { | ||
753 | struct cfg80211_registered_device *rdev; | ||
754 | struct net_device *netdev; | ||
755 | int result; | ||
756 | |||
757 | rtnl_lock(); | ||
758 | |||
759 | result = get_rdev_dev_by_info_ifindex(info, &rdev, &netdev); | ||
760 | if (result) | ||
761 | goto unlock; | ||
762 | |||
763 | result = __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info); | ||
764 | |||
765 | unlock: | ||
766 | rtnl_unlock(); | ||
767 | |||
768 | return result; | ||
769 | } | ||
770 | |||
692 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | 771 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) |
693 | { | 772 | { |
694 | struct cfg80211_registered_device *rdev; | 773 | struct cfg80211_registered_device *rdev; |
695 | int result = 0, rem_txq_params = 0; | 774 | struct net_device *netdev = NULL; |
775 | struct wireless_dev *wdev; | ||
776 | int result, rem_txq_params = 0; | ||
696 | struct nlattr *nl_txq_params; | 777 | struct nlattr *nl_txq_params; |
697 | u32 changed; | 778 | u32 changed; |
698 | u8 retry_short = 0, retry_long = 0; | 779 | u8 retry_short = 0, retry_long = 0; |
@@ -701,16 +782,50 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
701 | 782 | ||
702 | rtnl_lock(); | 783 | rtnl_lock(); |
703 | 784 | ||
785 | /* | ||
786 | * Try to find the wiphy and netdev. Normally this | ||
787 | * function shouldn't need the netdev, but this is | ||
788 | * done for backward compatibility -- previously | ||
789 | * setting the channel was done per wiphy, but now | ||
790 | * it is per netdev. Previous userland like hostapd | ||
791 | * also passed a netdev to set_wiphy, so that it is | ||
792 | * possible to let that go to the right netdev! | ||
793 | */ | ||
704 | mutex_lock(&cfg80211_mutex); | 794 | mutex_lock(&cfg80211_mutex); |
705 | 795 | ||
706 | rdev = __cfg80211_rdev_from_info(info); | 796 | if (info->attrs[NL80211_ATTR_IFINDEX]) { |
707 | if (IS_ERR(rdev)) { | 797 | int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); |
708 | mutex_unlock(&cfg80211_mutex); | 798 | |
709 | result = PTR_ERR(rdev); | 799 | netdev = dev_get_by_index(genl_info_net(info), ifindex); |
710 | goto unlock; | 800 | if (netdev && netdev->ieee80211_ptr) { |
801 | rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy); | ||
802 | mutex_lock(&rdev->mtx); | ||
803 | } else | ||
804 | netdev = NULL; | ||
711 | } | 805 | } |
712 | 806 | ||
713 | mutex_lock(&rdev->mtx); | 807 | if (!netdev) { |
808 | rdev = __cfg80211_rdev_from_info(info); | ||
809 | if (IS_ERR(rdev)) { | ||
810 | mutex_unlock(&cfg80211_mutex); | ||
811 | result = PTR_ERR(rdev); | ||
812 | goto unlock; | ||
813 | } | ||
814 | wdev = NULL; | ||
815 | netdev = NULL; | ||
816 | result = 0; | ||
817 | |||
818 | mutex_lock(&rdev->mtx); | ||
819 | } else if (netif_running(netdev) && | ||
820 | nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) | ||
821 | wdev = netdev->ieee80211_ptr; | ||
822 | else | ||
823 | wdev = NULL; | ||
824 | |||
825 | /* | ||
826 | * end workaround code, by now the rdev is available | ||
827 | * and locked, and wdev may or may not be NULL. | ||
828 | */ | ||
714 | 829 | ||
715 | if (info->attrs[NL80211_ATTR_WIPHY_NAME]) | 830 | if (info->attrs[NL80211_ATTR_WIPHY_NAME]) |
716 | result = cfg80211_dev_rename( | 831 | result = cfg80211_dev_rename( |
@@ -749,26 +864,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
749 | } | 864 | } |
750 | 865 | ||
751 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | 866 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { |
752 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | 867 | result = __nl80211_set_channel(rdev, wdev, info); |
753 | u32 freq; | ||
754 | |||
755 | result = -EINVAL; | ||
756 | |||
757 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
758 | channel_type = nla_get_u32(info->attrs[ | ||
759 | NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
760 | if (channel_type != NL80211_CHAN_NO_HT && | ||
761 | channel_type != NL80211_CHAN_HT20 && | ||
762 | channel_type != NL80211_CHAN_HT40PLUS && | ||
763 | channel_type != NL80211_CHAN_HT40MINUS) | ||
764 | goto bad_res; | ||
765 | } | ||
766 | |||
767 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
768 | |||
769 | mutex_lock(&rdev->devlist_mtx); | ||
770 | result = rdev_set_freq(rdev, NULL, freq, channel_type); | ||
771 | mutex_unlock(&rdev->devlist_mtx); | ||
772 | if (result) | 868 | if (result) |
773 | goto bad_res; | 869 | goto bad_res; |
774 | } | 870 | } |
@@ -865,6 +961,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
865 | 961 | ||
866 | bad_res: | 962 | bad_res: |
867 | mutex_unlock(&rdev->mtx); | 963 | mutex_unlock(&rdev->mtx); |
964 | if (netdev) | ||
965 | dev_put(netdev); | ||
868 | unlock: | 966 | unlock: |
869 | rtnl_unlock(); | 967 | rtnl_unlock(); |
870 | return result; | 968 | return result; |
@@ -3562,9 +3660,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
3562 | { | 3660 | { |
3563 | struct cfg80211_registered_device *rdev; | 3661 | struct cfg80211_registered_device *rdev; |
3564 | struct net_device *dev; | 3662 | struct net_device *dev; |
3565 | struct wireless_dev *wdev; | ||
3566 | struct cfg80211_crypto_settings crypto; | 3663 | struct cfg80211_crypto_settings crypto; |
3567 | struct ieee80211_channel *chan, *fixedchan; | 3664 | struct ieee80211_channel *chan; |
3568 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; | 3665 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; |
3569 | int err, ssid_len, ie_len = 0; | 3666 | int err, ssid_len, ie_len = 0; |
3570 | bool use_mfp = false; | 3667 | bool use_mfp = false; |
@@ -3607,16 +3704,6 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
3607 | goto out; | 3704 | goto out; |
3608 | } | 3705 | } |
3609 | 3706 | ||
3610 | mutex_lock(&rdev->devlist_mtx); | ||
3611 | wdev = dev->ieee80211_ptr; | ||
3612 | fixedchan = rdev_fixed_channel(rdev, wdev); | ||
3613 | if (fixedchan && chan != fixedchan) { | ||
3614 | err = -EBUSY; | ||
3615 | mutex_unlock(&rdev->devlist_mtx); | ||
3616 | goto out; | ||
3617 | } | ||
3618 | mutex_unlock(&rdev->devlist_mtx); | ||
3619 | |||
3620 | ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); | 3707 | ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); |
3621 | ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); | 3708 | ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); |
3622 | 3709 | ||
@@ -5186,6 +5273,12 @@ static struct genl_ops nl80211_ops[] = { | |||
5186 | .policy = nl80211_policy, | 5273 | .policy = nl80211_policy, |
5187 | .flags = GENL_ADMIN_PERM, | 5274 | .flags = GENL_ADMIN_PERM, |
5188 | }, | 5275 | }, |
5276 | { | ||
5277 | .cmd = NL80211_CMD_SET_CHANNEL, | ||
5278 | .doit = nl80211_set_channel, | ||
5279 | .policy = nl80211_policy, | ||
5280 | .flags = GENL_ADMIN_PERM, | ||
5281 | }, | ||
5189 | }; | 5282 | }; |
5190 | 5283 | ||
5191 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5284 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 8ddf5ae0dd03..72222f0074db 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -741,7 +741,6 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
741 | const u8 *prev_bssid) | 741 | const u8 *prev_bssid) |
742 | { | 742 | { |
743 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 743 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
744 | struct ieee80211_channel *chan; | ||
745 | struct cfg80211_bss *bss = NULL; | 744 | struct cfg80211_bss *bss = NULL; |
746 | int err; | 745 | int err; |
747 | 746 | ||
@@ -750,10 +749,6 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
750 | if (wdev->sme_state != CFG80211_SME_IDLE) | 749 | if (wdev->sme_state != CFG80211_SME_IDLE) |
751 | return -EALREADY; | 750 | return -EALREADY; |
752 | 751 | ||
753 | chan = rdev_fixed_channel(rdev, wdev); | ||
754 | if (chan && chan != connect->channel) | ||
755 | return -EBUSY; | ||
756 | |||
757 | if (WARN_ON(wdev->connect_keys)) { | 752 | if (WARN_ON(wdev->connect_keys)) { |
758 | kfree(wdev->connect_keys); | 753 | kfree(wdev->connect_keys); |
759 | wdev->connect_keys = NULL; | 754 | wdev->connect_keys = NULL; |
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index a60a2773b497..96342993cf93 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c | |||
@@ -782,16 +782,22 @@ int cfg80211_wext_siwfreq(struct net_device *dev, | |||
782 | return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra); | 782 | return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra); |
783 | case NL80211_IFTYPE_ADHOC: | 783 | case NL80211_IFTYPE_ADHOC: |
784 | return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); | 784 | return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); |
785 | default: | 785 | case NL80211_IFTYPE_MONITOR: |
786 | case NL80211_IFTYPE_WDS: | ||
787 | case NL80211_IFTYPE_MESH_POINT: | ||
786 | freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); | 788 | freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); |
787 | if (freq < 0) | 789 | if (freq < 0) |
788 | return freq; | 790 | return freq; |
789 | if (freq == 0) | 791 | if (freq == 0) |
790 | return -EINVAL; | 792 | return -EINVAL; |
793 | wdev_lock(wdev); | ||
791 | mutex_lock(&rdev->devlist_mtx); | 794 | mutex_lock(&rdev->devlist_mtx); |
792 | err = rdev_set_freq(rdev, NULL, freq, NL80211_CHAN_NO_HT); | 795 | err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); |
793 | mutex_unlock(&rdev->devlist_mtx); | 796 | mutex_unlock(&rdev->devlist_mtx); |
797 | wdev_unlock(wdev); | ||
794 | return err; | 798 | return err; |
799 | default: | ||
800 | return -EOPNOTSUPP; | ||
795 | } | 801 | } |
796 | } | 802 | } |
797 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq); | 803 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq); |
@@ -801,7 +807,6 @@ int cfg80211_wext_giwfreq(struct net_device *dev, | |||
801 | struct iw_freq *freq, char *extra) | 807 | struct iw_freq *freq, char *extra) |
802 | { | 808 | { |
803 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 809 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
804 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | ||
805 | 810 | ||
806 | switch (wdev->iftype) { | 811 | switch (wdev->iftype) { |
807 | case NL80211_IFTYPE_STATION: | 812 | case NL80211_IFTYPE_STATION: |
@@ -809,9 +814,9 @@ int cfg80211_wext_giwfreq(struct net_device *dev, | |||
809 | case NL80211_IFTYPE_ADHOC: | 814 | case NL80211_IFTYPE_ADHOC: |
810 | return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); | 815 | return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); |
811 | default: | 816 | default: |
812 | if (!rdev->channel) | 817 | if (!wdev->channel) |
813 | return -EINVAL; | 818 | return -EINVAL; |
814 | freq->m = rdev->channel->center_freq; | 819 | freq->m = wdev->channel->center_freq; |
815 | freq->e = 6; | 820 | freq->e = 6; |
816 | return 0; | 821 | return 0; |
817 | } | 822 | } |
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index d5c6140f4cb8..9818198add8a 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c | |||
@@ -108,7 +108,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, | |||
108 | 108 | ||
109 | /* SSID is not set, we just want to switch channel */ | 109 | /* SSID is not set, we just want to switch channel */ |
110 | if (chan && !wdev->wext.connect.ssid_len) { | 110 | if (chan && !wdev->wext.connect.ssid_len) { |
111 | err = rdev_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); | 111 | err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); |
112 | goto out; | 112 | goto out; |
113 | } | 113 | } |
114 | 114 | ||