4 * Portions of this file
5 * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <linux/ieee80211.h>
13 #include <linux/export.h>
14 #include <net/mac80211.h>
15 #include "ieee80211_i.h"
19 static void __check_vhtcap_disable(struct ieee80211_sub_if_data
*sdata
,
20 struct ieee80211_sta_vht_cap
*vht_cap
,
23 __le32 le_flag
= cpu_to_le32(flag
);
25 if (sdata
->u
.mgd
.vht_capa_mask
.vht_cap_info
& le_flag
&&
26 !(sdata
->u
.mgd
.vht_capa
.vht_cap_info
& le_flag
))
27 vht_cap
->cap
&= ~flag
;
30 void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data
*sdata
,
31 struct ieee80211_sta_vht_cap
*vht_cap
)
34 u16 rxmcs_mask
, rxmcs_cap
, rxmcs_n
, txmcs_mask
, txmcs_cap
, txmcs_n
;
36 if (!vht_cap
->vht_supported
)
39 if (sdata
->vif
.type
!= NL80211_IFTYPE_STATION
)
42 __check_vhtcap_disable(sdata
, vht_cap
,
43 IEEE80211_VHT_CAP_RXLDPC
);
44 __check_vhtcap_disable(sdata
, vht_cap
,
45 IEEE80211_VHT_CAP_SHORT_GI_80
);
46 __check_vhtcap_disable(sdata
, vht_cap
,
47 IEEE80211_VHT_CAP_SHORT_GI_160
);
48 __check_vhtcap_disable(sdata
, vht_cap
,
49 IEEE80211_VHT_CAP_TXSTBC
);
50 __check_vhtcap_disable(sdata
, vht_cap
,
51 IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE
);
52 __check_vhtcap_disable(sdata
, vht_cap
,
53 IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE
);
54 __check_vhtcap_disable(sdata
, vht_cap
,
55 IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN
);
56 __check_vhtcap_disable(sdata
, vht_cap
,
57 IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN
);
59 /* Allow user to decrease AMPDU length exponent */
60 if (sdata
->u
.mgd
.vht_capa_mask
.vht_cap_info
&
61 cpu_to_le32(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK
)) {
64 n
= le32_to_cpu(sdata
->u
.mgd
.vht_capa
.vht_cap_info
) &
65 IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK
;
66 n
>>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT
;
67 cap
= vht_cap
->cap
& IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK
;
68 cap
>>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT
;
72 ~IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK
;
74 n
<< IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT
;
78 /* Allow the user to decrease MCSes */
80 le16_to_cpu(sdata
->u
.mgd
.vht_capa_mask
.supp_mcs
.rx_mcs_map
);
81 rxmcs_n
= le16_to_cpu(sdata
->u
.mgd
.vht_capa
.supp_mcs
.rx_mcs_map
);
82 rxmcs_n
&= rxmcs_mask
;
83 rxmcs_cap
= le16_to_cpu(vht_cap
->vht_mcs
.rx_mcs_map
);
86 le16_to_cpu(sdata
->u
.mgd
.vht_capa_mask
.supp_mcs
.tx_mcs_map
);
87 txmcs_n
= le16_to_cpu(sdata
->u
.mgd
.vht_capa
.supp_mcs
.tx_mcs_map
);
88 txmcs_n
&= txmcs_mask
;
89 txmcs_cap
= le16_to_cpu(vht_cap
->vht_mcs
.tx_mcs_map
);
90 for (i
= 0; i
< 8; i
++) {
93 m
= (rxmcs_mask
>> 2*i
) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
94 n
= (rxmcs_n
>> 2*i
) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
95 c
= (rxmcs_cap
>> 2*i
) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
97 if (m
&& ((c
!= IEEE80211_VHT_MCS_NOT_SUPPORTED
&& n
< c
) ||
98 n
== IEEE80211_VHT_MCS_NOT_SUPPORTED
)) {
99 rxmcs_cap
&= ~(3 << 2*i
);
100 rxmcs_cap
|= (rxmcs_n
& (3 << 2*i
));
103 m
= (txmcs_mask
>> 2*i
) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
104 n
= (txmcs_n
>> 2*i
) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
105 c
= (txmcs_cap
>> 2*i
) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
107 if (m
&& ((c
!= IEEE80211_VHT_MCS_NOT_SUPPORTED
&& n
< c
) ||
108 n
== IEEE80211_VHT_MCS_NOT_SUPPORTED
)) {
109 txmcs_cap
&= ~(3 << 2*i
);
110 txmcs_cap
|= (txmcs_n
& (3 << 2*i
));
113 vht_cap
->vht_mcs
.rx_mcs_map
= cpu_to_le16(rxmcs_cap
);
114 vht_cap
->vht_mcs
.tx_mcs_map
= cpu_to_le16(txmcs_cap
);
118 ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data
*sdata
,
119 struct ieee80211_supported_band
*sband
,
120 const struct ieee80211_vht_cap
*vht_cap_ie
,
121 struct sta_info
*sta
)
123 struct ieee80211_sta_vht_cap
*vht_cap
= &sta
->sta
.vht_cap
;
124 struct ieee80211_sta_vht_cap own_cap
;
128 memset(vht_cap
, 0, sizeof(*vht_cap
));
130 if (!sta
->sta
.ht_cap
.ht_supported
)
133 if (!vht_cap_ie
|| !sband
->vht_cap
.vht_supported
)
136 /* Allow VHT if at least one channel on the sband supports 80 MHz */
138 for (i
= 0; i
< sband
->n_channels
; i
++) {
139 if (sband
->channels
[i
].flags
& (IEEE80211_CHAN_DISABLED
|
140 IEEE80211_CHAN_NO_80MHZ
))
151 * A VHT STA must support 40 MHz, but if we verify that here
152 * then we break a few things - some APs (e.g. Netgear R6300v2
153 * and others based on the BCM4360 chipset) will unset this
154 * capability bit when operating in 20 MHz.
157 vht_cap
->vht_supported
= true;
159 own_cap
= sband
->vht_cap
;
161 * If user has specified capability overrides, take care
162 * of that if the station we're setting up is the AP that
163 * we advertised a restricted capability set to. Override
164 * our own capabilities and then use those below.
166 if (sdata
->vif
.type
== NL80211_IFTYPE_STATION
&&
167 !test_sta_flag(sta
, WLAN_STA_TDLS_PEER
))
168 ieee80211_apply_vhtcap_overrides(sdata
, &own_cap
);
170 /* take some capabilities as-is */
171 cap_info
= le32_to_cpu(vht_cap_ie
->vht_cap_info
);
172 vht_cap
->cap
= cap_info
;
173 vht_cap
->cap
&= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895
|
174 IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991
|
175 IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454
|
176 IEEE80211_VHT_CAP_RXLDPC
|
177 IEEE80211_VHT_CAP_VHT_TXOP_PS
|
178 IEEE80211_VHT_CAP_HTC_VHT
|
179 IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK
|
180 IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB
|
181 IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB
|
182 IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN
|
183 IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN
;
185 /* and some based on our own capabilities */
186 switch (own_cap
.cap
& IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK
) {
187 case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ
:
188 vht_cap
->cap
|= cap_info
&
189 IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ
;
191 case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ
:
192 vht_cap
->cap
|= cap_info
&
193 IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK
;
200 /* symmetric capabilities */
201 vht_cap
->cap
|= cap_info
& own_cap
.cap
&
202 (IEEE80211_VHT_CAP_SHORT_GI_80
|
203 IEEE80211_VHT_CAP_SHORT_GI_160
);
206 if (own_cap
.cap
& IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE
)
207 vht_cap
->cap
|= cap_info
&
208 (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE
|
209 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK
);
211 if (own_cap
.cap
& IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE
)
212 vht_cap
->cap
|= cap_info
&
213 (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE
|
214 IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK
);
216 if (own_cap
.cap
& IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE
)
217 vht_cap
->cap
|= cap_info
&
218 IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE
;
220 if (own_cap
.cap
& IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE
)
221 vht_cap
->cap
|= cap_info
&
222 IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE
;
224 if (own_cap
.cap
& IEEE80211_VHT_CAP_TXSTBC
)
225 vht_cap
->cap
|= cap_info
& IEEE80211_VHT_CAP_RXSTBC_MASK
;
227 if (own_cap
.cap
& IEEE80211_VHT_CAP_RXSTBC_MASK
)
228 vht_cap
->cap
|= cap_info
& IEEE80211_VHT_CAP_TXSTBC
;
230 /* Copy peer MCS info, the driver might need them. */
231 memcpy(&vht_cap
->vht_mcs
, &vht_cap_ie
->supp_mcs
,
232 sizeof(struct ieee80211_vht_mcs_info
));
234 /* but also restrict MCSes */
235 for (i
= 0; i
< 8; i
++) {
236 u16 own_rx
, own_tx
, peer_rx
, peer_tx
;
238 own_rx
= le16_to_cpu(own_cap
.vht_mcs
.rx_mcs_map
);
239 own_rx
= (own_rx
>> i
* 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
241 own_tx
= le16_to_cpu(own_cap
.vht_mcs
.tx_mcs_map
);
242 own_tx
= (own_tx
>> i
* 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
244 peer_rx
= le16_to_cpu(vht_cap
->vht_mcs
.rx_mcs_map
);
245 peer_rx
= (peer_rx
>> i
* 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
247 peer_tx
= le16_to_cpu(vht_cap
->vht_mcs
.tx_mcs_map
);
248 peer_tx
= (peer_tx
>> i
* 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
250 if (peer_tx
!= IEEE80211_VHT_MCS_NOT_SUPPORTED
) {
251 if (own_rx
== IEEE80211_VHT_MCS_NOT_SUPPORTED
)
252 peer_tx
= IEEE80211_VHT_MCS_NOT_SUPPORTED
;
253 else if (own_rx
< peer_tx
)
257 if (peer_rx
!= IEEE80211_VHT_MCS_NOT_SUPPORTED
) {
258 if (own_tx
== IEEE80211_VHT_MCS_NOT_SUPPORTED
)
259 peer_rx
= IEEE80211_VHT_MCS_NOT_SUPPORTED
;
260 else if (own_tx
< peer_rx
)
264 vht_cap
->vht_mcs
.rx_mcs_map
&=
265 ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED
<< i
* 2);
266 vht_cap
->vht_mcs
.rx_mcs_map
|= cpu_to_le16(peer_rx
<< i
* 2);
268 vht_cap
->vht_mcs
.tx_mcs_map
&=
269 ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED
<< i
* 2);
270 vht_cap
->vht_mcs
.tx_mcs_map
|= cpu_to_le16(peer_tx
<< i
* 2);
273 /* finally set up the bandwidth */
274 switch (vht_cap
->cap
& IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK
) {
275 case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ
:
276 case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ
:
277 sta
->cur_max_bandwidth
= IEEE80211_STA_RX_BW_160
;
280 sta
->cur_max_bandwidth
= IEEE80211_STA_RX_BW_80
;
283 sta
->sta
.bandwidth
= ieee80211_sta_cur_vht_bw(sta
);
285 /* If HT IE reported 3839 bytes only, stay with that size. */
286 if (sta
->sta
.max_amsdu_len
== IEEE80211_MAX_MPDU_LEN_HT_3839
)
289 switch (vht_cap
->cap
& IEEE80211_VHT_CAP_MAX_MPDU_MASK
) {
290 case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454
:
291 sta
->sta
.max_amsdu_len
= IEEE80211_MAX_MPDU_LEN_VHT_11454
;
293 case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991
:
294 sta
->sta
.max_amsdu_len
= IEEE80211_MAX_MPDU_LEN_VHT_7991
;
296 case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895
:
298 sta
->sta
.max_amsdu_len
= IEEE80211_MAX_MPDU_LEN_VHT_3895
;
303 enum ieee80211_sta_rx_bandwidth
ieee80211_sta_cap_rx_bw(struct sta_info
*sta
)
305 struct ieee80211_sta_vht_cap
*vht_cap
= &sta
->sta
.vht_cap
;
308 if (!vht_cap
->vht_supported
)
309 return sta
->sta
.ht_cap
.cap
& IEEE80211_HT_CAP_SUP_WIDTH_20_40
?
310 IEEE80211_STA_RX_BW_40
:
311 IEEE80211_STA_RX_BW_20
;
313 cap_width
= vht_cap
->cap
& IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK
;
315 if (cap_width
== IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ
||
316 cap_width
== IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ
)
317 return IEEE80211_STA_RX_BW_160
;
319 return IEEE80211_STA_RX_BW_80
;
322 enum nl80211_chan_width
ieee80211_sta_cap_chan_bw(struct sta_info
*sta
)
324 struct ieee80211_sta_vht_cap
*vht_cap
= &sta
->sta
.vht_cap
;
327 if (!vht_cap
->vht_supported
) {
328 if (!sta
->sta
.ht_cap
.ht_supported
)
329 return NL80211_CHAN_WIDTH_20_NOHT
;
331 return sta
->sta
.ht_cap
.cap
& IEEE80211_HT_CAP_SUP_WIDTH_20_40
?
332 NL80211_CHAN_WIDTH_40
: NL80211_CHAN_WIDTH_20
;
335 cap_width
= vht_cap
->cap
& IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK
;
337 if (cap_width
== IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ
)
338 return NL80211_CHAN_WIDTH_160
;
339 else if (cap_width
== IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ
)
340 return NL80211_CHAN_WIDTH_80P80
;
342 return NL80211_CHAN_WIDTH_80
;
345 enum ieee80211_sta_rx_bandwidth
346 ieee80211_chan_width_to_rx_bw(enum nl80211_chan_width width
)
349 case NL80211_CHAN_WIDTH_20_NOHT
:
350 case NL80211_CHAN_WIDTH_20
:
351 return IEEE80211_STA_RX_BW_20
;
352 case NL80211_CHAN_WIDTH_40
:
353 return IEEE80211_STA_RX_BW_40
;
354 case NL80211_CHAN_WIDTH_80
:
355 return IEEE80211_STA_RX_BW_80
;
356 case NL80211_CHAN_WIDTH_160
:
357 case NL80211_CHAN_WIDTH_80P80
:
358 return IEEE80211_STA_RX_BW_160
;
361 return IEEE80211_STA_RX_BW_20
;
365 enum ieee80211_sta_rx_bandwidth
ieee80211_sta_cur_vht_bw(struct sta_info
*sta
)
367 struct ieee80211_sub_if_data
*sdata
= sta
->sdata
;
368 enum ieee80211_sta_rx_bandwidth bw
;
369 enum nl80211_chan_width bss_width
= sdata
->vif
.bss_conf
.chandef
.width
;
371 bw
= ieee80211_sta_cap_rx_bw(sta
);
372 bw
= min(bw
, sta
->cur_max_bandwidth
);
373 bw
= min(bw
, ieee80211_chan_width_to_rx_bw(bss_width
));
378 void ieee80211_sta_set_rx_nss(struct sta_info
*sta
)
380 u8 ht_rx_nss
= 0, vht_rx_nss
= 0;
382 /* if we received a notification already don't overwrite it */
386 if (sta
->sta
.ht_cap
.ht_supported
) {
387 if (sta
->sta
.ht_cap
.mcs
.rx_mask
[0])
389 if (sta
->sta
.ht_cap
.mcs
.rx_mask
[1])
391 if (sta
->sta
.ht_cap
.mcs
.rx_mask
[2])
393 if (sta
->sta
.ht_cap
.mcs
.rx_mask
[3])
395 /* FIXME: consider rx_highest? */
398 if (sta
->sta
.vht_cap
.vht_supported
) {
402 rx_mcs_map
= le16_to_cpu(sta
->sta
.vht_cap
.vht_mcs
.rx_mcs_map
);
404 for (i
= 7; i
>= 0; i
--) {
405 u8 mcs
= (rx_mcs_map
>> (2 * i
)) & 3;
407 if (mcs
!= IEEE80211_VHT_MCS_NOT_SUPPORTED
) {
412 /* FIXME: consider rx_highest? */
415 ht_rx_nss
= max(ht_rx_nss
, vht_rx_nss
);
416 sta
->sta
.rx_nss
= max_t(u8
, 1, ht_rx_nss
);
419 u32
__ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data
*sdata
,
420 struct sta_info
*sta
, u8 opmode
,
421 enum nl80211_band band
)
423 struct ieee80211_local
*local
= sdata
->local
;
424 struct ieee80211_supported_band
*sband
;
425 enum ieee80211_sta_rx_bandwidth new_bw
;
429 sband
= local
->hw
.wiphy
->bands
[band
];
431 /* ignore - no support for BF yet */
432 if (opmode
& IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF
)
435 nss
= opmode
& IEEE80211_OPMODE_NOTIF_RX_NSS_MASK
;
436 nss
>>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT
;
439 if (sta
->sta
.rx_nss
!= nss
) {
440 sta
->sta
.rx_nss
= nss
;
441 changed
|= IEEE80211_RC_NSS_CHANGED
;
444 switch (opmode
& IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK
) {
445 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ
:
446 sta
->cur_max_bandwidth
= IEEE80211_STA_RX_BW_20
;
448 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ
:
449 sta
->cur_max_bandwidth
= IEEE80211_STA_RX_BW_40
;
451 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ
:
452 sta
->cur_max_bandwidth
= IEEE80211_STA_RX_BW_80
;
454 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ
:
455 sta
->cur_max_bandwidth
= IEEE80211_STA_RX_BW_160
;
459 new_bw
= ieee80211_sta_cur_vht_bw(sta
);
460 if (new_bw
!= sta
->sta
.bandwidth
) {
461 sta
->sta
.bandwidth
= new_bw
;
462 changed
|= IEEE80211_RC_BW_CHANGED
;
468 void ieee80211_process_mu_groups(struct ieee80211_sub_if_data
*sdata
,
469 struct ieee80211_mgmt
*mgmt
)
471 struct ieee80211_bss_conf
*bss_conf
= &sdata
->vif
.bss_conf
;
473 if (!sdata
->vif
.mu_mimo_owner
)
476 if (!memcmp(mgmt
->u
.action
.u
.vht_group_notif
.position
,
477 bss_conf
->mu_group
.position
, WLAN_USER_POSITION_LEN
) &&
478 !memcmp(mgmt
->u
.action
.u
.vht_group_notif
.membership
,
479 bss_conf
->mu_group
.membership
, WLAN_MEMBERSHIP_LEN
))
482 memcpy(bss_conf
->mu_group
.membership
,
483 mgmt
->u
.action
.u
.vht_group_notif
.membership
,
484 WLAN_MEMBERSHIP_LEN
);
485 memcpy(bss_conf
->mu_group
.position
,
486 mgmt
->u
.action
.u
.vht_group_notif
.position
,
487 WLAN_USER_POSITION_LEN
);
489 ieee80211_bss_info_change_notify(sdata
, BSS_CHANGED_MU_GROUPS
);
492 void ieee80211_update_mu_groups(struct ieee80211_vif
*vif
,
493 const u8
*membership
, const u8
*position
)
495 struct ieee80211_bss_conf
*bss_conf
= &vif
->bss_conf
;
497 if (WARN_ON_ONCE(!vif
->mu_mimo_owner
))
500 memcpy(bss_conf
->mu_group
.membership
, membership
, WLAN_MEMBERSHIP_LEN
);
501 memcpy(bss_conf
->mu_group
.position
, position
, WLAN_USER_POSITION_LEN
);
503 EXPORT_SYMBOL_GPL(ieee80211_update_mu_groups
);
505 void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data
*sdata
,
506 struct sta_info
*sta
, u8 opmode
,
507 enum nl80211_band band
)
509 struct ieee80211_local
*local
= sdata
->local
;
510 struct ieee80211_supported_band
*sband
= local
->hw
.wiphy
->bands
[band
];
512 u32 changed
= __ieee80211_vht_handle_opmode(sdata
, sta
, opmode
, band
);
515 rate_control_rate_update(local
, sband
, sta
, changed
);
518 void ieee80211_get_vht_mask_from_cap(__le16 vht_cap
,
519 u16 vht_mask
[NL80211_VHT_NSS_MAX
])
522 u16 mask
, cap
= le16_to_cpu(vht_cap
);
524 for (i
= 0; i
< NL80211_VHT_NSS_MAX
; i
++) {
525 mask
= (cap
>> i
* 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED
;
527 case IEEE80211_VHT_MCS_SUPPORT_0_7
:
528 vht_mask
[i
] = 0x00FF;
530 case IEEE80211_VHT_MCS_SUPPORT_0_8
:
531 vht_mask
[i
] = 0x01FF;
533 case IEEE80211_VHT_MCS_SUPPORT_0_9
:
534 vht_mask
[i
] = 0x03FF;
536 case IEEE80211_VHT_MCS_NOT_SUPPORTED
: