4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
9 #include <linux/ieee80211.h>
10 #include <linux/export.h>
11 #include <net/mac80211.h>
12 #include "ieee80211_i.h"
16 static void __check_vhtcap_disable(struct ieee80211_sub_if_data
*sdata
,
17 struct ieee80211_sta_vht_cap
*vht_cap
,
20 __le32 le_flag
= cpu_to_le32(flag
);
22 if (sdata
->u
.mgd
.vht_capa_mask
.vht_cap_info
& le_flag
&&
23 !(sdata
->u
.mgd
.vht_capa
.vht_cap_info
& le_flag
))
24 vht_cap
->cap
&= ~flag
;
27 void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data
*sdata
,
28 struct ieee80211_sta_vht_cap
*vht_cap
)
31 u16 rxmcs_mask
, rxmcs_cap
, rxmcs_n
, txmcs_mask
, txmcs_cap
, txmcs_n
;
33 if (!vht_cap
->vht_supported
)
36 if (sdata
->vif
.type
!= NL80211_IFTYPE_STATION
)
39 __check_vhtcap_disable(sdata
, vht_cap
,
40 IEEE80211_VHT_CAP_RXLDPC
);
41 __check_vhtcap_disable(sdata
, vht_cap
,
42 IEEE80211_VHT_CAP_SHORT_GI_80
);
43 __check_vhtcap_disable(sdata
, vht_cap
,
44 IEEE80211_VHT_CAP_SHORT_GI_160
);
45 __check_vhtcap_disable(sdata
, vht_cap
,
46 IEEE80211_VHT_CAP_TXSTBC
);
47 __check_vhtcap_disable(sdata
, vht_cap
,
48 IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE
);
49 __check_vhtcap_disable(sdata
, vht_cap
,
50 IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE
);
51 __check_vhtcap_disable(sdata
, vht_cap
,
52 IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN
);
53 __check_vhtcap_disable(sdata
, vht_cap
,
54 IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN
);
56 /* Allow user to decrease AMPDU length exponent */
57 if (sdata
->u
.mgd
.vht_capa_mask
.vht_cap_info
&
58 cpu_to_le32(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK
)) {
61 n
= le32_to_cpu(sdata
->u
.mgd
.vht_capa
.vht_cap_info
) &
62 IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK
;
63 n
>>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT
;
64 cap
= vht_cap
->cap
& IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK
;
65 cap
>>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT
;
69 ~IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK
;
71 n
<< IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT
;
75 /* Allow the user to decrease MCSes */
77 le16_to_cpu(sdata
->u
.mgd
.vht_capa_mask
.supp_mcs
.rx_mcs_map
);
78 rxmcs_n
= le16_to_cpu(sdata
->u
.mgd
.vht_capa
.supp_mcs
.rx_mcs_map
);
79 rxmcs_n
&= rxmcs_mask
;
80 rxmcs_cap
= le16_to_cpu(vht_cap
->vht_mcs
.rx_mcs_map
);
83 le16_to_cpu(sdata
->u
.mgd
.vht_capa_mask
.supp_mcs
.tx_mcs_map
);
84 txmcs_n
= le16_to_cpu(sdata
->u
.mgd
.vht_capa
.supp_mcs
.tx_mcs_map
);
85 txmcs_n
&= txmcs_mask
;
86 txmcs_cap
= le16_to_cpu(vht_cap
->vht_mcs
.tx_mcs_map
);
87 for (i
= 0; i
< 8; i
++) {
90 m
= (rxmcs_mask
>> 2*i
) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
91 n
= (rxmcs_n
>> 2*i
) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
92 c
= (rxmcs_cap
>> 2*i
) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
94 if (m
&& ((c
!= IEEE80211_VHT_MCS_NOT_SUPPORTED
&& n
< c
) ||
95 n
== IEEE80211_VHT_MCS_NOT_SUPPORTED
)) {
96 rxmcs_cap
&= ~(3 << 2*i
);
97 rxmcs_cap
|= (rxmcs_n
& (3 << 2*i
));
100 m
= (txmcs_mask
>> 2*i
) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
101 n
= (txmcs_n
>> 2*i
) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
102 c
= (txmcs_cap
>> 2*i
) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
104 if (m
&& ((c
!= IEEE80211_VHT_MCS_NOT_SUPPORTED
&& n
< c
) ||
105 n
== IEEE80211_VHT_MCS_NOT_SUPPORTED
)) {
106 txmcs_cap
&= ~(3 << 2*i
);
107 txmcs_cap
|= (txmcs_n
& (3 << 2*i
));
110 vht_cap
->vht_mcs
.rx_mcs_map
= cpu_to_le16(rxmcs_cap
);
111 vht_cap
->vht_mcs
.tx_mcs_map
= cpu_to_le16(txmcs_cap
);
115 ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data
*sdata
,
116 struct ieee80211_supported_band
*sband
,
117 const struct ieee80211_vht_cap
*vht_cap_ie
,
118 struct sta_info
*sta
)
120 struct ieee80211_sta_vht_cap
*vht_cap
= &sta
->sta
.vht_cap
;
121 struct ieee80211_sta_vht_cap own_cap
;
124 memset(vht_cap
, 0, sizeof(*vht_cap
));
126 if (!sta
->sta
.ht_cap
.ht_supported
)
129 if (!vht_cap_ie
|| !sband
->vht_cap
.vht_supported
)
132 /* don't support VHT for TDLS peers for now */
133 if (test_sta_flag(sta
, WLAN_STA_TDLS_PEER
))
137 * A VHT STA must support 40 MHz, but if we verify that here
138 * then we break a few things - some APs (e.g. Netgear R6300v2
139 * and others based on the BCM4360 chipset) will unset this
140 * capability bit when operating in 20 MHz.
143 vht_cap
->vht_supported
= true;
145 own_cap
= sband
->vht_cap
;
147 * If user has specified capability overrides, take care
148 * of that if the station we're setting up is the AP that
149 * we advertised a restricted capability set to. Override
150 * our own capabilities and then use those below.
152 if (sdata
->vif
.type
== NL80211_IFTYPE_STATION
&&
153 !test_sta_flag(sta
, WLAN_STA_TDLS_PEER
))
154 ieee80211_apply_vhtcap_overrides(sdata
, &own_cap
);
156 /* take some capabilities as-is */
157 cap_info
= le32_to_cpu(vht_cap_ie
->vht_cap_info
);
158 vht_cap
->cap
= cap_info
;
159 vht_cap
->cap
&= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895
|
160 IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991
|
161 IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454
|
162 IEEE80211_VHT_CAP_RXLDPC
|
163 IEEE80211_VHT_CAP_VHT_TXOP_PS
|
164 IEEE80211_VHT_CAP_HTC_VHT
|
165 IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK
|
166 IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB
|
167 IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB
|
168 IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN
|
169 IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN
;
171 /* and some based on our own capabilities */
172 switch (own_cap
.cap
& IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK
) {
173 case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ
:
174 vht_cap
->cap
|= cap_info
&
175 IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ
;
177 case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ
:
178 vht_cap
->cap
|= cap_info
&
179 IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK
;
186 /* symmetric capabilities */
187 vht_cap
->cap
|= cap_info
& own_cap
.cap
&
188 (IEEE80211_VHT_CAP_SHORT_GI_80
|
189 IEEE80211_VHT_CAP_SHORT_GI_160
);
192 if (own_cap
.cap
& IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE
)
193 vht_cap
->cap
|= cap_info
&
194 (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE
|
195 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK
);
197 if (own_cap
.cap
& IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE
)
198 vht_cap
->cap
|= cap_info
&
199 (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE
|
200 IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK
);
202 if (own_cap
.cap
& IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE
)
203 vht_cap
->cap
|= cap_info
&
204 IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE
;
206 if (own_cap
.cap
& IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE
)
207 vht_cap
->cap
|= cap_info
&
208 IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE
;
210 if (own_cap
.cap
& IEEE80211_VHT_CAP_TXSTBC
)
211 vht_cap
->cap
|= cap_info
& IEEE80211_VHT_CAP_RXSTBC_MASK
;
213 if (own_cap
.cap
& IEEE80211_VHT_CAP_RXSTBC_MASK
)
214 vht_cap
->cap
|= cap_info
& IEEE80211_VHT_CAP_TXSTBC
;
216 /* Copy peer MCS info, the driver might need them. */
217 memcpy(&vht_cap
->vht_mcs
, &vht_cap_ie
->supp_mcs
,
218 sizeof(struct ieee80211_vht_mcs_info
));
220 /* but also restrict MCSes */
221 for (i
= 0; i
< 8; i
++) {
222 u16 own_rx
, own_tx
, peer_rx
, peer_tx
;
224 own_rx
= le16_to_cpu(own_cap
.vht_mcs
.rx_mcs_map
);
225 own_rx
= (own_rx
>> i
* 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
227 own_tx
= le16_to_cpu(own_cap
.vht_mcs
.tx_mcs_map
);
228 own_tx
= (own_tx
>> i
* 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
230 peer_rx
= le16_to_cpu(vht_cap
->vht_mcs
.rx_mcs_map
);
231 peer_rx
= (peer_rx
>> i
* 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
233 peer_tx
= le16_to_cpu(vht_cap
->vht_mcs
.tx_mcs_map
);
234 peer_tx
= (peer_tx
>> i
* 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
236 if (peer_tx
!= IEEE80211_VHT_MCS_NOT_SUPPORTED
) {
237 if (own_rx
== IEEE80211_VHT_MCS_NOT_SUPPORTED
)
238 peer_tx
= IEEE80211_VHT_MCS_NOT_SUPPORTED
;
239 else if (own_rx
< peer_tx
)
243 if (peer_rx
!= IEEE80211_VHT_MCS_NOT_SUPPORTED
) {
244 if (own_tx
== IEEE80211_VHT_MCS_NOT_SUPPORTED
)
245 peer_rx
= IEEE80211_VHT_MCS_NOT_SUPPORTED
;
246 else if (own_tx
< peer_rx
)
250 vht_cap
->vht_mcs
.rx_mcs_map
&=
251 ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED
<< i
* 2);
252 vht_cap
->vht_mcs
.rx_mcs_map
|= cpu_to_le16(peer_rx
<< i
* 2);
254 vht_cap
->vht_mcs
.tx_mcs_map
&=
255 ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED
<< i
* 2);
256 vht_cap
->vht_mcs
.tx_mcs_map
|= cpu_to_le16(peer_tx
<< i
* 2);
259 /* finally set up the bandwidth */
260 switch (vht_cap
->cap
& IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK
) {
261 case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ
:
262 case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ
:
263 sta
->cur_max_bandwidth
= IEEE80211_STA_RX_BW_160
;
266 sta
->cur_max_bandwidth
= IEEE80211_STA_RX_BW_80
;
269 sta
->sta
.bandwidth
= ieee80211_sta_cur_vht_bw(sta
);
272 enum ieee80211_sta_rx_bandwidth
ieee80211_sta_cap_rx_bw(struct sta_info
*sta
)
274 struct ieee80211_sta_vht_cap
*vht_cap
= &sta
->sta
.vht_cap
;
277 if (!vht_cap
->vht_supported
)
278 return sta
->sta
.ht_cap
.cap
& IEEE80211_HT_CAP_SUP_WIDTH_20_40
?
279 IEEE80211_STA_RX_BW_40
:
280 IEEE80211_STA_RX_BW_20
;
282 cap_width
= vht_cap
->cap
& IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK
;
284 if (cap_width
== IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ
||
285 cap_width
== IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ
)
286 return IEEE80211_STA_RX_BW_160
;
288 return IEEE80211_STA_RX_BW_80
;
291 static enum ieee80211_sta_rx_bandwidth
292 ieee80211_chan_width_to_rx_bw(enum nl80211_chan_width width
)
295 case NL80211_CHAN_WIDTH_20_NOHT
:
296 case NL80211_CHAN_WIDTH_20
:
297 return IEEE80211_STA_RX_BW_20
;
298 case NL80211_CHAN_WIDTH_40
:
299 return IEEE80211_STA_RX_BW_40
;
300 case NL80211_CHAN_WIDTH_80
:
301 return IEEE80211_STA_RX_BW_80
;
302 case NL80211_CHAN_WIDTH_160
:
303 case NL80211_CHAN_WIDTH_80P80
:
304 return IEEE80211_STA_RX_BW_160
;
307 return IEEE80211_STA_RX_BW_20
;
311 enum ieee80211_sta_rx_bandwidth
ieee80211_sta_cur_vht_bw(struct sta_info
*sta
)
313 struct ieee80211_sub_if_data
*sdata
= sta
->sdata
;
314 enum ieee80211_sta_rx_bandwidth bw
;
316 bw
= ieee80211_chan_width_to_rx_bw(sdata
->vif
.bss_conf
.chandef
.width
);
317 bw
= min(bw
, ieee80211_sta_cap_rx_bw(sta
));
318 bw
= min(bw
, sta
->cur_max_bandwidth
);
323 void ieee80211_sta_set_rx_nss(struct sta_info
*sta
)
325 u8 ht_rx_nss
= 0, vht_rx_nss
= 0;
327 /* if we received a notification already don't overwrite it */
331 if (sta
->sta
.ht_cap
.ht_supported
) {
332 if (sta
->sta
.ht_cap
.mcs
.rx_mask
[0])
334 if (sta
->sta
.ht_cap
.mcs
.rx_mask
[1])
336 if (sta
->sta
.ht_cap
.mcs
.rx_mask
[2])
338 if (sta
->sta
.ht_cap
.mcs
.rx_mask
[3])
340 /* FIXME: consider rx_highest? */
343 if (sta
->sta
.vht_cap
.vht_supported
) {
347 rx_mcs_map
= le16_to_cpu(sta
->sta
.vht_cap
.vht_mcs
.rx_mcs_map
);
349 for (i
= 7; i
>= 0; i
--) {
350 u8 mcs
= (rx_mcs_map
>> (2 * i
)) & 3;
352 if (mcs
!= IEEE80211_VHT_MCS_NOT_SUPPORTED
) {
357 /* FIXME: consider rx_highest? */
360 ht_rx_nss
= max(ht_rx_nss
, vht_rx_nss
);
361 sta
->sta
.rx_nss
= max_t(u8
, 1, ht_rx_nss
);
364 u32
__ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data
*sdata
,
365 struct sta_info
*sta
, u8 opmode
,
366 enum ieee80211_band band
, bool nss_only
)
368 struct ieee80211_local
*local
= sdata
->local
;
369 struct ieee80211_supported_band
*sband
;
370 enum ieee80211_sta_rx_bandwidth new_bw
;
374 sband
= local
->hw
.wiphy
->bands
[band
];
376 /* ignore - no support for BF yet */
377 if (opmode
& IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF
)
380 nss
= opmode
& IEEE80211_OPMODE_NOTIF_RX_NSS_MASK
;
381 nss
>>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT
;
384 if (sta
->sta
.rx_nss
!= nss
) {
385 sta
->sta
.rx_nss
= nss
;
386 changed
|= IEEE80211_RC_NSS_CHANGED
;
392 switch (opmode
& IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK
) {
393 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ
:
394 sta
->cur_max_bandwidth
= IEEE80211_STA_RX_BW_20
;
396 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ
:
397 sta
->cur_max_bandwidth
= IEEE80211_STA_RX_BW_40
;
399 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ
:
400 sta
->cur_max_bandwidth
= IEEE80211_STA_RX_BW_80
;
402 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ
:
403 sta
->cur_max_bandwidth
= IEEE80211_STA_RX_BW_160
;
407 new_bw
= ieee80211_sta_cur_vht_bw(sta
);
408 if (new_bw
!= sta
->sta
.bandwidth
) {
409 sta
->sta
.bandwidth
= new_bw
;
410 changed
|= IEEE80211_RC_BW_CHANGED
;
416 void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data
*sdata
,
417 struct sta_info
*sta
, u8 opmode
,
418 enum ieee80211_band band
, bool nss_only
)
420 struct ieee80211_local
*local
= sdata
->local
;
421 struct ieee80211_supported_band
*sband
= local
->hw
.wiphy
->bands
[band
];
423 u32 changed
= __ieee80211_vht_handle_opmode(sdata
, sta
, opmode
,
427 rate_control_rate_update(local
, sband
, sta
, changed
);