1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2818255eaSMahesh Palivela /* 3818255eaSMahesh Palivela * VHT handling 4818255eaSMahesh Palivela * 523a1f8d4SSara Sharon * Portions of this file 665554d07SSara Sharon * Copyright(c) 2015 - 2016 Intel Deutschland GmbH 79166cc49SJohannes Berg * Copyright (C) 2018 - 2020 Intel Corporation 8818255eaSMahesh Palivela */ 9818255eaSMahesh Palivela 10818255eaSMahesh Palivela #include <linux/ieee80211.h> 11818255eaSMahesh Palivela #include <linux/export.h> 12818255eaSMahesh Palivela #include <net/mac80211.h> 13818255eaSMahesh Palivela #include "ieee80211_i.h" 140af83d3dSJohannes Berg #include "rate.h" 15818255eaSMahesh Palivela 16818255eaSMahesh Palivela 17dd5ecfeaSJohannes Berg static void __check_vhtcap_disable(struct ieee80211_sub_if_data *sdata, 18dd5ecfeaSJohannes Berg struct ieee80211_sta_vht_cap *vht_cap, 19dd5ecfeaSJohannes Berg u32 flag) 20dd5ecfeaSJohannes Berg { 21dd5ecfeaSJohannes Berg __le32 le_flag = cpu_to_le32(flag); 22dd5ecfeaSJohannes Berg 23dd5ecfeaSJohannes Berg if (sdata->u.mgd.vht_capa_mask.vht_cap_info & le_flag && 24dd5ecfeaSJohannes Berg !(sdata->u.mgd.vht_capa.vht_cap_info & le_flag)) 25dd5ecfeaSJohannes Berg vht_cap->cap &= ~flag; 26dd5ecfeaSJohannes Berg } 27dd5ecfeaSJohannes Berg 28dd5ecfeaSJohannes Berg void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata, 29dd5ecfeaSJohannes Berg struct ieee80211_sta_vht_cap *vht_cap) 30dd5ecfeaSJohannes Berg { 31dd5ecfeaSJohannes Berg int i; 32dd5ecfeaSJohannes Berg u16 rxmcs_mask, rxmcs_cap, rxmcs_n, txmcs_mask, txmcs_cap, txmcs_n; 33dd5ecfeaSJohannes Berg 34dd5ecfeaSJohannes Berg if (!vht_cap->vht_supported) 35dd5ecfeaSJohannes Berg return; 36dd5ecfeaSJohannes Berg 37dd5ecfeaSJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_STATION) 38dd5ecfeaSJohannes Berg return; 39dd5ecfeaSJohannes Berg 40dd5ecfeaSJohannes Berg __check_vhtcap_disable(sdata, vht_cap, 41dd5ecfeaSJohannes Berg IEEE80211_VHT_CAP_RXLDPC); 42dd5ecfeaSJohannes Berg __check_vhtcap_disable(sdata, vht_cap, 43dd5ecfeaSJohannes Berg IEEE80211_VHT_CAP_SHORT_GI_80); 44dd5ecfeaSJohannes Berg __check_vhtcap_disable(sdata, vht_cap, 45dd5ecfeaSJohannes Berg IEEE80211_VHT_CAP_SHORT_GI_160); 46dd5ecfeaSJohannes Berg __check_vhtcap_disable(sdata, vht_cap, 47dd5ecfeaSJohannes Berg IEEE80211_VHT_CAP_TXSTBC); 48dd5ecfeaSJohannes Berg __check_vhtcap_disable(sdata, vht_cap, 49dd5ecfeaSJohannes Berg IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); 50dd5ecfeaSJohannes Berg __check_vhtcap_disable(sdata, vht_cap, 51dd5ecfeaSJohannes Berg IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); 52dd5ecfeaSJohannes Berg __check_vhtcap_disable(sdata, vht_cap, 53dd5ecfeaSJohannes Berg IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN); 54dd5ecfeaSJohannes Berg __check_vhtcap_disable(sdata, vht_cap, 55dd5ecfeaSJohannes Berg IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN); 56dd5ecfeaSJohannes Berg 57dd5ecfeaSJohannes Berg /* Allow user to decrease AMPDU length exponent */ 58dd5ecfeaSJohannes Berg if (sdata->u.mgd.vht_capa_mask.vht_cap_info & 59dd5ecfeaSJohannes Berg cpu_to_le32(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK)) { 60dd5ecfeaSJohannes Berg u32 cap, n; 61dd5ecfeaSJohannes Berg 62dd5ecfeaSJohannes Berg n = le32_to_cpu(sdata->u.mgd.vht_capa.vht_cap_info) & 63dd5ecfeaSJohannes Berg IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; 64dd5ecfeaSJohannes Berg n >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; 65dd5ecfeaSJohannes Berg cap = vht_cap->cap & IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; 66dd5ecfeaSJohannes Berg cap >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; 67dd5ecfeaSJohannes Berg 68dd5ecfeaSJohannes Berg if (n < cap) { 69dd5ecfeaSJohannes Berg vht_cap->cap &= 70dd5ecfeaSJohannes Berg ~IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; 71dd5ecfeaSJohannes Berg vht_cap->cap |= 72dd5ecfeaSJohannes Berg n << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; 73dd5ecfeaSJohannes Berg } 74dd5ecfeaSJohannes Berg } 75dd5ecfeaSJohannes Berg 76dd5ecfeaSJohannes Berg /* Allow the user to decrease MCSes */ 77dd5ecfeaSJohannes Berg rxmcs_mask = 78dd5ecfeaSJohannes Berg le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.rx_mcs_map); 79dd5ecfeaSJohannes Berg rxmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.rx_mcs_map); 80dd5ecfeaSJohannes Berg rxmcs_n &= rxmcs_mask; 81dd5ecfeaSJohannes Berg rxmcs_cap = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); 82dd5ecfeaSJohannes Berg 83dd5ecfeaSJohannes Berg txmcs_mask = 84dd5ecfeaSJohannes Berg le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.tx_mcs_map); 85dd5ecfeaSJohannes Berg txmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.tx_mcs_map); 86dd5ecfeaSJohannes Berg txmcs_n &= txmcs_mask; 87dd5ecfeaSJohannes Berg txmcs_cap = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); 88dd5ecfeaSJohannes Berg for (i = 0; i < 8; i++) { 89dd5ecfeaSJohannes Berg u8 m, n, c; 90dd5ecfeaSJohannes Berg 91dd5ecfeaSJohannes Berg m = (rxmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 92dd5ecfeaSJohannes Berg n = (rxmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 93dd5ecfeaSJohannes Berg c = (rxmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 94dd5ecfeaSJohannes Berg 95dd5ecfeaSJohannes Berg if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || 96dd5ecfeaSJohannes Berg n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { 97dd5ecfeaSJohannes Berg rxmcs_cap &= ~(3 << 2*i); 98dd5ecfeaSJohannes Berg rxmcs_cap |= (rxmcs_n & (3 << 2*i)); 99dd5ecfeaSJohannes Berg } 100dd5ecfeaSJohannes Berg 101dd5ecfeaSJohannes Berg m = (txmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 102dd5ecfeaSJohannes Berg n = (txmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 103dd5ecfeaSJohannes Berg c = (txmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 104dd5ecfeaSJohannes Berg 105dd5ecfeaSJohannes Berg if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || 106dd5ecfeaSJohannes Berg n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { 107dd5ecfeaSJohannes Berg txmcs_cap &= ~(3 << 2*i); 108dd5ecfeaSJohannes Berg txmcs_cap |= (txmcs_n & (3 << 2*i)); 109dd5ecfeaSJohannes Berg } 110dd5ecfeaSJohannes Berg } 111dd5ecfeaSJohannes Berg vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(rxmcs_cap); 112dd5ecfeaSJohannes Berg vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(txmcs_cap); 113dd5ecfeaSJohannes Berg } 114dd5ecfeaSJohannes Berg 1154a3cb702SJohannes Berg void 1164a3cb702SJohannes Berg ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, 117818255eaSMahesh Palivela struct ieee80211_supported_band *sband, 1184a3cb702SJohannes Berg const struct ieee80211_vht_cap *vht_cap_ie, 1194a34215eSJohannes Berg struct sta_info *sta) 120818255eaSMahesh Palivela { 1214a34215eSJohannes Berg struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; 12255d942f4SJohannes Berg struct ieee80211_sta_vht_cap own_cap; 12355d942f4SJohannes Berg u32 cap_info, i; 12452a45f38SArik Nemtsov bool have_80mhz; 125818255eaSMahesh Palivela 126818255eaSMahesh Palivela memset(vht_cap, 0, sizeof(*vht_cap)); 127818255eaSMahesh Palivela 1284a34215eSJohannes Berg if (!sta->sta.ht_cap.ht_supported) 1294a34215eSJohannes Berg return; 1304a34215eSJohannes Berg 131818255eaSMahesh Palivela if (!vht_cap_ie || !sband->vht_cap.vht_supported) 132818255eaSMahesh Palivela return; 133818255eaSMahesh Palivela 13452a45f38SArik Nemtsov /* Allow VHT if at least one channel on the sband supports 80 MHz */ 13552a45f38SArik Nemtsov have_80mhz = false; 13652a45f38SArik Nemtsov for (i = 0; i < sband->n_channels; i++) { 13752a45f38SArik Nemtsov if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED | 13852a45f38SArik Nemtsov IEEE80211_CHAN_NO_80MHZ)) 13952a45f38SArik Nemtsov continue; 14052a45f38SArik Nemtsov 14152a45f38SArik Nemtsov have_80mhz = true; 14252a45f38SArik Nemtsov break; 14352a45f38SArik Nemtsov } 14452a45f38SArik Nemtsov 14552a45f38SArik Nemtsov if (!have_80mhz) 14652a45f38SArik Nemtsov return; 14752a45f38SArik Nemtsov 1484a817aa7SJohannes Berg /* 1494a817aa7SJohannes Berg * A VHT STA must support 40 MHz, but if we verify that here 1504a817aa7SJohannes Berg * then we break a few things - some APs (e.g. Netgear R6300v2 1514a817aa7SJohannes Berg * and others based on the BCM4360 chipset) will unset this 1524a817aa7SJohannes Berg * capability bit when operating in 20 MHz. 1534a817aa7SJohannes Berg */ 154e1a0c6b3SJohannes Berg 155818255eaSMahesh Palivela vht_cap->vht_supported = true; 156818255eaSMahesh Palivela 15755d942f4SJohannes Berg own_cap = sband->vht_cap; 15855d942f4SJohannes Berg /* 15955d942f4SJohannes Berg * If user has specified capability overrides, take care 16055d942f4SJohannes Berg * of that if the station we're setting up is the AP that 16155d942f4SJohannes Berg * we advertised a restricted capability set to. Override 16255d942f4SJohannes Berg * our own capabilities and then use those below. 16355d942f4SJohannes Berg */ 16455d942f4SJohannes Berg if (sdata->vif.type == NL80211_IFTYPE_STATION && 16555d942f4SJohannes Berg !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) 16655d942f4SJohannes Berg ieee80211_apply_vhtcap_overrides(sdata, &own_cap); 16755d942f4SJohannes Berg 16855d942f4SJohannes Berg /* take some capabilities as-is */ 16955d942f4SJohannes Berg cap_info = le32_to_cpu(vht_cap_ie->vht_cap_info); 17055d942f4SJohannes Berg vht_cap->cap = cap_info; 1713bd5c7a2SFelix Fietkau vht_cap->cap &= IEEE80211_VHT_CAP_RXLDPC | 17255d942f4SJohannes Berg IEEE80211_VHT_CAP_VHT_TXOP_PS | 17355d942f4SJohannes Berg IEEE80211_VHT_CAP_HTC_VHT | 17455d942f4SJohannes Berg IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | 17555d942f4SJohannes Berg IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB | 17655d942f4SJohannes Berg IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB | 17755d942f4SJohannes Berg IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | 17855d942f4SJohannes Berg IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; 17955d942f4SJohannes Berg 1803bd5c7a2SFelix Fietkau vht_cap->cap |= min_t(u32, cap_info & IEEE80211_VHT_CAP_MAX_MPDU_MASK, 1813bd5c7a2SFelix Fietkau own_cap.cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK); 1823bd5c7a2SFelix Fietkau 18355d942f4SJohannes Berg /* and some based on our own capabilities */ 18455d942f4SJohannes Berg switch (own_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { 18555d942f4SJohannes Berg case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: 18655d942f4SJohannes Berg vht_cap->cap |= cap_info & 18755d942f4SJohannes Berg IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; 18855d942f4SJohannes Berg break; 18955d942f4SJohannes Berg case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: 19055d942f4SJohannes Berg vht_cap->cap |= cap_info & 19155d942f4SJohannes Berg IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 19255d942f4SJohannes Berg break; 19355d942f4SJohannes Berg default: 19455d942f4SJohannes Berg /* nothing */ 19555d942f4SJohannes Berg break; 19655d942f4SJohannes Berg } 19755d942f4SJohannes Berg 19855d942f4SJohannes Berg /* symmetric capabilities */ 19955d942f4SJohannes Berg vht_cap->cap |= cap_info & own_cap.cap & 20055d942f4SJohannes Berg (IEEE80211_VHT_CAP_SHORT_GI_80 | 20155d942f4SJohannes Berg IEEE80211_VHT_CAP_SHORT_GI_160); 20255d942f4SJohannes Berg 20355d942f4SJohannes Berg /* remaining ones */ 204fbdd90eaSEyal Shapira if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) 20555d942f4SJohannes Berg vht_cap->cap |= cap_info & 20655d942f4SJohannes Berg (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | 207fbdd90eaSEyal Shapira IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK); 20855d942f4SJohannes Berg 20955d942f4SJohannes Berg if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) 21055d942f4SJohannes Berg vht_cap->cap |= cap_info & 2115eb7906bSEliad Peller (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | 212fbdd90eaSEyal Shapira IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK); 21355d942f4SJohannes Berg 21455d942f4SJohannes Berg if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) 21555d942f4SJohannes Berg vht_cap->cap |= cap_info & 21655d942f4SJohannes Berg IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; 21755d942f4SJohannes Berg 21855d942f4SJohannes Berg if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) 21955d942f4SJohannes Berg vht_cap->cap |= cap_info & 22055d942f4SJohannes Berg IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; 22155d942f4SJohannes Berg 22255d942f4SJohannes Berg if (own_cap.cap & IEEE80211_VHT_CAP_TXSTBC) 22355d942f4SJohannes Berg vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_RXSTBC_MASK; 22455d942f4SJohannes Berg 22555d942f4SJohannes Berg if (own_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK) 22655d942f4SJohannes Berg vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_TXSTBC; 227818255eaSMahesh Palivela 228818255eaSMahesh Palivela /* Copy peer MCS info, the driver might need them. */ 229818255eaSMahesh Palivela memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, 230818255eaSMahesh Palivela sizeof(struct ieee80211_vht_mcs_info)); 231e1a0c6b3SJohannes Berg 232e80d6425SJohannes Berg /* copy EXT_NSS_BW Support value or remove the capability */ 233e80d6425SJohannes Berg if (ieee80211_hw_check(&sdata->local->hw, SUPPORTS_VHT_EXT_NSS_BW)) 234e80d6425SJohannes Berg vht_cap->cap |= (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK); 235e80d6425SJohannes Berg else 236e80d6425SJohannes Berg vht_cap->vht_mcs.tx_highest &= 237e80d6425SJohannes Berg ~cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); 238e80d6425SJohannes Berg 23955d942f4SJohannes Berg /* but also restrict MCSes */ 24055d942f4SJohannes Berg for (i = 0; i < 8; i++) { 24155d942f4SJohannes Berg u16 own_rx, own_tx, peer_rx, peer_tx; 24255d942f4SJohannes Berg 24355d942f4SJohannes Berg own_rx = le16_to_cpu(own_cap.vht_mcs.rx_mcs_map); 24455d942f4SJohannes Berg own_rx = (own_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 24555d942f4SJohannes Berg 24655d942f4SJohannes Berg own_tx = le16_to_cpu(own_cap.vht_mcs.tx_mcs_map); 24755d942f4SJohannes Berg own_tx = (own_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 24855d942f4SJohannes Berg 24955d942f4SJohannes Berg peer_rx = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); 25055d942f4SJohannes Berg peer_rx = (peer_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 25155d942f4SJohannes Berg 25255d942f4SJohannes Berg peer_tx = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); 25355d942f4SJohannes Berg peer_tx = (peer_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 25455d942f4SJohannes Berg 25555d942f4SJohannes Berg if (peer_tx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 25655d942f4SJohannes Berg if (own_rx == IEEE80211_VHT_MCS_NOT_SUPPORTED) 25755d942f4SJohannes Berg peer_tx = IEEE80211_VHT_MCS_NOT_SUPPORTED; 25855d942f4SJohannes Berg else if (own_rx < peer_tx) 25955d942f4SJohannes Berg peer_tx = own_rx; 26055d942f4SJohannes Berg } 26155d942f4SJohannes Berg 26255d942f4SJohannes Berg if (peer_rx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 26355d942f4SJohannes Berg if (own_tx == IEEE80211_VHT_MCS_NOT_SUPPORTED) 26455d942f4SJohannes Berg peer_rx = IEEE80211_VHT_MCS_NOT_SUPPORTED; 26555d942f4SJohannes Berg else if (own_tx < peer_rx) 26655d942f4SJohannes Berg peer_rx = own_tx; 26755d942f4SJohannes Berg } 26855d942f4SJohannes Berg 26955d942f4SJohannes Berg vht_cap->vht_mcs.rx_mcs_map &= 27055d942f4SJohannes Berg ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); 27155d942f4SJohannes Berg vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(peer_rx << i * 2); 27255d942f4SJohannes Berg 27355d942f4SJohannes Berg vht_cap->vht_mcs.tx_mcs_map &= 27455d942f4SJohannes Berg ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); 27555d942f4SJohannes Berg vht_cap->vht_mcs.tx_mcs_map |= cpu_to_le16(peer_tx << i * 2); 27655d942f4SJohannes Berg } 27755d942f4SJohannes Berg 278c8eaf347SFilip Matusiak /* 279c8eaf347SFilip Matusiak * This is a workaround for VHT-enabled STAs which break the spec 280c8eaf347SFilip Matusiak * and have the VHT-MCS Rx map filled in with value 3 for all eight 281c8eaf347SFilip Matusiak * spacial streams, an example is AR9462. 282c8eaf347SFilip Matusiak * 283c8eaf347SFilip Matusiak * As per spec, in section 22.1.1 Introduction to the VHT PHY 284c8eaf347SFilip Matusiak * A VHT STA shall support at least single spactial stream VHT-MCSs 285c8eaf347SFilip Matusiak * 0 to 7 (transmit and receive) in all supported channel widths. 286c8eaf347SFilip Matusiak */ 287c8eaf347SFilip Matusiak if (vht_cap->vht_mcs.rx_mcs_map == cpu_to_le16(0xFFFF)) { 288c8eaf347SFilip Matusiak vht_cap->vht_supported = false; 289c8eaf347SFilip Matusiak sdata_info(sdata, "Ignoring VHT IE from %pM due to invalid rx_mcs_map\n", 290c8eaf347SFilip Matusiak sta->addr); 291c8eaf347SFilip Matusiak return; 292c8eaf347SFilip Matusiak } 293c8eaf347SFilip Matusiak 29455d942f4SJohannes Berg /* finally set up the bandwidth */ 2950af83d3dSJohannes Berg switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { 2960af83d3dSJohannes Berg case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: 2970af83d3dSJohannes Berg case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: 2980af83d3dSJohannes Berg sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; 2990af83d3dSJohannes Berg break; 3000af83d3dSJohannes Berg default: 3010af83d3dSJohannes Berg sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; 302e80d6425SJohannes Berg 303e80d6425SJohannes Berg if (!(vht_cap->vht_mcs.tx_highest & 304e80d6425SJohannes Berg cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE))) 305e80d6425SJohannes Berg break; 306e80d6425SJohannes Berg 307e80d6425SJohannes Berg /* 308e80d6425SJohannes Berg * If this is non-zero, then it does support 160 MHz after all, 309e80d6425SJohannes Berg * in one form or the other. We don't distinguish here (or even 310e80d6425SJohannes Berg * above) between 160 and 80+80 yet. 311e80d6425SJohannes Berg */ 312e80d6425SJohannes Berg if (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) 313e80d6425SJohannes Berg sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; 3140af83d3dSJohannes Berg } 3150af83d3dSJohannes Berg 316e1a0c6b3SJohannes Berg sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); 317506bcfa8SEmmanuel Grumbach 318506bcfa8SEmmanuel Grumbach switch (vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) { 319506bcfa8SEmmanuel Grumbach case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: 320506bcfa8SEmmanuel Grumbach sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454; 321506bcfa8SEmmanuel Grumbach break; 322506bcfa8SEmmanuel Grumbach case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991: 323506bcfa8SEmmanuel Grumbach sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991; 324506bcfa8SEmmanuel Grumbach break; 325506bcfa8SEmmanuel Grumbach case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895: 326506bcfa8SEmmanuel Grumbach default: 327506bcfa8SEmmanuel Grumbach sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895; 328506bcfa8SEmmanuel Grumbach break; 329506bcfa8SEmmanuel Grumbach } 330e1a0c6b3SJohannes Berg } 331e1a0c6b3SJohannes Berg 332cf2c9cc3SJohannes Berg /* FIXME: move this to some better location - parses HE now */ 3331c45c5ceSEliad Peller enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta) 3341c45c5ceSEliad Peller { 3351c45c5ceSEliad Peller struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; 336cf2c9cc3SJohannes Berg struct ieee80211_sta_he_cap *he_cap = &sta->sta.he_cap; 3371c45c5ceSEliad Peller u32 cap_width; 3381c45c5ceSEliad Peller 339cf2c9cc3SJohannes Berg if (he_cap->has_he) { 340cf2c9cc3SJohannes Berg u8 info = he_cap->he_cap_elem.phy_cap_info[0]; 341cf2c9cc3SJohannes Berg 342cf2c9cc3SJohannes Berg if (sta->sdata->vif.bss_conf.chandef.chan->band == 343cf2c9cc3SJohannes Berg NL80211_BAND_2GHZ) { 344cf2c9cc3SJohannes Berg if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) 345cf2c9cc3SJohannes Berg return IEEE80211_STA_RX_BW_40; 346cf2c9cc3SJohannes Berg else 347cf2c9cc3SJohannes Berg return IEEE80211_STA_RX_BW_20; 348cf2c9cc3SJohannes Berg } 349cf2c9cc3SJohannes Berg 350cf2c9cc3SJohannes Berg if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G || 351cf2c9cc3SJohannes Berg info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) 352cf2c9cc3SJohannes Berg return IEEE80211_STA_RX_BW_160; 353cf2c9cc3SJohannes Berg else if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) 354cf2c9cc3SJohannes Berg return IEEE80211_STA_RX_BW_80; 355cf2c9cc3SJohannes Berg 356cf2c9cc3SJohannes Berg return IEEE80211_STA_RX_BW_20; 357cf2c9cc3SJohannes Berg } 358cf2c9cc3SJohannes Berg 3591c45c5ceSEliad Peller if (!vht_cap->vht_supported) 3601c45c5ceSEliad Peller return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 3611c45c5ceSEliad Peller IEEE80211_STA_RX_BW_40 : 3621c45c5ceSEliad Peller IEEE80211_STA_RX_BW_20; 3631c45c5ceSEliad Peller 3641c45c5ceSEliad Peller cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 3651c45c5ceSEliad Peller 3661c45c5ceSEliad Peller if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ || 3671c45c5ceSEliad Peller cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) 3681c45c5ceSEliad Peller return IEEE80211_STA_RX_BW_160; 3691c45c5ceSEliad Peller 370e5c0b0ffSMordechay Goodstein /* 371e5c0b0ffSMordechay Goodstein * If this is non-zero, then it does support 160 MHz after all, 372e5c0b0ffSMordechay Goodstein * in one form or the other. We don't distinguish here (or even 373e5c0b0ffSMordechay Goodstein * above) between 160 and 80+80 yet. 374e5c0b0ffSMordechay Goodstein */ 375e5c0b0ffSMordechay Goodstein if (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) 376e5c0b0ffSMordechay Goodstein return IEEE80211_STA_RX_BW_160; 377e5c0b0ffSMordechay Goodstein 3781c45c5ceSEliad Peller return IEEE80211_STA_RX_BW_80; 3791c45c5ceSEliad Peller } 3801c45c5ceSEliad Peller 38159021c67SArik Nemtsov enum nl80211_chan_width ieee80211_sta_cap_chan_bw(struct sta_info *sta) 38259021c67SArik Nemtsov { 38359021c67SArik Nemtsov struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; 38459021c67SArik Nemtsov u32 cap_width; 38559021c67SArik Nemtsov 38659021c67SArik Nemtsov if (!vht_cap->vht_supported) { 38759021c67SArik Nemtsov if (!sta->sta.ht_cap.ht_supported) 38859021c67SArik Nemtsov return NL80211_CHAN_WIDTH_20_NOHT; 38959021c67SArik Nemtsov 39059021c67SArik Nemtsov return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 39159021c67SArik Nemtsov NL80211_CHAN_WIDTH_40 : NL80211_CHAN_WIDTH_20; 39259021c67SArik Nemtsov } 39359021c67SArik Nemtsov 39459021c67SArik Nemtsov cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 39559021c67SArik Nemtsov 39659021c67SArik Nemtsov if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) 39759021c67SArik Nemtsov return NL80211_CHAN_WIDTH_160; 39859021c67SArik Nemtsov else if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) 39959021c67SArik Nemtsov return NL80211_CHAN_WIDTH_80P80; 40059021c67SArik Nemtsov 40159021c67SArik Nemtsov return NL80211_CHAN_WIDTH_80; 40259021c67SArik Nemtsov } 40359021c67SArik Nemtsov 40497f5f425Stamizhr@codeaurora.org enum nl80211_chan_width 40597f5f425Stamizhr@codeaurora.org ieee80211_sta_rx_bw_to_chan_width(struct sta_info *sta) 40697f5f425Stamizhr@codeaurora.org { 40797f5f425Stamizhr@codeaurora.org enum ieee80211_sta_rx_bandwidth cur_bw = sta->sta.bandwidth; 40897f5f425Stamizhr@codeaurora.org struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; 40997f5f425Stamizhr@codeaurora.org u32 cap_width; 41097f5f425Stamizhr@codeaurora.org 41197f5f425Stamizhr@codeaurora.org switch (cur_bw) { 41297f5f425Stamizhr@codeaurora.org case IEEE80211_STA_RX_BW_20: 41397f5f425Stamizhr@codeaurora.org if (!sta->sta.ht_cap.ht_supported) 41497f5f425Stamizhr@codeaurora.org return NL80211_CHAN_WIDTH_20_NOHT; 41597f5f425Stamizhr@codeaurora.org else 41697f5f425Stamizhr@codeaurora.org return NL80211_CHAN_WIDTH_20; 41797f5f425Stamizhr@codeaurora.org case IEEE80211_STA_RX_BW_40: 41897f5f425Stamizhr@codeaurora.org return NL80211_CHAN_WIDTH_40; 41997f5f425Stamizhr@codeaurora.org case IEEE80211_STA_RX_BW_80: 42097f5f425Stamizhr@codeaurora.org return NL80211_CHAN_WIDTH_80; 42197f5f425Stamizhr@codeaurora.org case IEEE80211_STA_RX_BW_160: 42297f5f425Stamizhr@codeaurora.org cap_width = 42397f5f425Stamizhr@codeaurora.org vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 42497f5f425Stamizhr@codeaurora.org 42597f5f425Stamizhr@codeaurora.org if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) 42697f5f425Stamizhr@codeaurora.org return NL80211_CHAN_WIDTH_160; 42797f5f425Stamizhr@codeaurora.org 42897f5f425Stamizhr@codeaurora.org return NL80211_CHAN_WIDTH_80P80; 42997f5f425Stamizhr@codeaurora.org default: 43097f5f425Stamizhr@codeaurora.org return NL80211_CHAN_WIDTH_20; 43197f5f425Stamizhr@codeaurora.org } 43297f5f425Stamizhr@codeaurora.org } 43397f5f425Stamizhr@codeaurora.org 43459021c67SArik Nemtsov enum ieee80211_sta_rx_bandwidth 4351c45c5ceSEliad Peller ieee80211_chan_width_to_rx_bw(enum nl80211_chan_width width) 4361c45c5ceSEliad Peller { 4371c45c5ceSEliad Peller switch (width) { 4381c45c5ceSEliad Peller case NL80211_CHAN_WIDTH_20_NOHT: 4391c45c5ceSEliad Peller case NL80211_CHAN_WIDTH_20: 4401c45c5ceSEliad Peller return IEEE80211_STA_RX_BW_20; 4411c45c5ceSEliad Peller case NL80211_CHAN_WIDTH_40: 4421c45c5ceSEliad Peller return IEEE80211_STA_RX_BW_40; 4431c45c5ceSEliad Peller case NL80211_CHAN_WIDTH_80: 4441c45c5ceSEliad Peller return IEEE80211_STA_RX_BW_80; 4451c45c5ceSEliad Peller case NL80211_CHAN_WIDTH_160: 4461c45c5ceSEliad Peller case NL80211_CHAN_WIDTH_80P80: 4471c45c5ceSEliad Peller return IEEE80211_STA_RX_BW_160; 4481c45c5ceSEliad Peller default: 4491c45c5ceSEliad Peller WARN_ON_ONCE(1); 4501c45c5ceSEliad Peller return IEEE80211_STA_RX_BW_20; 4511c45c5ceSEliad Peller } 4521c45c5ceSEliad Peller } 4531c45c5ceSEliad Peller 454cf2c9cc3SJohannes Berg /* FIXME: rename/move - this deals with everything not just VHT */ 455e1a0c6b3SJohannes Berg enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) 456e1a0c6b3SJohannes Berg { 457e1a0c6b3SJohannes Berg struct ieee80211_sub_if_data *sdata = sta->sdata; 4580af83d3dSJohannes Berg enum ieee80211_sta_rx_bandwidth bw; 459b98fb44fSArik Nemtsov enum nl80211_chan_width bss_width = sdata->vif.bss_conf.chandef.width; 460e1a0c6b3SJohannes Berg 461b98fb44fSArik Nemtsov bw = ieee80211_sta_cap_rx_bw(sta); 4621c45c5ceSEliad Peller bw = min(bw, sta->cur_max_bandwidth); 463504871e6SManikanta Pubbisetty 464504871e6SManikanta Pubbisetty /* Don't consider AP's bandwidth for TDLS peers, section 11.23.1 of 465504871e6SManikanta Pubbisetty * IEEE80211-2016 specification makes higher bandwidth operation 466504871e6SManikanta Pubbisetty * possible on the TDLS link if the peers have wider bandwidth 467504871e6SManikanta Pubbisetty * capability. 468*f65607cdSJohannes Berg * 469*f65607cdSJohannes Berg * However, in this case, and only if the TDLS peer is authorized, 470*f65607cdSJohannes Berg * limit to the tdls_chandef so that the configuration here isn't 471*f65607cdSJohannes Berg * wider than what's actually requested on the channel context. 472504871e6SManikanta Pubbisetty */ 473504871e6SManikanta Pubbisetty if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && 474*f65607cdSJohannes Berg test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) && 475*f65607cdSJohannes Berg test_sta_flag(sta, WLAN_STA_AUTHORIZED) && 476*f65607cdSJohannes Berg sta->tdls_chandef.chan) 477*f65607cdSJohannes Berg bw = min(bw, ieee80211_chan_width_to_rx_bw(sta->tdls_chandef.width)); 478*f65607cdSJohannes Berg else 479b98fb44fSArik Nemtsov bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width)); 480b98fb44fSArik Nemtsov 4810af83d3dSJohannes Berg return bw; 482818255eaSMahesh Palivela } 4838921d04eSJohannes Berg 4848921d04eSJohannes Berg void ieee80211_sta_set_rx_nss(struct sta_info *sta) 4858921d04eSJohannes Berg { 486f46209b9STova Mussai u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, rx_nss; 4878921d04eSJohannes Berg 4888921d04eSJohannes Berg /* if we received a notification already don't overwrite it */ 4898921d04eSJohannes Berg if (sta->sta.rx_nss) 4908921d04eSJohannes Berg return; 4918921d04eSJohannes Berg 492f46209b9STova Mussai if (sta->sta.he_cap.has_he) { 493f46209b9STova Mussai int i; 494f46209b9STova Mussai u8 rx_mcs_80 = 0, rx_mcs_160 = 0; 495f46209b9STova Mussai const struct ieee80211_sta_he_cap *he_cap = &sta->sta.he_cap; 496f46209b9STova Mussai u16 mcs_160_map = 497f46209b9STova Mussai le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); 498f46209b9STova Mussai u16 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); 499f46209b9STova Mussai 500f46209b9STova Mussai for (i = 7; i >= 0; i--) { 501f46209b9STova Mussai u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3; 502f46209b9STova Mussai 503f46209b9STova Mussai if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 504f46209b9STova Mussai rx_mcs_160 = i + 1; 505f46209b9STova Mussai break; 506f46209b9STova Mussai } 507f46209b9STova Mussai } 508f46209b9STova Mussai for (i = 7; i >= 0; i--) { 509f46209b9STova Mussai u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3; 510f46209b9STova Mussai 511f46209b9STova Mussai if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 512f46209b9STova Mussai rx_mcs_80 = i + 1; 513f46209b9STova Mussai break; 514f46209b9STova Mussai } 515f46209b9STova Mussai } 516f46209b9STova Mussai 517f46209b9STova Mussai he_rx_nss = min(rx_mcs_80, rx_mcs_160); 518f46209b9STova Mussai } 519f46209b9STova Mussai 5208921d04eSJohannes Berg if (sta->sta.ht_cap.ht_supported) { 5218921d04eSJohannes Berg if (sta->sta.ht_cap.mcs.rx_mask[0]) 5228921d04eSJohannes Berg ht_rx_nss++; 5238921d04eSJohannes Berg if (sta->sta.ht_cap.mcs.rx_mask[1]) 5248921d04eSJohannes Berg ht_rx_nss++; 5258921d04eSJohannes Berg if (sta->sta.ht_cap.mcs.rx_mask[2]) 5268921d04eSJohannes Berg ht_rx_nss++; 5278921d04eSJohannes Berg if (sta->sta.ht_cap.mcs.rx_mask[3]) 5288921d04eSJohannes Berg ht_rx_nss++; 5298921d04eSJohannes Berg /* FIXME: consider rx_highest? */ 5308921d04eSJohannes Berg } 5318921d04eSJohannes Berg 5328921d04eSJohannes Berg if (sta->sta.vht_cap.vht_supported) { 5338921d04eSJohannes Berg int i; 5348921d04eSJohannes Berg u16 rx_mcs_map; 5358921d04eSJohannes Berg 5368921d04eSJohannes Berg rx_mcs_map = le16_to_cpu(sta->sta.vht_cap.vht_mcs.rx_mcs_map); 5378921d04eSJohannes Berg 5388921d04eSJohannes Berg for (i = 7; i >= 0; i--) { 5398921d04eSJohannes Berg u8 mcs = (rx_mcs_map >> (2 * i)) & 3; 5408921d04eSJohannes Berg 5418921d04eSJohannes Berg if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 5428921d04eSJohannes Berg vht_rx_nss = i + 1; 5438921d04eSJohannes Berg break; 5448921d04eSJohannes Berg } 5458921d04eSJohannes Berg } 5468921d04eSJohannes Berg /* FIXME: consider rx_highest? */ 5478921d04eSJohannes Berg } 5488921d04eSJohannes Berg 549f46209b9STova Mussai rx_nss = max(vht_rx_nss, ht_rx_nss); 550f46209b9STova Mussai rx_nss = max(he_rx_nss, rx_nss); 551f46209b9STova Mussai sta->sta.rx_nss = max_t(u8, 1, rx_nss); 5528921d04eSJohannes Berg } 5530af83d3dSJohannes Berg 554b1bce14aSMarek Kwaczynski u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, 5550af83d3dSJohannes Berg struct sta_info *sta, u8 opmode, 55657fbcce3SJohannes Berg enum nl80211_band band) 5570af83d3dSJohannes Berg { 5580af83d3dSJohannes Berg enum ieee80211_sta_rx_bandwidth new_bw; 559ff84e7bfStamizhr@codeaurora.org struct sta_opmode_info sta_opmode = {}; 5600af83d3dSJohannes Berg u32 changed = 0; 5610af83d3dSJohannes Berg u8 nss; 5620af83d3dSJohannes Berg 5630af83d3dSJohannes Berg /* ignore - no support for BF yet */ 5640af83d3dSJohannes Berg if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF) 565b1bce14aSMarek Kwaczynski return 0; 5660af83d3dSJohannes Berg 5670af83d3dSJohannes Berg nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK; 5680af83d3dSJohannes Berg nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; 5690af83d3dSJohannes Berg nss += 1; 5700af83d3dSJohannes Berg 5710af83d3dSJohannes Berg if (sta->sta.rx_nss != nss) { 5720af83d3dSJohannes Berg sta->sta.rx_nss = nss; 573ff84e7bfStamizhr@codeaurora.org sta_opmode.rx_nss = nss; 5740af83d3dSJohannes Berg changed |= IEEE80211_RC_NSS_CHANGED; 575ff84e7bfStamizhr@codeaurora.org sta_opmode.changed |= STA_OPMODE_N_SS_CHANGED; 5760af83d3dSJohannes Berg } 5770af83d3dSJohannes Berg 5780af83d3dSJohannes Berg switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) { 5790af83d3dSJohannes Berg case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ: 5809166cc49SJohannes Berg /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */ 5810af83d3dSJohannes Berg sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20; 5820af83d3dSJohannes Berg break; 5830af83d3dSJohannes Berg case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ: 5849166cc49SJohannes Berg /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */ 5850af83d3dSJohannes Berg sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40; 5860af83d3dSJohannes Berg break; 5870af83d3dSJohannes Berg case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ: 5889166cc49SJohannes Berg if (opmode & IEEE80211_OPMODE_NOTIF_BW_160_80P80) 5899166cc49SJohannes Berg sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; 5909166cc49SJohannes Berg else 5910af83d3dSJohannes Berg sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; 5920af83d3dSJohannes Berg break; 5930af83d3dSJohannes Berg case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ: 5949166cc49SJohannes Berg /* legacy only, no longer used by newer spec */ 5950af83d3dSJohannes Berg sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; 5960af83d3dSJohannes Berg break; 5970af83d3dSJohannes Berg } 5980af83d3dSJohannes Berg 5990af83d3dSJohannes Berg new_bw = ieee80211_sta_cur_vht_bw(sta); 6000af83d3dSJohannes Berg if (new_bw != sta->sta.bandwidth) { 6010af83d3dSJohannes Berg sta->sta.bandwidth = new_bw; 60297f5f425Stamizhr@codeaurora.org sta_opmode.bw = ieee80211_sta_rx_bw_to_chan_width(sta); 60338745c74SIlan Peer changed |= IEEE80211_RC_BW_CHANGED; 604ff84e7bfStamizhr@codeaurora.org sta_opmode.changed |= STA_OPMODE_MAX_BW_CHANGED; 6050af83d3dSJohannes Berg } 6060af83d3dSJohannes Berg 607ff84e7bfStamizhr@codeaurora.org if (sta_opmode.changed) 608ff84e7bfStamizhr@codeaurora.org cfg80211_sta_opmode_change_notify(sdata->dev, sta->addr, 609ff84e7bfStamizhr@codeaurora.org &sta_opmode, GFP_KERNEL); 610ff84e7bfStamizhr@codeaurora.org 611b1bce14aSMarek Kwaczynski return changed; 612b1bce14aSMarek Kwaczynski } 613b1bce14aSMarek Kwaczynski 61423a1f8d4SSara Sharon void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata, 61523a1f8d4SSara Sharon struct ieee80211_mgmt *mgmt) 61623a1f8d4SSara Sharon { 61723a1f8d4SSara Sharon struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; 61823a1f8d4SSara Sharon 619b5a33d52SSara Sharon if (!sdata->vif.mu_mimo_owner) 62023a1f8d4SSara Sharon return; 62123a1f8d4SSara Sharon 62223a1f8d4SSara Sharon if (!memcmp(mgmt->u.action.u.vht_group_notif.position, 62323a1f8d4SSara Sharon bss_conf->mu_group.position, WLAN_USER_POSITION_LEN) && 62423a1f8d4SSara Sharon !memcmp(mgmt->u.action.u.vht_group_notif.membership, 62523a1f8d4SSara Sharon bss_conf->mu_group.membership, WLAN_MEMBERSHIP_LEN)) 62623a1f8d4SSara Sharon return; 62723a1f8d4SSara Sharon 6280241fa19SSara Sharon memcpy(bss_conf->mu_group.membership, 6290241fa19SSara Sharon mgmt->u.action.u.vht_group_notif.membership, 6300241fa19SSara Sharon WLAN_MEMBERSHIP_LEN); 6310241fa19SSara Sharon memcpy(bss_conf->mu_group.position, 6320241fa19SSara Sharon mgmt->u.action.u.vht_group_notif.position, 6330241fa19SSara Sharon WLAN_USER_POSITION_LEN); 63423a1f8d4SSara Sharon 63523a1f8d4SSara Sharon ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_MU_GROUPS); 63623a1f8d4SSara Sharon } 63723a1f8d4SSara Sharon 63865554d07SSara Sharon void ieee80211_update_mu_groups(struct ieee80211_vif *vif, 63965554d07SSara Sharon const u8 *membership, const u8 *position) 64065554d07SSara Sharon { 641b5a33d52SSara Sharon struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; 64265554d07SSara Sharon 643b5a33d52SSara Sharon if (WARN_ON_ONCE(!vif->mu_mimo_owner)) 64465554d07SSara Sharon return; 64565554d07SSara Sharon 64665554d07SSara Sharon memcpy(bss_conf->mu_group.membership, membership, WLAN_MEMBERSHIP_LEN); 64765554d07SSara Sharon memcpy(bss_conf->mu_group.position, position, WLAN_USER_POSITION_LEN); 64865554d07SSara Sharon } 64965554d07SSara Sharon EXPORT_SYMBOL_GPL(ieee80211_update_mu_groups); 65065554d07SSara Sharon 651b1bce14aSMarek Kwaczynski void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, 652b1bce14aSMarek Kwaczynski struct sta_info *sta, u8 opmode, 65357fbcce3SJohannes Berg enum nl80211_band band) 654b1bce14aSMarek Kwaczynski { 655b1bce14aSMarek Kwaczynski struct ieee80211_local *local = sdata->local; 656b1bce14aSMarek Kwaczynski struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; 657b1bce14aSMarek Kwaczynski 658cf1e05c6SEyal Shapira u32 changed = __ieee80211_vht_handle_opmode(sdata, sta, opmode, band); 659b1bce14aSMarek Kwaczynski 660d2941df8SJohannes Berg if (changed > 0) { 661d2941df8SJohannes Berg ieee80211_recalc_min_chandef(sdata); 6620af83d3dSJohannes Berg rate_control_rate_update(local, sband, sta, changed); 6630af83d3dSJohannes Berg } 664d2941df8SJohannes Berg } 665b119ad6eSLorenzo Bianconi 666b119ad6eSLorenzo Bianconi void ieee80211_get_vht_mask_from_cap(__le16 vht_cap, 667b119ad6eSLorenzo Bianconi u16 vht_mask[NL80211_VHT_NSS_MAX]) 668b119ad6eSLorenzo Bianconi { 669b119ad6eSLorenzo Bianconi int i; 670b119ad6eSLorenzo Bianconi u16 mask, cap = le16_to_cpu(vht_cap); 671b119ad6eSLorenzo Bianconi 672b119ad6eSLorenzo Bianconi for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { 673b119ad6eSLorenzo Bianconi mask = (cap >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 674b119ad6eSLorenzo Bianconi switch (mask) { 675b119ad6eSLorenzo Bianconi case IEEE80211_VHT_MCS_SUPPORT_0_7: 676b119ad6eSLorenzo Bianconi vht_mask[i] = 0x00FF; 677b119ad6eSLorenzo Bianconi break; 678b119ad6eSLorenzo Bianconi case IEEE80211_VHT_MCS_SUPPORT_0_8: 679b119ad6eSLorenzo Bianconi vht_mask[i] = 0x01FF; 680b119ad6eSLorenzo Bianconi break; 681b119ad6eSLorenzo Bianconi case IEEE80211_VHT_MCS_SUPPORT_0_9: 682b119ad6eSLorenzo Bianconi vht_mask[i] = 0x03FF; 683b119ad6eSLorenzo Bianconi break; 684b119ad6eSLorenzo Bianconi case IEEE80211_VHT_MCS_NOT_SUPPORTED: 685b119ad6eSLorenzo Bianconi default: 686b119ad6eSLorenzo Bianconi vht_mask[i] = 0; 687b119ad6eSLorenzo Bianconi break; 688b119ad6eSLorenzo Bianconi } 689b119ad6eSLorenzo Bianconi } 690b119ad6eSLorenzo Bianconi } 691