162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VHT handling 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Portions of this file 662306a36Sopenharmony_ci * Copyright(c) 2015 - 2016 Intel Deutschland GmbH 762306a36Sopenharmony_ci * Copyright (C) 2018 - 2023 Intel Corporation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/ieee80211.h> 1162306a36Sopenharmony_ci#include <linux/export.h> 1262306a36Sopenharmony_ci#include <net/mac80211.h> 1362306a36Sopenharmony_ci#include "ieee80211_i.h" 1462306a36Sopenharmony_ci#include "rate.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic void __check_vhtcap_disable(struct ieee80211_sub_if_data *sdata, 1862306a36Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap, 1962306a36Sopenharmony_ci u32 flag) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci __le32 le_flag = cpu_to_le32(flag); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci if (sdata->u.mgd.vht_capa_mask.vht_cap_info & le_flag && 2462306a36Sopenharmony_ci !(sdata->u.mgd.vht_capa.vht_cap_info & le_flag)) 2562306a36Sopenharmony_ci vht_cap->cap &= ~flag; 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_civoid ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata, 2962306a36Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci int i; 3262306a36Sopenharmony_ci u16 rxmcs_mask, rxmcs_cap, rxmcs_n, txmcs_mask, txmcs_cap, txmcs_n; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (!vht_cap->vht_supported) 3562306a36Sopenharmony_ci return; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_STATION) 3862306a36Sopenharmony_ci return; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 4162306a36Sopenharmony_ci IEEE80211_VHT_CAP_RXLDPC); 4262306a36Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 4362306a36Sopenharmony_ci IEEE80211_VHT_CAP_SHORT_GI_80); 4462306a36Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 4562306a36Sopenharmony_ci IEEE80211_VHT_CAP_SHORT_GI_160); 4662306a36Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 4762306a36Sopenharmony_ci IEEE80211_VHT_CAP_TXSTBC); 4862306a36Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 4962306a36Sopenharmony_ci IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); 5062306a36Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 5162306a36Sopenharmony_ci IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); 5262306a36Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 5362306a36Sopenharmony_ci IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN); 5462306a36Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 5562306a36Sopenharmony_ci IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* Allow user to decrease AMPDU length exponent */ 5862306a36Sopenharmony_ci if (sdata->u.mgd.vht_capa_mask.vht_cap_info & 5962306a36Sopenharmony_ci cpu_to_le32(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK)) { 6062306a36Sopenharmony_ci u32 cap, n; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci n = le32_to_cpu(sdata->u.mgd.vht_capa.vht_cap_info) & 6362306a36Sopenharmony_ci IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; 6462306a36Sopenharmony_ci n >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; 6562306a36Sopenharmony_ci cap = vht_cap->cap & IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; 6662306a36Sopenharmony_ci cap >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (n < cap) { 6962306a36Sopenharmony_ci vht_cap->cap &= 7062306a36Sopenharmony_ci ~IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; 7162306a36Sopenharmony_ci vht_cap->cap |= 7262306a36Sopenharmony_ci n << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Allow the user to decrease MCSes */ 7762306a36Sopenharmony_ci rxmcs_mask = 7862306a36Sopenharmony_ci le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.rx_mcs_map); 7962306a36Sopenharmony_ci rxmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.rx_mcs_map); 8062306a36Sopenharmony_ci rxmcs_n &= rxmcs_mask; 8162306a36Sopenharmony_ci rxmcs_cap = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci txmcs_mask = 8462306a36Sopenharmony_ci le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.tx_mcs_map); 8562306a36Sopenharmony_ci txmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.tx_mcs_map); 8662306a36Sopenharmony_ci txmcs_n &= txmcs_mask; 8762306a36Sopenharmony_ci txmcs_cap = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); 8862306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 8962306a36Sopenharmony_ci u8 m, n, c; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci m = (rxmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 9262306a36Sopenharmony_ci n = (rxmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 9362306a36Sopenharmony_ci c = (rxmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || 9662306a36Sopenharmony_ci n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { 9762306a36Sopenharmony_ci rxmcs_cap &= ~(3 << 2*i); 9862306a36Sopenharmony_ci rxmcs_cap |= (rxmcs_n & (3 << 2*i)); 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci m = (txmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 10262306a36Sopenharmony_ci n = (txmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 10362306a36Sopenharmony_ci c = (txmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || 10662306a36Sopenharmony_ci n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { 10762306a36Sopenharmony_ci txmcs_cap &= ~(3 << 2*i); 10862306a36Sopenharmony_ci txmcs_cap |= (txmcs_n & (3 << 2*i)); 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(rxmcs_cap); 11262306a36Sopenharmony_ci vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(txmcs_cap); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_civoid 11662306a36Sopenharmony_ciieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, 11762306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 11862306a36Sopenharmony_ci const struct ieee80211_vht_cap *vht_cap_ie, 11962306a36Sopenharmony_ci const struct ieee80211_vht_cap *vht_cap_ie2, 12062306a36Sopenharmony_ci struct link_sta_info *link_sta) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap; 12362306a36Sopenharmony_ci struct ieee80211_sta_vht_cap own_cap; 12462306a36Sopenharmony_ci u32 cap_info, i; 12562306a36Sopenharmony_ci bool have_80mhz; 12662306a36Sopenharmony_ci u32 mpdu_len; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci memset(vht_cap, 0, sizeof(*vht_cap)); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (!link_sta->pub->ht_cap.ht_supported) 13162306a36Sopenharmony_ci return; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!vht_cap_ie || !sband->vht_cap.vht_supported) 13462306a36Sopenharmony_ci return; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Allow VHT if at least one channel on the sband supports 80 MHz */ 13762306a36Sopenharmony_ci have_80mhz = false; 13862306a36Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 13962306a36Sopenharmony_ci if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED | 14062306a36Sopenharmony_ci IEEE80211_CHAN_NO_80MHZ)) 14162306a36Sopenharmony_ci continue; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci have_80mhz = true; 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (!have_80mhz) 14862306a36Sopenharmony_ci return; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * A VHT STA must support 40 MHz, but if we verify that here 15262306a36Sopenharmony_ci * then we break a few things - some APs (e.g. Netgear R6300v2 15362306a36Sopenharmony_ci * and others based on the BCM4360 chipset) will unset this 15462306a36Sopenharmony_ci * capability bit when operating in 20 MHz. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci vht_cap->vht_supported = true; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci own_cap = sband->vht_cap; 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * If user has specified capability overrides, take care 16262306a36Sopenharmony_ci * of that if the station we're setting up is the AP that 16362306a36Sopenharmony_ci * we advertised a restricted capability set to. Override 16462306a36Sopenharmony_ci * our own capabilities and then use those below. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_STATION && 16762306a36Sopenharmony_ci !test_sta_flag(link_sta->sta, WLAN_STA_TDLS_PEER)) 16862306a36Sopenharmony_ci ieee80211_apply_vhtcap_overrides(sdata, &own_cap); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* take some capabilities as-is */ 17162306a36Sopenharmony_ci cap_info = le32_to_cpu(vht_cap_ie->vht_cap_info); 17262306a36Sopenharmony_ci vht_cap->cap = cap_info; 17362306a36Sopenharmony_ci vht_cap->cap &= IEEE80211_VHT_CAP_RXLDPC | 17462306a36Sopenharmony_ci IEEE80211_VHT_CAP_VHT_TXOP_PS | 17562306a36Sopenharmony_ci IEEE80211_VHT_CAP_HTC_VHT | 17662306a36Sopenharmony_ci IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | 17762306a36Sopenharmony_ci IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB | 17862306a36Sopenharmony_ci IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB | 17962306a36Sopenharmony_ci IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | 18062306a36Sopenharmony_ci IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci vht_cap->cap |= min_t(u32, cap_info & IEEE80211_VHT_CAP_MAX_MPDU_MASK, 18362306a36Sopenharmony_ci own_cap.cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* and some based on our own capabilities */ 18662306a36Sopenharmony_ci switch (own_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { 18762306a36Sopenharmony_ci case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: 18862306a36Sopenharmony_ci vht_cap->cap |= cap_info & 18962306a36Sopenharmony_ci IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: 19262306a36Sopenharmony_ci vht_cap->cap |= cap_info & 19362306a36Sopenharmony_ci IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci default: 19662306a36Sopenharmony_ci /* nothing */ 19762306a36Sopenharmony_ci break; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* symmetric capabilities */ 20162306a36Sopenharmony_ci vht_cap->cap |= cap_info & own_cap.cap & 20262306a36Sopenharmony_ci (IEEE80211_VHT_CAP_SHORT_GI_80 | 20362306a36Sopenharmony_ci IEEE80211_VHT_CAP_SHORT_GI_160); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* remaining ones */ 20662306a36Sopenharmony_ci if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) 20762306a36Sopenharmony_ci vht_cap->cap |= cap_info & 20862306a36Sopenharmony_ci (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | 20962306a36Sopenharmony_ci IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) 21262306a36Sopenharmony_ci vht_cap->cap |= cap_info & 21362306a36Sopenharmony_ci (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | 21462306a36Sopenharmony_ci IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) 21762306a36Sopenharmony_ci vht_cap->cap |= cap_info & 21862306a36Sopenharmony_ci IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) 22162306a36Sopenharmony_ci vht_cap->cap |= cap_info & 22262306a36Sopenharmony_ci IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (own_cap.cap & IEEE80211_VHT_CAP_TXSTBC) 22562306a36Sopenharmony_ci vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_RXSTBC_MASK; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (own_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK) 22862306a36Sopenharmony_ci vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_TXSTBC; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Copy peer MCS info, the driver might need them. */ 23162306a36Sopenharmony_ci memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, 23262306a36Sopenharmony_ci sizeof(struct ieee80211_vht_mcs_info)); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* copy EXT_NSS_BW Support value or remove the capability */ 23562306a36Sopenharmony_ci if (ieee80211_hw_check(&sdata->local->hw, SUPPORTS_VHT_EXT_NSS_BW)) 23662306a36Sopenharmony_ci vht_cap->cap |= (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK); 23762306a36Sopenharmony_ci else 23862306a36Sopenharmony_ci vht_cap->vht_mcs.tx_highest &= 23962306a36Sopenharmony_ci ~cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* but also restrict MCSes */ 24262306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 24362306a36Sopenharmony_ci u16 own_rx, own_tx, peer_rx, peer_tx; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci own_rx = le16_to_cpu(own_cap.vht_mcs.rx_mcs_map); 24662306a36Sopenharmony_ci own_rx = (own_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci own_tx = le16_to_cpu(own_cap.vht_mcs.tx_mcs_map); 24962306a36Sopenharmony_ci own_tx = (own_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci peer_rx = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); 25262306a36Sopenharmony_ci peer_rx = (peer_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci peer_tx = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); 25562306a36Sopenharmony_ci peer_tx = (peer_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (peer_tx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 25862306a36Sopenharmony_ci if (own_rx == IEEE80211_VHT_MCS_NOT_SUPPORTED) 25962306a36Sopenharmony_ci peer_tx = IEEE80211_VHT_MCS_NOT_SUPPORTED; 26062306a36Sopenharmony_ci else if (own_rx < peer_tx) 26162306a36Sopenharmony_ci peer_tx = own_rx; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (peer_rx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 26562306a36Sopenharmony_ci if (own_tx == IEEE80211_VHT_MCS_NOT_SUPPORTED) 26662306a36Sopenharmony_ci peer_rx = IEEE80211_VHT_MCS_NOT_SUPPORTED; 26762306a36Sopenharmony_ci else if (own_tx < peer_rx) 26862306a36Sopenharmony_ci peer_rx = own_tx; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci vht_cap->vht_mcs.rx_mcs_map &= 27262306a36Sopenharmony_ci ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); 27362306a36Sopenharmony_ci vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(peer_rx << i * 2); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci vht_cap->vht_mcs.tx_mcs_map &= 27662306a36Sopenharmony_ci ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); 27762306a36Sopenharmony_ci vht_cap->vht_mcs.tx_mcs_map |= cpu_to_le16(peer_tx << i * 2); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* 28162306a36Sopenharmony_ci * This is a workaround for VHT-enabled STAs which break the spec 28262306a36Sopenharmony_ci * and have the VHT-MCS Rx map filled in with value 3 for all eight 28362306a36Sopenharmony_ci * spacial streams, an example is AR9462. 28462306a36Sopenharmony_ci * 28562306a36Sopenharmony_ci * As per spec, in section 22.1.1 Introduction to the VHT PHY 28662306a36Sopenharmony_ci * A VHT STA shall support at least single spactial stream VHT-MCSs 28762306a36Sopenharmony_ci * 0 to 7 (transmit and receive) in all supported channel widths. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci if (vht_cap->vht_mcs.rx_mcs_map == cpu_to_le16(0xFFFF)) { 29062306a36Sopenharmony_ci vht_cap->vht_supported = false; 29162306a36Sopenharmony_ci sdata_info(sdata, 29262306a36Sopenharmony_ci "Ignoring VHT IE from %pM (link:%pM) due to invalid rx_mcs_map\n", 29362306a36Sopenharmony_ci link_sta->sta->addr, link_sta->addr); 29462306a36Sopenharmony_ci return; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* finally set up the bandwidth */ 29862306a36Sopenharmony_ci switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { 29962306a36Sopenharmony_ci case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: 30062306a36Sopenharmony_ci case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: 30162306a36Sopenharmony_ci link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci default: 30462306a36Sopenharmony_ci link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (!(vht_cap->vht_mcs.tx_highest & 30762306a36Sopenharmony_ci cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE))) 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* 31162306a36Sopenharmony_ci * If this is non-zero, then it does support 160 MHz after all, 31262306a36Sopenharmony_ci * in one form or the other. We don't distinguish here (or even 31362306a36Sopenharmony_ci * above) between 160 and 80+80 yet. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci if (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) 31662306a36Sopenharmony_ci link_sta->cur_max_bandwidth = 31762306a36Sopenharmony_ci IEEE80211_STA_RX_BW_160; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* 32362306a36Sopenharmony_ci * Work around the Cisco 9115 FW 17.3 bug by taking the min of 32462306a36Sopenharmony_ci * both reported MPDU lengths. 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_ci mpdu_len = vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK; 32762306a36Sopenharmony_ci if (vht_cap_ie2) 32862306a36Sopenharmony_ci mpdu_len = min_t(u32, mpdu_len, 32962306a36Sopenharmony_ci le32_get_bits(vht_cap_ie2->vht_cap_info, 33062306a36Sopenharmony_ci IEEE80211_VHT_CAP_MAX_MPDU_MASK)); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* 33362306a36Sopenharmony_ci * FIXME - should the amsdu len be per link? store per link 33462306a36Sopenharmony_ci * and maintain a minimum? 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci switch (mpdu_len) { 33762306a36Sopenharmony_ci case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: 33862306a36Sopenharmony_ci link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454; 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991: 34162306a36Sopenharmony_ci link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991; 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895: 34462306a36Sopenharmony_ci default: 34562306a36Sopenharmony_ci link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895; 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ieee80211_sta_recalc_aggregates(&link_sta->sta->sta); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/* FIXME: move this to some better location - parses HE/EHT now */ 35362306a36Sopenharmony_cienum ieee80211_sta_rx_bandwidth 35462306a36Sopenharmony_ciieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci unsigned int link_id = link_sta->link_id; 35762306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link_sta->sta->sdata; 35862306a36Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap; 35962306a36Sopenharmony_ci struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap; 36062306a36Sopenharmony_ci struct ieee80211_sta_eht_cap *eht_cap = &link_sta->pub->eht_cap; 36162306a36Sopenharmony_ci u32 cap_width; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (he_cap->has_he) { 36462306a36Sopenharmony_ci struct ieee80211_bss_conf *link_conf; 36562306a36Sopenharmony_ci enum ieee80211_sta_rx_bandwidth ret; 36662306a36Sopenharmony_ci u8 info; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci rcu_read_lock(); 36962306a36Sopenharmony_ci link_conf = rcu_dereference(sdata->vif.link_conf[link_id]); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (eht_cap->has_eht && 37262306a36Sopenharmony_ci link_conf->chandef.chan->band == NL80211_BAND_6GHZ) { 37362306a36Sopenharmony_ci info = eht_cap->eht_cap_elem.phy_cap_info[0]; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (info & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) { 37662306a36Sopenharmony_ci ret = IEEE80211_STA_RX_BW_320; 37762306a36Sopenharmony_ci goto out; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci info = he_cap->he_cap_elem.phy_cap_info[0]; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (link_conf->chandef.chan->band == NL80211_BAND_2GHZ) { 38462306a36Sopenharmony_ci if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) 38562306a36Sopenharmony_ci ret = IEEE80211_STA_RX_BW_40; 38662306a36Sopenharmony_ci else 38762306a36Sopenharmony_ci ret = IEEE80211_STA_RX_BW_20; 38862306a36Sopenharmony_ci goto out; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G || 39262306a36Sopenharmony_ci info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) 39362306a36Sopenharmony_ci ret = IEEE80211_STA_RX_BW_160; 39462306a36Sopenharmony_ci else if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) 39562306a36Sopenharmony_ci ret = IEEE80211_STA_RX_BW_80; 39662306a36Sopenharmony_ci else 39762306a36Sopenharmony_ci ret = IEEE80211_STA_RX_BW_20; 39862306a36Sopenharmony_ciout: 39962306a36Sopenharmony_ci rcu_read_unlock(); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci return ret; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (!vht_cap->vht_supported) 40562306a36Sopenharmony_ci return link_sta->pub->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 40662306a36Sopenharmony_ci IEEE80211_STA_RX_BW_40 : 40762306a36Sopenharmony_ci IEEE80211_STA_RX_BW_20; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ || 41262306a36Sopenharmony_ci cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) 41362306a36Sopenharmony_ci return IEEE80211_STA_RX_BW_160; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* 41662306a36Sopenharmony_ci * If this is non-zero, then it does support 160 MHz after all, 41762306a36Sopenharmony_ci * in one form or the other. We don't distinguish here (or even 41862306a36Sopenharmony_ci * above) between 160 and 80+80 yet. 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_ci if (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) 42162306a36Sopenharmony_ci return IEEE80211_STA_RX_BW_160; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return IEEE80211_STA_RX_BW_80; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cienum nl80211_chan_width 42762306a36Sopenharmony_ciieee80211_sta_cap_chan_bw(struct link_sta_info *link_sta) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap; 43062306a36Sopenharmony_ci u32 cap_width; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!vht_cap->vht_supported) { 43362306a36Sopenharmony_ci if (!link_sta->pub->ht_cap.ht_supported) 43462306a36Sopenharmony_ci return NL80211_CHAN_WIDTH_20_NOHT; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci return link_sta->pub->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 43762306a36Sopenharmony_ci NL80211_CHAN_WIDTH_40 : NL80211_CHAN_WIDTH_20; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) 44362306a36Sopenharmony_ci return NL80211_CHAN_WIDTH_160; 44462306a36Sopenharmony_ci else if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) 44562306a36Sopenharmony_ci return NL80211_CHAN_WIDTH_80P80; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return NL80211_CHAN_WIDTH_80; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cienum nl80211_chan_width 45162306a36Sopenharmony_ciieee80211_sta_rx_bw_to_chan_width(struct link_sta_info *link_sta) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci enum ieee80211_sta_rx_bandwidth cur_bw = 45462306a36Sopenharmony_ci link_sta->pub->bandwidth; 45562306a36Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap = 45662306a36Sopenharmony_ci &link_sta->pub->vht_cap; 45762306a36Sopenharmony_ci u32 cap_width; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci switch (cur_bw) { 46062306a36Sopenharmony_ci case IEEE80211_STA_RX_BW_20: 46162306a36Sopenharmony_ci if (!link_sta->pub->ht_cap.ht_supported) 46262306a36Sopenharmony_ci return NL80211_CHAN_WIDTH_20_NOHT; 46362306a36Sopenharmony_ci else 46462306a36Sopenharmony_ci return NL80211_CHAN_WIDTH_20; 46562306a36Sopenharmony_ci case IEEE80211_STA_RX_BW_40: 46662306a36Sopenharmony_ci return NL80211_CHAN_WIDTH_40; 46762306a36Sopenharmony_ci case IEEE80211_STA_RX_BW_80: 46862306a36Sopenharmony_ci return NL80211_CHAN_WIDTH_80; 46962306a36Sopenharmony_ci case IEEE80211_STA_RX_BW_160: 47062306a36Sopenharmony_ci cap_width = 47162306a36Sopenharmony_ci vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) 47462306a36Sopenharmony_ci return NL80211_CHAN_WIDTH_160; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return NL80211_CHAN_WIDTH_80P80; 47762306a36Sopenharmony_ci default: 47862306a36Sopenharmony_ci return NL80211_CHAN_WIDTH_20; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cienum ieee80211_sta_rx_bandwidth 48362306a36Sopenharmony_ciieee80211_chan_width_to_rx_bw(enum nl80211_chan_width width) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci switch (width) { 48662306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20_NOHT: 48762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20: 48862306a36Sopenharmony_ci return IEEE80211_STA_RX_BW_20; 48962306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 49062306a36Sopenharmony_ci return IEEE80211_STA_RX_BW_40; 49162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 49262306a36Sopenharmony_ci return IEEE80211_STA_RX_BW_80; 49362306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 49462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 49562306a36Sopenharmony_ci return IEEE80211_STA_RX_BW_160; 49662306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_320: 49762306a36Sopenharmony_ci return IEEE80211_STA_RX_BW_320; 49862306a36Sopenharmony_ci default: 49962306a36Sopenharmony_ci WARN_ON_ONCE(1); 50062306a36Sopenharmony_ci return IEEE80211_STA_RX_BW_20; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/* FIXME: rename/move - this deals with everything not just VHT */ 50562306a36Sopenharmony_cienum ieee80211_sta_rx_bandwidth 50662306a36Sopenharmony_ciieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct sta_info *sta = link_sta->sta; 50962306a36Sopenharmony_ci struct ieee80211_bss_conf *link_conf; 51062306a36Sopenharmony_ci enum nl80211_chan_width bss_width; 51162306a36Sopenharmony_ci enum ieee80211_sta_rx_bandwidth bw; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci rcu_read_lock(); 51462306a36Sopenharmony_ci link_conf = rcu_dereference(sta->sdata->vif.link_conf[link_sta->link_id]); 51562306a36Sopenharmony_ci if (WARN_ON(!link_conf)) 51662306a36Sopenharmony_ci bss_width = NL80211_CHAN_WIDTH_20_NOHT; 51762306a36Sopenharmony_ci else 51862306a36Sopenharmony_ci bss_width = link_conf->chandef.width; 51962306a36Sopenharmony_ci rcu_read_unlock(); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci bw = ieee80211_sta_cap_rx_bw(link_sta); 52262306a36Sopenharmony_ci bw = min(bw, link_sta->cur_max_bandwidth); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* Don't consider AP's bandwidth for TDLS peers, section 11.23.1 of 52562306a36Sopenharmony_ci * IEEE80211-2016 specification makes higher bandwidth operation 52662306a36Sopenharmony_ci * possible on the TDLS link if the peers have wider bandwidth 52762306a36Sopenharmony_ci * capability. 52862306a36Sopenharmony_ci * 52962306a36Sopenharmony_ci * However, in this case, and only if the TDLS peer is authorized, 53062306a36Sopenharmony_ci * limit to the tdls_chandef so that the configuration here isn't 53162306a36Sopenharmony_ci * wider than what's actually requested on the channel context. 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ci if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && 53462306a36Sopenharmony_ci test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) && 53562306a36Sopenharmony_ci test_sta_flag(sta, WLAN_STA_AUTHORIZED) && 53662306a36Sopenharmony_ci sta->tdls_chandef.chan) 53762306a36Sopenharmony_ci bw = min(bw, ieee80211_chan_width_to_rx_bw(sta->tdls_chandef.width)); 53862306a36Sopenharmony_ci else 53962306a36Sopenharmony_ci bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width)); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci return bw; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_civoid ieee80211_sta_set_rx_nss(struct link_sta_info *link_sta) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, eht_rx_nss = 0, rx_nss; 54762306a36Sopenharmony_ci bool support_160; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* if we received a notification already don't overwrite it */ 55062306a36Sopenharmony_ci if (link_sta->pub->rx_nss) 55162306a36Sopenharmony_ci return; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (link_sta->pub->eht_cap.has_eht) { 55462306a36Sopenharmony_ci int i; 55562306a36Sopenharmony_ci const u8 *rx_nss_mcs = (void *)&link_sta->pub->eht_cap.eht_mcs_nss_supp; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* get the max nss for EHT over all possible bandwidths and mcs */ 55862306a36Sopenharmony_ci for (i = 0; i < sizeof(struct ieee80211_eht_mcs_nss_supp); i++) 55962306a36Sopenharmony_ci eht_rx_nss = max_t(u8, eht_rx_nss, 56062306a36Sopenharmony_ci u8_get_bits(rx_nss_mcs[i], 56162306a36Sopenharmony_ci IEEE80211_EHT_MCS_NSS_RX)); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (link_sta->pub->he_cap.has_he) { 56562306a36Sopenharmony_ci int i; 56662306a36Sopenharmony_ci u8 rx_mcs_80 = 0, rx_mcs_160 = 0; 56762306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap; 56862306a36Sopenharmony_ci u16 mcs_160_map = 56962306a36Sopenharmony_ci le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); 57062306a36Sopenharmony_ci u16 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci for (i = 7; i >= 0; i--) { 57362306a36Sopenharmony_ci u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (mcs_160 != IEEE80211_HE_MCS_NOT_SUPPORTED) { 57662306a36Sopenharmony_ci rx_mcs_160 = i + 1; 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci for (i = 7; i >= 0; i--) { 58162306a36Sopenharmony_ci u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (mcs_80 != IEEE80211_HE_MCS_NOT_SUPPORTED) { 58462306a36Sopenharmony_ci rx_mcs_80 = i + 1; 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci support_160 = he_cap->he_cap_elem.phy_cap_info[0] & 59062306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (support_160) 59362306a36Sopenharmony_ci he_rx_nss = min(rx_mcs_80, rx_mcs_160); 59462306a36Sopenharmony_ci else 59562306a36Sopenharmony_ci he_rx_nss = rx_mcs_80; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (link_sta->pub->ht_cap.ht_supported) { 59962306a36Sopenharmony_ci if (link_sta->pub->ht_cap.mcs.rx_mask[0]) 60062306a36Sopenharmony_ci ht_rx_nss++; 60162306a36Sopenharmony_ci if (link_sta->pub->ht_cap.mcs.rx_mask[1]) 60262306a36Sopenharmony_ci ht_rx_nss++; 60362306a36Sopenharmony_ci if (link_sta->pub->ht_cap.mcs.rx_mask[2]) 60462306a36Sopenharmony_ci ht_rx_nss++; 60562306a36Sopenharmony_ci if (link_sta->pub->ht_cap.mcs.rx_mask[3]) 60662306a36Sopenharmony_ci ht_rx_nss++; 60762306a36Sopenharmony_ci /* FIXME: consider rx_highest? */ 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (link_sta->pub->vht_cap.vht_supported) { 61162306a36Sopenharmony_ci int i; 61262306a36Sopenharmony_ci u16 rx_mcs_map; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci rx_mcs_map = le16_to_cpu(link_sta->pub->vht_cap.vht_mcs.rx_mcs_map); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci for (i = 7; i >= 0; i--) { 61762306a36Sopenharmony_ci u8 mcs = (rx_mcs_map >> (2 * i)) & 3; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 62062306a36Sopenharmony_ci vht_rx_nss = i + 1; 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci /* FIXME: consider rx_highest? */ 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci rx_nss = max(vht_rx_nss, ht_rx_nss); 62862306a36Sopenharmony_ci rx_nss = max(he_rx_nss, rx_nss); 62962306a36Sopenharmony_ci rx_nss = max(eht_rx_nss, rx_nss); 63062306a36Sopenharmony_ci link_sta->pub->rx_nss = max_t(u8, 1, rx_nss); 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ciu32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, 63462306a36Sopenharmony_ci struct link_sta_info *link_sta, 63562306a36Sopenharmony_ci u8 opmode, enum nl80211_band band) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci enum ieee80211_sta_rx_bandwidth new_bw; 63862306a36Sopenharmony_ci struct sta_opmode_info sta_opmode = {}; 63962306a36Sopenharmony_ci u32 changed = 0; 64062306a36Sopenharmony_ci u8 nss, cur_nss; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* ignore - no support for BF yet */ 64362306a36Sopenharmony_ci if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF) 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK; 64762306a36Sopenharmony_ci nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; 64862306a36Sopenharmony_ci nss += 1; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (link_sta->pub->rx_nss != nss) { 65162306a36Sopenharmony_ci cur_nss = link_sta->pub->rx_nss; 65262306a36Sopenharmony_ci /* Reset rx_nss and call ieee80211_sta_set_rx_nss() which 65362306a36Sopenharmony_ci * will set the same to max nss value calculated based on capability. 65462306a36Sopenharmony_ci */ 65562306a36Sopenharmony_ci link_sta->pub->rx_nss = 0; 65662306a36Sopenharmony_ci ieee80211_sta_set_rx_nss(link_sta); 65762306a36Sopenharmony_ci /* Do not allow an nss change to rx_nss greater than max_nss 65862306a36Sopenharmony_ci * negotiated and capped to APs capability during association. 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_ci if (nss <= link_sta->pub->rx_nss) { 66162306a36Sopenharmony_ci link_sta->pub->rx_nss = nss; 66262306a36Sopenharmony_ci sta_opmode.rx_nss = nss; 66362306a36Sopenharmony_ci changed |= IEEE80211_RC_NSS_CHANGED; 66462306a36Sopenharmony_ci sta_opmode.changed |= STA_OPMODE_N_SS_CHANGED; 66562306a36Sopenharmony_ci } else { 66662306a36Sopenharmony_ci link_sta->pub->rx_nss = cur_nss; 66762306a36Sopenharmony_ci pr_warn_ratelimited("Ignoring NSS change in VHT Operating Mode Notification from %pM with invalid nss %d", 66862306a36Sopenharmony_ci link_sta->pub->addr, nss); 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) { 67362306a36Sopenharmony_ci case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ: 67462306a36Sopenharmony_ci /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */ 67562306a36Sopenharmony_ci link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20; 67662306a36Sopenharmony_ci break; 67762306a36Sopenharmony_ci case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ: 67862306a36Sopenharmony_ci /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */ 67962306a36Sopenharmony_ci link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40; 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ: 68262306a36Sopenharmony_ci if (opmode & IEEE80211_OPMODE_NOTIF_BW_160_80P80) 68362306a36Sopenharmony_ci link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; 68462306a36Sopenharmony_ci else 68562306a36Sopenharmony_ci link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; 68662306a36Sopenharmony_ci break; 68762306a36Sopenharmony_ci case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ: 68862306a36Sopenharmony_ci /* legacy only, no longer used by newer spec */ 68962306a36Sopenharmony_ci link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci new_bw = ieee80211_sta_cur_vht_bw(link_sta); 69462306a36Sopenharmony_ci if (new_bw != link_sta->pub->bandwidth) { 69562306a36Sopenharmony_ci link_sta->pub->bandwidth = new_bw; 69662306a36Sopenharmony_ci sta_opmode.bw = ieee80211_sta_rx_bw_to_chan_width(link_sta); 69762306a36Sopenharmony_ci changed |= IEEE80211_RC_BW_CHANGED; 69862306a36Sopenharmony_ci sta_opmode.changed |= STA_OPMODE_MAX_BW_CHANGED; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (sta_opmode.changed) 70262306a36Sopenharmony_ci cfg80211_sta_opmode_change_notify(sdata->dev, link_sta->addr, 70362306a36Sopenharmony_ci &sta_opmode, GFP_KERNEL); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci return changed; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_civoid ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata, 70962306a36Sopenharmony_ci struct ieee80211_link_data *link, 71062306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct ieee80211_bss_conf *link_conf = link->conf; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (!link_conf->mu_mimo_owner) 71562306a36Sopenharmony_ci return; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (!memcmp(mgmt->u.action.u.vht_group_notif.position, 71862306a36Sopenharmony_ci link_conf->mu_group.position, WLAN_USER_POSITION_LEN) && 71962306a36Sopenharmony_ci !memcmp(mgmt->u.action.u.vht_group_notif.membership, 72062306a36Sopenharmony_ci link_conf->mu_group.membership, WLAN_MEMBERSHIP_LEN)) 72162306a36Sopenharmony_ci return; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci memcpy(link_conf->mu_group.membership, 72462306a36Sopenharmony_ci mgmt->u.action.u.vht_group_notif.membership, 72562306a36Sopenharmony_ci WLAN_MEMBERSHIP_LEN); 72662306a36Sopenharmony_ci memcpy(link_conf->mu_group.position, 72762306a36Sopenharmony_ci mgmt->u.action.u.vht_group_notif.position, 72862306a36Sopenharmony_ci WLAN_USER_POSITION_LEN); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci ieee80211_link_info_change_notify(sdata, link, 73162306a36Sopenharmony_ci BSS_CHANGED_MU_GROUPS); 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_civoid ieee80211_update_mu_groups(struct ieee80211_vif *vif, unsigned int link_id, 73562306a36Sopenharmony_ci const u8 *membership, const u8 *position) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct ieee80211_bss_conf *link_conf; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci rcu_read_lock(); 74062306a36Sopenharmony_ci link_conf = rcu_dereference(vif->link_conf[link_id]); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (!WARN_ON_ONCE(!link_conf || !link_conf->mu_mimo_owner)) { 74362306a36Sopenharmony_ci memcpy(link_conf->mu_group.membership, membership, 74462306a36Sopenharmony_ci WLAN_MEMBERSHIP_LEN); 74562306a36Sopenharmony_ci memcpy(link_conf->mu_group.position, position, 74662306a36Sopenharmony_ci WLAN_USER_POSITION_LEN); 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci rcu_read_unlock(); 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_update_mu_groups); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_civoid ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, 75362306a36Sopenharmony_ci struct link_sta_info *link_sta, 75462306a36Sopenharmony_ci u8 opmode, enum nl80211_band band) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 75762306a36Sopenharmony_ci struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci u32 changed = __ieee80211_vht_handle_opmode(sdata, link_sta, 76062306a36Sopenharmony_ci opmode, band); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (changed > 0) { 76362306a36Sopenharmony_ci ieee80211_recalc_min_chandef(sdata, link_sta->link_id); 76462306a36Sopenharmony_ci rate_control_rate_update(local, sband, link_sta->sta, 76562306a36Sopenharmony_ci link_sta->link_id, changed); 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_civoid ieee80211_get_vht_mask_from_cap(__le16 vht_cap, 77062306a36Sopenharmony_ci u16 vht_mask[NL80211_VHT_NSS_MAX]) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci int i; 77362306a36Sopenharmony_ci u16 mask, cap = le16_to_cpu(vht_cap); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { 77662306a36Sopenharmony_ci mask = (cap >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 77762306a36Sopenharmony_ci switch (mask) { 77862306a36Sopenharmony_ci case IEEE80211_VHT_MCS_SUPPORT_0_7: 77962306a36Sopenharmony_ci vht_mask[i] = 0x00FF; 78062306a36Sopenharmony_ci break; 78162306a36Sopenharmony_ci case IEEE80211_VHT_MCS_SUPPORT_0_8: 78262306a36Sopenharmony_ci vht_mask[i] = 0x01FF; 78362306a36Sopenharmony_ci break; 78462306a36Sopenharmony_ci case IEEE80211_VHT_MCS_SUPPORT_0_9: 78562306a36Sopenharmony_ci vht_mask[i] = 0x03FF; 78662306a36Sopenharmony_ci break; 78762306a36Sopenharmony_ci case IEEE80211_VHT_MCS_NOT_SUPPORTED: 78862306a36Sopenharmony_ci default: 78962306a36Sopenharmony_ci vht_mask[i] = 0; 79062306a36Sopenharmony_ci break; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci} 794