aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2010-05-05 09:28:27 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-05-07 14:55:51 -0400
commit0aaffa9b9699894aab3266195a529baf9f96ac29 (patch)
tree26fe5f5277ac6d7061ea723f92d4038b0c28b0b8 /net/mac80211
parentf444de05d20e27cdd960c13fcbcfca3099f03143 (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')
-rw-r--r--net/mac80211/cfg.c23
-rw-r--r--net/mac80211/chan.c70
-rw-r--r--net/mac80211/ibss.c5
-rw-r--r--net/mac80211/ieee80211_i.h5
-rw-r--r--net/mac80211/main.c2
-rw-r--r--net/mac80211/mlme.c44
6 files changed, 118 insertions, 31 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 414b7dd7d7fd..ab166c6d9399 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1166,23 +1166,34 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
1166 enum nl80211_channel_type channel_type) 1166 enum nl80211_channel_type channel_type)
1167{ 1167{
1168 struct ieee80211_local *local = wiphy_priv(wiphy); 1168 struct ieee80211_local *local = wiphy_priv(wiphy);
1169 struct ieee80211_sub_if_data *sdata = NULL;
1170
1171 if (netdev)
1172 sdata = IEEE80211_DEV_TO_SUB_IF(netdev);
1169 1173
1170 switch (ieee80211_get_channel_mode(local, NULL)) { 1174 switch (ieee80211_get_channel_mode(local, NULL)) {
1171 case CHAN_MODE_HOPPING: 1175 case CHAN_MODE_HOPPING:
1172 return -EBUSY; 1176 return -EBUSY;
1173 case CHAN_MODE_FIXED: 1177 case CHAN_MODE_FIXED:
1174 if (local->oper_channel == chan && 1178 if (local->oper_channel != chan)
1175 local->oper_channel_type == channel_type) 1179 return -EBUSY;
1180 if (!sdata && local->_oper_channel_type == channel_type)
1176 return 0; 1181 return 0;
1177 return -EBUSY; 1182 break;
1178 case CHAN_MODE_UNDEFINED: 1183 case CHAN_MODE_UNDEFINED:
1179 break; 1184 break;
1180 } 1185 }
1181 1186
1182 local->oper_channel = chan; 1187 local->oper_channel = chan;
1183 local->oper_channel_type = channel_type;
1184 1188
1185 return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 1189 if (!ieee80211_set_channel_type(local, sdata, channel_type))
1190 return -EBUSY;
1191
1192 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
1193 if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR)
1194 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT);
1195
1196 return 0;
1186} 1197}
1187 1198
1188#ifdef CONFIG_PM 1199#ifdef CONFIG_PM
@@ -1406,7 +1417,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
1406 * association, there's no need to send an action frame. 1417 * association, there's no need to send an action frame.
1407 */ 1418 */
1408 if (!sdata->u.mgd.associated || 1419 if (!sdata->u.mgd.associated ||
1409 sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) { 1420 sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
1410 mutex_lock(&sdata->local->iflist_mtx); 1421 mutex_lock(&sdata->local->iflist_mtx);
1411 ieee80211_recalc_smps(sdata->local, sdata); 1422 ieee80211_recalc_smps(sdata->local, sdata);
1412 mutex_unlock(&sdata->local->iflist_mtx); 1423 mutex_unlock(&sdata->local->iflist_mtx);
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 08f3832661a5..5d218c530a4e 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -2,6 +2,7 @@
2 * mac80211 - channel management 2 * mac80211 - channel management
3 */ 3 */
4 4
5#include <linux/nl80211.h>
5#include "ieee80211_i.h" 6#include "ieee80211_i.h"
6 7
7enum ieee80211_chan_mode 8enum ieee80211_chan_mode
@@ -55,3 +56,72 @@ ieee80211_get_channel_mode(struct ieee80211_local *local,
55 56
56 return mode; 57 return mode;
57} 58}
59
60bool 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 d5855ae387e8..36745f494f63 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -102,7 +102,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
102 sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; 102 sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
103 103
104 local->oper_channel = chan; 104 local->oper_channel = chan;
105 local->oper_channel_type = NL80211_CHAN_NO_HT; 105 WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
106 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 106 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
107 107
108 sband = local->hw.wiphy->bands[chan->band]; 108 sband = local->hw.wiphy->bands[chan->band];
@@ -910,7 +910,8 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
910 /* fix ourselves to that channel now already */ 910 /* fix ourselves to that channel now already */
911 if (params->channel_fixed) { 911 if (params->channel_fixed) {
912 sdata->local->oper_channel = params->channel; 912 sdata->local->oper_channel = params->channel;
913 sdata->local->oper_channel_type = NL80211_CHAN_NO_HT; 913 WARN_ON(!ieee80211_set_channel_type(sdata->local, sdata,
914 NL80211_CHAN_NO_HT));
914 } 915 }
915 916
916 if (params->ie) { 917 if (params->ie) {
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 359edff31471..69e7f4131f46 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -768,7 +768,7 @@ struct ieee80211_local {
768 enum mac80211_scan_state next_scan_state; 768 enum mac80211_scan_state next_scan_state;
769 struct delayed_work scan_work; 769 struct delayed_work scan_work;
770 struct ieee80211_sub_if_data *scan_sdata; 770 struct ieee80211_sub_if_data *scan_sdata;
771 enum nl80211_channel_type oper_channel_type; 771 enum nl80211_channel_type _oper_channel_type;
772 struct ieee80211_channel *oper_channel, *csa_channel; 772 struct ieee80211_channel *oper_channel, *csa_channel;
773 773
774 /* Temporary remain-on-channel for off-channel operations */ 774 /* Temporary remain-on-channel for off-channel operations */
@@ -1239,6 +1239,9 @@ enum ieee80211_chan_mode {
1239enum ieee80211_chan_mode 1239enum ieee80211_chan_mode
1240ieee80211_get_channel_mode(struct ieee80211_local *local, 1240ieee80211_get_channel_mode(struct ieee80211_local *local,
1241 struct ieee80211_sub_if_data *ignore); 1241 struct ieee80211_sub_if_data *ignore);
1242bool ieee80211_set_channel_type(struct ieee80211_local *local,
1243 struct ieee80211_sub_if_data *sdata,
1244 enum nl80211_channel_type chantype);
1242 1245
1243#ifdef CONFIG_MAC80211_NOINLINE 1246#ifdef CONFIG_MAC80211_NOINLINE
1244#define debug_noinline noinline 1247#define debug_noinline noinline
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 353b6b42d9c5..d763d76e809f 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 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);