aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorPaul Stewart <pstew@chromium.org>2012-03-13 10:46:18 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-03-13 14:55:53 -0400
commit3117bbdb7899d43927c8ce4fe885ab7c1231c121 (patch)
treed2fc142e77a9d90d9054f45e666457c901bd8975 /net/mac80211
parente9ac0745c734d39cb55ce45f1fb03a85c972b35a (diff)
mac80211: Don't let regulatory make us deaf
When regulatory information changes our HT behavior (e.g, when we get a country code from the AP we have just associated with), we should use this information to change the power with which we transmit, and what channels we transmit. Sometimes the channel parameters we derive from regulatory information contradicts the parameters we used in association. For example, we could have associated specifying HT40, but the regulatory rules we apply may forbid HT40 operation. In the situation above, we should reconfigure ourselves to transmit in HT20 only, however it makes no sense for us to disable receive in HT40, since if we associated with these parameters, the AP has every reason to expect we can and will receive packets this way. The code in mac80211 does not have the capability of sending the appropriate action frames to signal a change in HT behaviour so the AP has no clue we can no longer receive frames encoded this way. In some broken AP implementations, this can leave us effectively deaf if the AP never retries in lower HT rates. This change breaks up the channel_type parameter in the ieee80211_enable_ht function into a separate receive and transmit part. It honors the channel flags set by regulatory in order to configure the rate control algorithm, but uses the capability flags to configure the channel on the radio, since these were used in association to set the AP's transmit rate. Signed-off-by: Paul Stewart <pstew@chromium.org> Cc: Sam Leffler <sleffler@chromium.org> Cc: Johannes Berg <johannes@sipsolutions.net> Reviewed-by: Luis R Rodriguez <mcgrof@frijolero.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/chan.c27
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/mlme.c32
-rw-r--r--net/mac80211/rx.c8
4 files changed, 51 insertions, 19 deletions
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index d1f7abddb182..e00ce8c3e28e 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -3,6 +3,7 @@
3 */ 3 */
4 4
5#include <linux/nl80211.h> 5#include <linux/nl80211.h>
6#include <net/cfg80211.h>
6#include "ieee80211_i.h" 7#include "ieee80211_i.h"
7 8
8static enum ieee80211_chan_mode 9static enum ieee80211_chan_mode
@@ -134,3 +135,29 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
134 135
135 return result; 136 return result;
136} 137}
138
139/*
140 * ieee80211_get_tx_channel_type returns the channel type we should
141 * use for packet transmission, given the channel capability and
142 * whatever regulatory flags we have been given.
143 */
144enum nl80211_channel_type ieee80211_get_tx_channel_type(
145 struct ieee80211_local *local,
146 enum nl80211_channel_type channel_type)
147{
148 switch (channel_type) {
149 case NL80211_CHAN_HT40PLUS:
150 if (local->hw.conf.channel->flags &
151 IEEE80211_CHAN_NO_HT40PLUS)
152 return NL80211_CHAN_HT20;
153 break;
154 case NL80211_CHAN_HT40MINUS:
155 if (local->hw.conf.channel->flags &
156 IEEE80211_CHAN_NO_HT40MINUS)
157 return NL80211_CHAN_HT20;
158 break;
159 default:
160 break;
161 }
162 return channel_type;
163}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 173c1396070d..63fb0eb79d8e 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1502,6 +1502,9 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
1502 enum nl80211_channel_type chantype); 1502 enum nl80211_channel_type chantype);
1503enum nl80211_channel_type 1503enum nl80211_channel_type
1504ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info); 1504ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info);
1505enum nl80211_channel_type ieee80211_get_tx_channel_type(
1506 struct ieee80211_local *local,
1507 enum nl80211_channel_type channel_type);
1505 1508
1506#ifdef CONFIG_MAC80211_NOINLINE 1509#ifdef CONFIG_MAC80211_NOINLINE
1507#define debug_noinline noinline 1510#define debug_noinline noinline
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 90d1db36cdef..0df22372af8d 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -189,7 +189,8 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
189 u16 ht_opmode; 189 u16 ht_opmode;
190 bool enable_ht = true; 190 bool enable_ht = true;
191 enum nl80211_channel_type prev_chantype; 191 enum nl80211_channel_type prev_chantype;
192 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; 192 enum nl80211_channel_type rx_channel_type = NL80211_CHAN_NO_HT;
193 enum nl80211_channel_type tx_channel_type;
193 194
194 sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; 195 sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
195 prev_chantype = sdata->vif.bss_conf.channel_type; 196 prev_chantype = sdata->vif.bss_conf.channel_type;
@@ -216,7 +217,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
216 } 217 }
217 218
218 if (enable_ht) { 219 if (enable_ht) {
219 channel_type = NL80211_CHAN_HT20; 220 rx_channel_type = NL80211_CHAN_HT20;
220 221
221 if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) && 222 if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
222 !ieee80111_cfg_override_disables_ht40(sdata) && 223 !ieee80111_cfg_override_disables_ht40(sdata) &&
@@ -224,29 +225,28 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
224 (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) { 225 (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
225 switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { 226 switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
226 case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 227 case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
227 if (!(local->hw.conf.channel->flags & 228 rx_channel_type = NL80211_CHAN_HT40PLUS;
228 IEEE80211_CHAN_NO_HT40PLUS))
229 channel_type = NL80211_CHAN_HT40PLUS;
230 break; 229 break;
231 case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 230 case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
232 if (!(local->hw.conf.channel->flags & 231 rx_channel_type = NL80211_CHAN_HT40MINUS;
233 IEEE80211_CHAN_NO_HT40MINUS))
234 channel_type = NL80211_CHAN_HT40MINUS;
235 break; 232 break;
236 } 233 }
237 } 234 }
238 } 235 }
239 236
237 tx_channel_type = ieee80211_get_tx_channel_type(local, rx_channel_type);
238
240 if (local->tmp_channel) 239 if (local->tmp_channel)
241 local->tmp_channel_type = channel_type; 240 local->tmp_channel_type = rx_channel_type;
242 241
243 if (!ieee80211_set_channel_type(local, sdata, channel_type)) { 242 if (!ieee80211_set_channel_type(local, sdata, rx_channel_type)) {
244 /* can only fail due to HT40+/- mismatch */ 243 /* can only fail due to HT40+/- mismatch */
245 channel_type = NL80211_CHAN_HT20; 244 rx_channel_type = NL80211_CHAN_HT20;
246 WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type)); 245 WARN_ON(!ieee80211_set_channel_type(local, sdata,
246 rx_channel_type));
247 } 247 }
248 248
249 if (beacon_htcap_ie && (prev_chantype != channel_type)) { 249 if (beacon_htcap_ie && (prev_chantype != rx_channel_type)) {
250 /* 250 /*
251 * Whenever the AP announces the HT mode change that can be 251 * Whenever the AP announces the HT mode change that can be
252 * 40MHz intolerant or etc., it would be safer to stop tx 252 * 40MHz intolerant or etc., it would be safer to stop tx
@@ -264,13 +264,13 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
264 /* channel_type change automatically detected */ 264 /* channel_type change automatically detected */
265 ieee80211_hw_config(local, 0); 265 ieee80211_hw_config(local, 0);
266 266
267 if (prev_chantype != channel_type) { 267 if (prev_chantype != tx_channel_type) {
268 rcu_read_lock(); 268 rcu_read_lock();
269 sta = sta_info_get(sdata, bssid); 269 sta = sta_info_get(sdata, bssid);
270 if (sta) 270 if (sta)
271 rate_control_rate_update(local, sband, sta, 271 rate_control_rate_update(local, sband, sta,
272 IEEE80211_RC_HT_CHANGED, 272 IEEE80211_RC_HT_CHANGED,
273 channel_type); 273 tx_channel_type);
274 rcu_read_unlock(); 274 rcu_read_unlock();
275 275
276 if (beacon_htcap_ie) 276 if (beacon_htcap_ie)
@@ -283,7 +283,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
283 /* if bss configuration changed store the new one */ 283 /* if bss configuration changed store the new one */
284 if (sdata->ht_opmode_valid != enable_ht || 284 if (sdata->ht_opmode_valid != enable_ht ||
285 sdata->vif.bss_conf.ht_operation_mode != ht_opmode || 285 sdata->vif.bss_conf.ht_operation_mode != ht_opmode ||
286 prev_chantype != channel_type) { 286 prev_chantype != rx_channel_type) {
287 changed |= BSS_CHANGED_HT; 287 changed |= BSS_CHANGED_HT;
288 sdata->vif.bss_conf.ht_operation_mode = ht_opmode; 288 sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
289 sdata->ht_opmode_valid = enable_ht; 289 sdata->ht_opmode_valid = enable_ht;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 53c88d145472..bcfe8c77c839 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2269,9 +2269,11 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
2269 2269
2270 sband = rx->local->hw.wiphy->bands[status->band]; 2270 sband = rx->local->hw.wiphy->bands[status->band];
2271 2271
2272 rate_control_rate_update(local, sband, rx->sta, 2272 rate_control_rate_update(
2273 IEEE80211_RC_SMPS_CHANGED, 2273 local, sband, rx->sta,
2274 local->_oper_channel_type); 2274 IEEE80211_RC_SMPS_CHANGED,
2275 ieee80211_get_tx_channel_type(
2276 local, local->_oper_channel_type));
2275 goto handled; 2277 goto handled;
2276 } 2278 }
2277 default: 2279 default: