aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-02-07 05:47:44 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-02-15 03:41:30 -0500
commite1a0c6b3a4b27ed5f21291d0bbee2167ec201ef5 (patch)
tree18e5c9bd022ea469e7350a52ca67ce505a7608e6 /net/mac80211
parent4a34215ef7487b1cbd783e7cc485eb03de893bd0 (diff)
mac80211: stop toggling IEEE80211_HT_CAP_SUP_WIDTH_20_40
For VHT, many more bandwidth changes are possible. As a first step, stop toggling the IEEE80211_HT_CAP_SUP_WIDTH_20_40 flag in the HT capabilities and instead introduce a bandwidth field indicating the currently usable bandwidth to transmit to the station. Of course, make all drivers use it. To achieve this, make ieee80211_ht_cap_ie_to_sta_ht_cap() get the station as an argument, rather than the new capabilities, so it can set up the new bandwidth field. If the station is a VHT station and VHT bandwidth is in use, also set the bandwidth accordingly. Doing this allows us to get rid of the supports_40mhz flag as the HT capabilities now reflect the true capability instead of the current setting. While at it, also fix ieee80211_ht_cap_ie_to_sta_ht_cap() to not ignore HT cap overrides when MCS TX isn't supported (not that it really happens...) Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/cfg.c3
-rw-r--r--net/mac80211/ht.c82
-rw-r--r--net/mac80211/ibss.c23
-rw-r--r--net/mac80211/ieee80211_i.h5
-rw-r--r--net/mac80211/mesh_plink.c6
-rw-r--r--net/mac80211/mlme.c22
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c19
-rw-r--r--net/mac80211/rx.c21
-rw-r--r--net/mac80211/sta_info.h4
-rw-r--r--net/mac80211/vht.c39
10 files changed, 130 insertions, 94 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 9c9496d01120..c3869ba42343 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1252,8 +1252,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
1252 1252
1253 if (params->ht_capa) 1253 if (params->ht_capa)
1254 ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, 1254 ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
1255 params->ht_capa, 1255 params->ht_capa, sta);
1256 &sta->sta.ht_cap);
1257 1256
1258 if (params->vht_capa) 1257 if (params->vht_capa)
1259 ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, 1258 ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 9e7560becbbb..a64b4f0d373f 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -37,6 +37,9 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
37 u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask); 37 u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask);
38 int i; 38 int i;
39 39
40 if (!ht_cap->ht_supported)
41 return;
42
40 if (sdata->vif.type != NL80211_IFTYPE_STATION) { 43 if (sdata->vif.type != NL80211_IFTYPE_STATION) {
41 /* AP interfaces call this code when adding new stations, 44 /* AP interfaces call this code when adding new stations,
42 * so just silently ignore non station interfaces. 45 * so just silently ignore non station interfaces.
@@ -89,22 +92,23 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
89} 92}
90 93
91 94
92void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, 95bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
93 struct ieee80211_supported_band *sband, 96 struct ieee80211_supported_band *sband,
94 struct ieee80211_ht_cap *ht_cap_ie, 97 struct ieee80211_ht_cap *ht_cap_ie,
95 struct ieee80211_sta_ht_cap *ht_cap) 98 struct sta_info *sta)
96{ 99{
100 struct ieee80211_sta_ht_cap ht_cap;
97 u8 ampdu_info, tx_mcs_set_cap; 101 u8 ampdu_info, tx_mcs_set_cap;
98 int i, max_tx_streams; 102 int i, max_tx_streams;
103 bool changed;
104 enum ieee80211_sta_rx_bandwidth bw;
99 105
100 BUG_ON(!ht_cap); 106 memset(&ht_cap, 0, sizeof(ht_cap));
101
102 memset(ht_cap, 0, sizeof(*ht_cap));
103 107
104 if (!ht_cap_ie || !sband->ht_cap.ht_supported) 108 if (!ht_cap_ie || !sband->ht_cap.ht_supported)
105 return; 109 goto apply;
106 110
107 ht_cap->ht_supported = true; 111 ht_cap.ht_supported = true;
108 112
109 /* 113 /*
110 * The bits listed in this expression should be 114 * The bits listed in this expression should be
@@ -112,7 +116,7 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
112 * advertises more then we can't use those thus 116 * advertises more then we can't use those thus
113 * we mask them out. 117 * we mask them out.
114 */ 118 */
115 ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) & 119 ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) &
116 (sband->ht_cap.cap | 120 (sband->ht_cap.cap |
117 ~(IEEE80211_HT_CAP_LDPC_CODING | 121 ~(IEEE80211_HT_CAP_LDPC_CODING |
118 IEEE80211_HT_CAP_SUP_WIDTH_20_40 | 122 IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
@@ -121,44 +125,30 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
121 IEEE80211_HT_CAP_SGI_40 | 125 IEEE80211_HT_CAP_SGI_40 |
122 IEEE80211_HT_CAP_DSSSCCK40)); 126 IEEE80211_HT_CAP_DSSSCCK40));
123 127
124 /* Unset 40 MHz if we're not using a 40 MHz channel */
125 switch (sdata->vif.bss_conf.chandef.width) {
126 case NL80211_CHAN_WIDTH_20_NOHT:
127 case NL80211_CHAN_WIDTH_20:
128 ht_cap->cap &= ~IEEE80211_HT_CAP_SGI_40;
129 ht_cap->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
130 break;
131 case NL80211_CHAN_WIDTH_40:
132 case NL80211_CHAN_WIDTH_80:
133 case NL80211_CHAN_WIDTH_80P80:
134 case NL80211_CHAN_WIDTH_160:
135 break;
136 }
137
138 /* 128 /*
139 * The STBC bits are asymmetric -- if we don't have 129 * The STBC bits are asymmetric -- if we don't have
140 * TX then mask out the peer's RX and vice versa. 130 * TX then mask out the peer's RX and vice versa.
141 */ 131 */
142 if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)) 132 if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC))
143 ht_cap->cap &= ~IEEE80211_HT_CAP_RX_STBC; 133 ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC;
144 if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)) 134 if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC))
145 ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC; 135 ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;
146 136
147 ampdu_info = ht_cap_ie->ampdu_params_info; 137 ampdu_info = ht_cap_ie->ampdu_params_info;
148 ht_cap->ampdu_factor = 138 ht_cap.ampdu_factor =
149 ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; 139 ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR;
150 ht_cap->ampdu_density = 140 ht_cap.ampdu_density =
151 (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2; 141 (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
152 142
153 /* own MCS TX capabilities */ 143 /* own MCS TX capabilities */
154 tx_mcs_set_cap = sband->ht_cap.mcs.tx_params; 144 tx_mcs_set_cap = sband->ht_cap.mcs.tx_params;
155 145
156 /* Copy peer MCS TX capabilities, the driver might need them. */ 146 /* Copy peer MCS TX capabilities, the driver might need them. */
157 ht_cap->mcs.tx_params = ht_cap_ie->mcs.tx_params; 147 ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params;
158 148
159 /* can we TX with MCS rates? */ 149 /* can we TX with MCS rates? */
160 if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) 150 if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED))
161 return; 151 goto apply;
162 152
163 /* Counting from 0, therefore +1 */ 153 /* Counting from 0, therefore +1 */
164 if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) 154 if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF)
@@ -176,25 +166,53 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
176 * - remainder are multiple spatial streams using unequal modulation 166 * - remainder are multiple spatial streams using unequal modulation
177 */ 167 */
178 for (i = 0; i < max_tx_streams; i++) 168 for (i = 0; i < max_tx_streams; i++)
179 ht_cap->mcs.rx_mask[i] = 169 ht_cap.mcs.rx_mask[i] =
180 sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; 170 sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
181 171
182 if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION) 172 if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
183 for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE; 173 for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
184 i < IEEE80211_HT_MCS_MASK_LEN; i++) 174 i < IEEE80211_HT_MCS_MASK_LEN; i++)
185 ht_cap->mcs.rx_mask[i] = 175 ht_cap.mcs.rx_mask[i] =
186 sband->ht_cap.mcs.rx_mask[i] & 176 sband->ht_cap.mcs.rx_mask[i] &
187 ht_cap_ie->mcs.rx_mask[i]; 177 ht_cap_ie->mcs.rx_mask[i];
188 178
189 /* handle MCS rate 32 too */ 179 /* handle MCS rate 32 too */
190 if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) 180 if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
191 ht_cap->mcs.rx_mask[32/8] |= 1; 181 ht_cap.mcs.rx_mask[32/8] |= 1;
192 182
183 apply:
193 /* 184 /*
194 * If user has specified capability over-rides, take care 185 * If user has specified capability over-rides, take care
195 * of that here. 186 * of that here.
196 */ 187 */
197 ieee80211_apply_htcap_overrides(sdata, ht_cap); 188 ieee80211_apply_htcap_overrides(sdata, &ht_cap);
189
190 changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
191
192 memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
193
194 switch (sdata->vif.bss_conf.chandef.width) {
195 default:
196 WARN_ON_ONCE(1);
197 /* fall through */
198 case NL80211_CHAN_WIDTH_20_NOHT:
199 case NL80211_CHAN_WIDTH_20:
200 bw = IEEE80211_STA_RX_BW_20;
201 break;
202 case NL80211_CHAN_WIDTH_40:
203 case NL80211_CHAN_WIDTH_80:
204 case NL80211_CHAN_WIDTH_80P80:
205 case NL80211_CHAN_WIDTH_160:
206 bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
207 IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
208 break;
209 }
210
211 if (bw != sta->sta.bandwidth)
212 changed = true;
213 sta->sta.bandwidth = bw;
214
215 return changed;
198} 216}
199 217
200void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, 218void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 2db1f2b90bfe..40b71dfcc79d 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -496,33 +496,26 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
496 if (sta && elems->ht_operation && elems->ht_cap_elem && 496 if (sta && elems->ht_operation && elems->ht_cap_elem &&
497 sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) { 497 sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) {
498 /* we both use HT */ 498 /* we both use HT */
499 struct ieee80211_sta_ht_cap sta_ht_cap_new; 499 struct ieee80211_ht_cap htcap_ie;
500 struct cfg80211_chan_def chandef; 500 struct cfg80211_chan_def chandef;
501 501
502 ieee80211_ht_oper_to_chandef(channel, 502 ieee80211_ht_oper_to_chandef(channel,
503 elems->ht_operation, 503 elems->ht_operation,
504 &chandef); 504 &chandef);
505 505
506 ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, 506 memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
507 elems->ht_cap_elem,
508 &sta_ht_cap_new);
509 507
510 /* 508 /*
511 * fall back to HT20 if we don't use or use 509 * fall back to HT20 if we don't use or use
512 * the other extension channel 510 * the other extension channel
513 */ 511 */
514 if (chandef.width != NL80211_CHAN_WIDTH_40 || 512 if (cfg80211_get_chandef_type(&chandef) !=
515 cfg80211_get_chandef_type(&chandef) !=
516 sdata->u.ibss.channel_type) 513 sdata->u.ibss.channel_type)
517 sta_ht_cap_new.cap &= 514 htcap_ie.cap_info &=
518 ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; 515 cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
519 516
520 if (memcmp(&sta->sta.ht_cap, &sta_ht_cap_new, 517 rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(
521 sizeof(sta_ht_cap_new))) { 518 sdata, sband, &htcap_ie, sta);
522 memcpy(&sta->sta.ht_cap, &sta_ht_cap_new,
523 sizeof(sta_ht_cap_new));
524 rates_updated = true;
525 }
526 } 519 }
527 520
528 if (sta && rates_updated) { 521 if (sta && rates_updated) {
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 9206d43ca572..6b41d7787a5a 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1384,10 +1384,10 @@ void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
1384/* HT */ 1384/* HT */
1385void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, 1385void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
1386 struct ieee80211_sta_ht_cap *ht_cap); 1386 struct ieee80211_sta_ht_cap *ht_cap);
1387void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, 1387bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
1388 struct ieee80211_supported_band *sband, 1388 struct ieee80211_supported_band *sband,
1389 struct ieee80211_ht_cap *ht_cap_ie, 1389 struct ieee80211_ht_cap *ht_cap_ie,
1390 struct ieee80211_sta_ht_cap *ht_cap); 1390 struct sta_info *sta);
1391void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, 1391void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
1392 const u8 *da, u16 tid, 1392 const u8 *da, u16 tid,
1393 u16 initiator, u16 reason_code); 1393 u16 initiator, u16 reason_code);
@@ -1431,6 +1431,7 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
1431 struct ieee80211_supported_band *sband, 1431 struct ieee80211_supported_band *sband,
1432 struct ieee80211_vht_cap *vht_cap_ie, 1432 struct ieee80211_vht_cap *vht_cap_ie,
1433 struct sta_info *sta); 1433 struct sta_info *sta);
1434enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta);
1434 1435
1435/* Spectrum management */ 1436/* Spectrum management */
1436void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, 1437void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index a4c7a7e98d14..7765139b24aa 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -373,8 +373,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
373 if (elems->ht_cap_elem && 373 if (elems->ht_cap_elem &&
374 sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) 374 sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
375 ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, 375 ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
376 elems->ht_cap_elem, 376 elems->ht_cap_elem, sta);
377 &sta->sta.ht_cap);
378 else 377 else
379 memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap)); 378 memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap));
380 379
@@ -383,8 +382,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
383 382
384 if (!(elems->ht_operation->ht_param & 383 if (!(elems->ht_operation->ht_param &
385 IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) 384 IEEE80211_HT_PARAM_CHAN_WIDTH_ANY))
386 sta->sta.ht_cap.cap &= 385 sta->sta.bandwidth = IEEE80211_STA_RX_BW_20;
387 ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
388 ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, 386 ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
389 elems->ht_operation, &chandef); 387 elems->ht_operation, &chandef);
390 if (sta->ch_width != chandef.width) 388 if (sta->ch_width != chandef.width)
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 58b6e67ffbed..be1147286801 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -219,19 +219,20 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata,
219 mutex_lock(&local->sta_mtx); 219 mutex_lock(&local->sta_mtx);
220 sta = sta_info_get(sdata, bssid); 220 sta = sta_info_get(sdata, bssid);
221 221
222 WARN_ON_ONCE(!sta); 222 if (WARN_ON_ONCE(!sta)) {
223 mutex_unlock(&local->sta_mtx);
224 return changed;
225 }
223 226
224 if (sta && !sta->supports_40mhz) 227 if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
225 disable_40 = true; 228 disable_40 = true;
226 229
227 if (sta && (!reconfig || 230 if (!reconfig ||
228 (disable_40 != !(sta->sta.ht_cap.cap & 231 disable_40 != (sta->sta.bandwidth < IEEE80211_STA_RX_BW_40)) {
229 IEEE80211_HT_CAP_SUP_WIDTH_20_40)))) {
230
231 if (disable_40) 232 if (disable_40)
232 sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; 233 sta->sta.bandwidth = IEEE80211_STA_RX_BW_20;
233 else 234 else
234 sta->sta.ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; 235 sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta);
235 236
236 rate_control_rate_update(local, sband, sta, 237 rate_control_rate_update(local, sband, sta,
237 IEEE80211_RC_BW_CHANGED); 238 IEEE80211_RC_BW_CHANGED);
@@ -2210,10 +2211,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
2210 2211
2211 if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) 2212 if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
2212 ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, 2213 ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
2213 elems.ht_cap_elem, &sta->sta.ht_cap); 2214 elems.ht_cap_elem, sta);
2214
2215 sta->supports_40mhz =
2216 sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
2217 2215
2218 if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) 2216 if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
2219 ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, 2217 ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 6e35dcd2aa2f..6176c71d47ff 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -848,8 +848,6 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
848 IEEE80211_HT_CAP_SM_PS_SHIFT; 848 IEEE80211_HT_CAP_SM_PS_SHIFT;
849 849
850 for (i = 0; i < ARRAY_SIZE(mi->groups); i++) { 850 for (i = 0; i < ARRAY_SIZE(mi->groups); i++) {
851 u16 req = 0;
852
853 mi->groups[i].supported = 0; 851 mi->groups[i].supported = 0;
854 if (i == MINSTREL_CCK_GROUP) { 852 if (i == MINSTREL_CCK_GROUP) {
855 minstrel_ht_update_cck(mp, mi, sband, sta); 853 minstrel_ht_update_cck(mp, mi, sband, sta);
@@ -857,16 +855,17 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
857 } 855 }
858 856
859 if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) { 857 if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) {
860 if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) 858 if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
861 req |= IEEE80211_HT_CAP_SGI_40; 859 if (!(sta_cap & IEEE80211_HT_CAP_SGI_40))
862 else 860 continue;
863 req |= IEEE80211_HT_CAP_SGI_20; 861 } else {
862 if (!(sta_cap & IEEE80211_HT_CAP_SGI_20))
863 continue;
864 }
864 } 865 }
865 866
866 if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) 867 if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH &&
867 req |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; 868 sta->bandwidth < IEEE80211_STA_RX_BW_40)
868
869 if ((sta_cap & req) != req)
870 continue; 869 continue;
871 870
872 /* Mark MCS > 7 as unsupported if STA is in static SMPS mode */ 871 /* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index b5f1bba7ffe1..8a861a50b12f 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2410,26 +2410,21 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
2410 case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: { 2410 case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: {
2411 struct ieee80211_supported_band *sband; 2411 struct ieee80211_supported_band *sband;
2412 u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth; 2412 u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth;
2413 bool old_40mhz, new_40mhz; 2413 enum ieee80211_sta_rx_bandwidth new_bw;
2414 2414
2415 /* If it doesn't support 40 MHz it can't change ... */ 2415 /* If it doesn't support 40 MHz it can't change ... */
2416 if (!rx->sta->supports_40mhz) 2416 if (!(rx->sta->sta.ht_cap.cap &
2417 IEEE80211_HT_CAP_SUP_WIDTH_20_40))
2417 goto handled; 2418 goto handled;
2418 2419
2419 old_40mhz = rx->sta->sta.ht_cap.cap & 2420 if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ)
2420 IEEE80211_HT_CAP_SUP_WIDTH_20_40; 2421 new_bw = IEEE80211_STA_RX_BW_20;
2421 new_40mhz = chanwidth == IEEE80211_HT_CHANWIDTH_ANY; 2422 else
2423 new_bw = ieee80211_sta_cur_vht_bw(rx->sta);
2422 2424
2423 if (old_40mhz == new_40mhz) 2425 if (rx->sta->sta.bandwidth == new_bw)
2424 goto handled; 2426 goto handled;
2425 2427
2426 if (new_40mhz)
2427 rx->sta->sta.ht_cap.cap |=
2428 IEEE80211_HT_CAP_SUP_WIDTH_20_40;
2429 else
2430 rx->sta->sta.ht_cap.cap &=
2431 ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
2432
2433 sband = rx->local->hw.wiphy->bands[status->band]; 2428 sband = rx->local->hw.wiphy->bands[status->band];
2434 2429
2435 rate_control_rate_update(local, sband, rx->sta, 2430 rate_control_rate_update(local, sband, rx->sta,
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 350578c396c0..03c42f8d39f0 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -296,8 +296,6 @@ struct sta_ampdu_mlme {
296 * @sta: station information we share with the driver 296 * @sta: station information we share with the driver
297 * @sta_state: duplicates information about station state (for debug) 297 * @sta_state: duplicates information about station state (for debug)
298 * @beacon_loss_count: number of times beacon loss has triggered 298 * @beacon_loss_count: number of times beacon loss has triggered
299 * @supports_40mhz: tracks whether the station advertised 40 MHz support
300 * as we overwrite its HT parameters with the currently used value
301 * @rcu_head: RCU head used for freeing this station struct 299 * @rcu_head: RCU head used for freeing this station struct
302 */ 300 */
303struct sta_info { 301struct sta_info {
@@ -403,8 +401,6 @@ struct sta_info {
403 unsigned int lost_packets; 401 unsigned int lost_packets;
404 unsigned int beacon_loss_count; 402 unsigned int beacon_loss_count;
405 403
406 bool supports_40mhz;
407
408 /* keep last! */ 404 /* keep last! */
409 struct ieee80211_sta sta; 405 struct ieee80211_sta sta;
410}; 406};
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index 1606aa165d5f..0fc9a2fb8d53 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -27,6 +27,10 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
27 if (!vht_cap_ie || !sband->vht_cap.vht_supported) 27 if (!vht_cap_ie || !sband->vht_cap.vht_supported)
28 return; 28 return;
29 29
30 /* A VHT STA must support 40 MHz */
31 if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
32 return;
33
30 vht_cap->vht_supported = true; 34 vht_cap->vht_supported = true;
31 35
32 vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info); 36 vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info);
@@ -34,4 +38,39 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
34 /* Copy peer MCS info, the driver might need them. */ 38 /* Copy peer MCS info, the driver might need them. */
35 memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, 39 memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs,
36 sizeof(struct ieee80211_vht_mcs_info)); 40 sizeof(struct ieee80211_vht_mcs_info));
41
42 sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta);
43}
44
45enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
46{
47 struct ieee80211_sub_if_data *sdata = sta->sdata;
48 u32 cap = sta->sta.vht_cap.cap;
49
50 if (!sta->sta.vht_cap.vht_supported)
51 return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
52 IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
53
54 /* TODO: handle VHT opmode notification data */
55
56 switch (sdata->vif.bss_conf.chandef.width) {
57 default:
58 WARN_ON_ONCE(1);
59 /* fall through */
60 case NL80211_CHAN_WIDTH_20_NOHT:
61 case NL80211_CHAN_WIDTH_20:
62 case NL80211_CHAN_WIDTH_40:
63 return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
64 IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
65 case NL80211_CHAN_WIDTH_160:
66 if (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)
67 return IEEE80211_STA_RX_BW_160;
68 /* fall through */
69 case NL80211_CHAN_WIDTH_80P80:
70 if (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
71 return IEEE80211_STA_RX_BW_160;
72 /* fall through */
73 case NL80211_CHAN_WIDTH_80:
74 return IEEE80211_STA_RX_BW_80;
75 }
37} 76}