diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2010-05-05 09:28:27 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-05-07 14:55:51 -0400 |
commit | 0aaffa9b9699894aab3266195a529baf9f96ac29 (patch) | |
tree | 26fe5f5277ac6d7061ea723f92d4038b0c28b0b8 /net/mac80211/mlme.c | |
parent | f444de05d20e27cdd960c13fcbcfca3099f03143 (diff) |
mac80211: improve HT channel handling
Currently, when one interface switches HT mode,
all others will follow along. This is clearly
undesirable, since the new one might switch to
no-HT while another one is operating in HT.
Address this issue by keeping track of the HT
mode per interface, and allowing only changes
that are compatible, i.e. switching into HT40+
is not possible when another interface is in
HT40-, in that case the second one needs to
fall back to HT20.
Also, to allow drivers to know what's going on,
store the per-interface HT mode (channel type)
in the virtual interface's bss_conf.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r-- | net/mac80211/mlme.c | 44 |
1 files changed, 23 insertions, 21 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 17cb8ae912bc..6e149b49d4f0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -136,11 +136,14 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, | |||
136 | struct sta_info *sta; | 136 | struct sta_info *sta; |
137 | u32 changed = 0; | 137 | u32 changed = 0; |
138 | u16 ht_opmode; | 138 | u16 ht_opmode; |
139 | bool enable_ht = true, ht_changed; | 139 | bool enable_ht = true; |
140 | enum nl80211_channel_type prev_chantype; | ||
140 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | 141 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; |
141 | 142 | ||
142 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | 143 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; |
143 | 144 | ||
145 | prev_chantype = sdata->vif.bss_conf.channel_type; | ||
146 | |||
144 | /* HT is not supported */ | 147 | /* HT is not supported */ |
145 | if (!sband->ht_cap.ht_supported) | 148 | if (!sband->ht_cap.ht_supported) |
146 | enable_ht = false; | 149 | enable_ht = false; |
@@ -171,38 +174,37 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, | |||
171 | } | 174 | } |
172 | } | 175 | } |
173 | 176 | ||
174 | ht_changed = conf_is_ht(&local->hw.conf) != enable_ht || | ||
175 | channel_type != local->hw.conf.channel_type; | ||
176 | |||
177 | if (local->tmp_channel) | 177 | if (local->tmp_channel) |
178 | local->tmp_channel_type = channel_type; | 178 | local->tmp_channel_type = channel_type; |
179 | local->oper_channel_type = channel_type; | ||
180 | 179 | ||
181 | if (ht_changed) { | 180 | if (!ieee80211_set_channel_type(local, sdata, channel_type)) { |
182 | /* channel_type change automatically detected */ | 181 | /* can only fail due to HT40+/- mismatch */ |
183 | ieee80211_hw_config(local, 0); | 182 | channel_type = NL80211_CHAN_HT20; |
183 | WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type)); | ||
184 | } | ||
184 | 185 | ||
186 | /* channel_type change automatically detected */ | ||
187 | ieee80211_hw_config(local, 0); | ||
188 | |||
189 | if (prev_chantype != channel_type) { | ||
185 | rcu_read_lock(); | 190 | rcu_read_lock(); |
186 | sta = sta_info_get(sdata, bssid); | 191 | sta = sta_info_get(sdata, bssid); |
187 | if (sta) | 192 | if (sta) |
188 | rate_control_rate_update(local, sband, sta, | 193 | rate_control_rate_update(local, sband, sta, |
189 | IEEE80211_RC_HT_CHANGED, | 194 | IEEE80211_RC_HT_CHANGED, |
190 | local->oper_channel_type); | 195 | channel_type); |
191 | rcu_read_unlock(); | 196 | rcu_read_unlock(); |
192 | } | 197 | } |
193 | |||
194 | /* disable HT */ | ||
195 | if (!enable_ht) | ||
196 | return 0; | ||
197 | 198 | ||
198 | ht_opmode = le16_to_cpu(hti->operation_mode); | 199 | ht_opmode = le16_to_cpu(hti->operation_mode); |
199 | 200 | ||
200 | /* if bss configuration changed store the new one */ | 201 | /* if bss configuration changed store the new one */ |
201 | if (!sdata->ht_opmode_valid || | 202 | if (sdata->ht_opmode_valid != enable_ht || |
202 | sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { | 203 | sdata->vif.bss_conf.ht_operation_mode != ht_opmode || |
204 | prev_chantype != channel_type) { | ||
203 | changed |= BSS_CHANGED_HT; | 205 | changed |= BSS_CHANGED_HT; |
204 | sdata->vif.bss_conf.ht_operation_mode = ht_opmode; | 206 | sdata->vif.bss_conf.ht_operation_mode = ht_opmode; |
205 | sdata->ht_opmode_valid = true; | 207 | sdata->ht_opmode_valid = enable_ht; |
206 | } | 208 | } |
207 | 209 | ||
208 | return changed; | 210 | return changed; |
@@ -865,7 +867,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, | |||
865 | ieee80211_set_wmm_default(sdata); | 867 | ieee80211_set_wmm_default(sdata); |
866 | 868 | ||
867 | /* channel(_type) changes are handled by ieee80211_hw_config */ | 869 | /* channel(_type) changes are handled by ieee80211_hw_config */ |
868 | local->oper_channel_type = NL80211_CHAN_NO_HT; | 870 | WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); |
869 | 871 | ||
870 | /* on the next assoc, re-program HT parameters */ | 872 | /* on the next assoc, re-program HT parameters */ |
871 | sdata->ht_opmode_valid = false; | 873 | sdata->ht_opmode_valid = false; |
@@ -882,8 +884,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, | |||
882 | 884 | ||
883 | ieee80211_hw_config(local, config_changed); | 885 | ieee80211_hw_config(local, config_changed); |
884 | 886 | ||
885 | /* And the BSSID changed -- not very interesting here */ | 887 | /* The BSSID (not really interesting) and HT changed */ |
886 | changed |= BSS_CHANGED_BSSID; | 888 | changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; |
887 | ieee80211_bss_info_change_notify(sdata, changed); | 889 | ieee80211_bss_info_change_notify(sdata, changed); |
888 | 890 | ||
889 | if (remove_sta) | 891 | if (remove_sta) |
@@ -2265,7 +2267,7 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata, | |||
2265 | if ((chan != local->tmp_channel || | 2267 | if ((chan != local->tmp_channel || |
2266 | channel_type != local->tmp_channel_type) && | 2268 | channel_type != local->tmp_channel_type) && |
2267 | (chan != local->oper_channel || | 2269 | (chan != local->oper_channel || |
2268 | channel_type != local->oper_channel_type)) | 2270 | channel_type != local->_oper_channel_type)) |
2269 | return -EBUSY; | 2271 | return -EBUSY; |
2270 | 2272 | ||
2271 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); | 2273 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); |