diff options
author | Johannes Berg <johannes.berg@intel.com> | 2013-02-07 05:47:44 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-02-15 03:41:30 -0500 |
commit | e1a0c6b3a4b27ed5f21291d0bbee2167ec201ef5 (patch) | |
tree | 18e5c9bd022ea469e7350a52ca67ce505a7608e6 /net/mac80211 | |
parent | 4a34215ef7487b1cbd783e7cc485eb03de893bd0 (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.c | 3 | ||||
-rw-r--r-- | net/mac80211/ht.c | 82 | ||||
-rw-r--r-- | net/mac80211/ibss.c | 23 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 5 | ||||
-rw-r--r-- | net/mac80211/mesh_plink.c | 6 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 22 | ||||
-rw-r--r-- | net/mac80211/rc80211_minstrel_ht.c | 19 | ||||
-rw-r--r-- | net/mac80211/rx.c | 21 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 4 | ||||
-rw-r--r-- | net/mac80211/vht.c | 39 |
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 | ||
92 | void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, | 95 | bool 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 | ||
200 | void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, | 218 | void 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 */ |
1385 | void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, | 1385 | void 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); |
1387 | void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, | 1387 | bool 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); |
1391 | void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, | 1391 | void 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); |
1434 | enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta); | ||
1434 | 1435 | ||
1435 | /* Spectrum management */ | 1436 | /* Spectrum management */ |
1436 | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, | 1437 | void 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 | */ |
303 | struct sta_info { | 301 | struct 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 | |||
45 | enum 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 | } |