18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * VHT handling 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Portions of this file 68c2ecf20Sopenharmony_ci * Copyright(c) 2015 - 2016 Intel Deutschland GmbH 78c2ecf20Sopenharmony_ci * Copyright (C) 2018 - 2020 Intel Corporation 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/ieee80211.h> 118c2ecf20Sopenharmony_ci#include <linux/export.h> 128c2ecf20Sopenharmony_ci#include <net/mac80211.h> 138c2ecf20Sopenharmony_ci#include "ieee80211_i.h" 148c2ecf20Sopenharmony_ci#include "rate.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic void __check_vhtcap_disable(struct ieee80211_sub_if_data *sdata, 188c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap, 198c2ecf20Sopenharmony_ci u32 flag) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci __le32 le_flag = cpu_to_le32(flag); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci if (sdata->u.mgd.vht_capa_mask.vht_cap_info & le_flag && 248c2ecf20Sopenharmony_ci !(sdata->u.mgd.vht_capa.vht_cap_info & le_flag)) 258c2ecf20Sopenharmony_ci vht_cap->cap &= ~flag; 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_civoid ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata, 298c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci int i; 328c2ecf20Sopenharmony_ci u16 rxmcs_mask, rxmcs_cap, rxmcs_n, txmcs_mask, txmcs_cap, txmcs_n; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (!vht_cap->vht_supported) 358c2ecf20Sopenharmony_ci return; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_STATION) 388c2ecf20Sopenharmony_ci return; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 418c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_RXLDPC); 428c2ecf20Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 438c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_SHORT_GI_80); 448c2ecf20Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 458c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_SHORT_GI_160); 468c2ecf20Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 478c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_TXSTBC); 488c2ecf20Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 498c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); 508c2ecf20Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 518c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); 528c2ecf20Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 538c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN); 548c2ecf20Sopenharmony_ci __check_vhtcap_disable(sdata, vht_cap, 558c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* Allow user to decrease AMPDU length exponent */ 588c2ecf20Sopenharmony_ci if (sdata->u.mgd.vht_capa_mask.vht_cap_info & 598c2ecf20Sopenharmony_ci cpu_to_le32(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK)) { 608c2ecf20Sopenharmony_ci u32 cap, n; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci n = le32_to_cpu(sdata->u.mgd.vht_capa.vht_cap_info) & 638c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; 648c2ecf20Sopenharmony_ci n >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; 658c2ecf20Sopenharmony_ci cap = vht_cap->cap & IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; 668c2ecf20Sopenharmony_ci cap >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (n < cap) { 698c2ecf20Sopenharmony_ci vht_cap->cap &= 708c2ecf20Sopenharmony_ci ~IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; 718c2ecf20Sopenharmony_ci vht_cap->cap |= 728c2ecf20Sopenharmony_ci n << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* Allow the user to decrease MCSes */ 778c2ecf20Sopenharmony_ci rxmcs_mask = 788c2ecf20Sopenharmony_ci le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.rx_mcs_map); 798c2ecf20Sopenharmony_ci rxmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.rx_mcs_map); 808c2ecf20Sopenharmony_ci rxmcs_n &= rxmcs_mask; 818c2ecf20Sopenharmony_ci rxmcs_cap = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci txmcs_mask = 848c2ecf20Sopenharmony_ci le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.tx_mcs_map); 858c2ecf20Sopenharmony_ci txmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.tx_mcs_map); 868c2ecf20Sopenharmony_ci txmcs_n &= txmcs_mask; 878c2ecf20Sopenharmony_ci txmcs_cap = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); 888c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 898c2ecf20Sopenharmony_ci u8 m, n, c; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci m = (rxmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 928c2ecf20Sopenharmony_ci n = (rxmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 938c2ecf20Sopenharmony_ci c = (rxmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || 968c2ecf20Sopenharmony_ci n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { 978c2ecf20Sopenharmony_ci rxmcs_cap &= ~(3 << 2*i); 988c2ecf20Sopenharmony_ci rxmcs_cap |= (rxmcs_n & (3 << 2*i)); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci m = (txmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 1028c2ecf20Sopenharmony_ci n = (txmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 1038c2ecf20Sopenharmony_ci c = (txmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || 1068c2ecf20Sopenharmony_ci n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { 1078c2ecf20Sopenharmony_ci txmcs_cap &= ~(3 << 2*i); 1088c2ecf20Sopenharmony_ci txmcs_cap |= (txmcs_n & (3 << 2*i)); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(rxmcs_cap); 1128c2ecf20Sopenharmony_ci vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(txmcs_cap); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_civoid 1168c2ecf20Sopenharmony_ciieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, 1178c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband, 1188c2ecf20Sopenharmony_ci const struct ieee80211_vht_cap *vht_cap_ie, 1198c2ecf20Sopenharmony_ci struct sta_info *sta) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; 1228c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap own_cap; 1238c2ecf20Sopenharmony_ci u32 cap_info, i; 1248c2ecf20Sopenharmony_ci bool have_80mhz; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci memset(vht_cap, 0, sizeof(*vht_cap)); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (!sta->sta.ht_cap.ht_supported) 1298c2ecf20Sopenharmony_ci return; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (!vht_cap_ie || !sband->vht_cap.vht_supported) 1328c2ecf20Sopenharmony_ci return; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* Allow VHT if at least one channel on the sband supports 80 MHz */ 1358c2ecf20Sopenharmony_ci have_80mhz = false; 1368c2ecf20Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 1378c2ecf20Sopenharmony_ci if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED | 1388c2ecf20Sopenharmony_ci IEEE80211_CHAN_NO_80MHZ)) 1398c2ecf20Sopenharmony_ci continue; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci have_80mhz = true; 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (!have_80mhz) 1468c2ecf20Sopenharmony_ci return; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* 1498c2ecf20Sopenharmony_ci * A VHT STA must support 40 MHz, but if we verify that here 1508c2ecf20Sopenharmony_ci * then we break a few things - some APs (e.g. Netgear R6300v2 1518c2ecf20Sopenharmony_ci * and others based on the BCM4360 chipset) will unset this 1528c2ecf20Sopenharmony_ci * capability bit when operating in 20 MHz. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci vht_cap->vht_supported = true; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci own_cap = sband->vht_cap; 1588c2ecf20Sopenharmony_ci /* 1598c2ecf20Sopenharmony_ci * If user has specified capability overrides, take care 1608c2ecf20Sopenharmony_ci * of that if the station we're setting up is the AP that 1618c2ecf20Sopenharmony_ci * we advertised a restricted capability set to. Override 1628c2ecf20Sopenharmony_ci * our own capabilities and then use those below. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_STATION && 1658c2ecf20Sopenharmony_ci !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) 1668c2ecf20Sopenharmony_ci ieee80211_apply_vhtcap_overrides(sdata, &own_cap); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* take some capabilities as-is */ 1698c2ecf20Sopenharmony_ci cap_info = le32_to_cpu(vht_cap_ie->vht_cap_info); 1708c2ecf20Sopenharmony_ci vht_cap->cap = cap_info; 1718c2ecf20Sopenharmony_ci vht_cap->cap &= IEEE80211_VHT_CAP_RXLDPC | 1728c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_VHT_TXOP_PS | 1738c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_HTC_VHT | 1748c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | 1758c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB | 1768c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB | 1778c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | 1788c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci vht_cap->cap |= min_t(u32, cap_info & IEEE80211_VHT_CAP_MAX_MPDU_MASK, 1818c2ecf20Sopenharmony_ci own_cap.cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* and some based on our own capabilities */ 1848c2ecf20Sopenharmony_ci switch (own_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { 1858c2ecf20Sopenharmony_ci case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: 1868c2ecf20Sopenharmony_ci vht_cap->cap |= cap_info & 1878c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: 1908c2ecf20Sopenharmony_ci vht_cap->cap |= cap_info & 1918c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci default: 1948c2ecf20Sopenharmony_ci /* nothing */ 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* symmetric capabilities */ 1998c2ecf20Sopenharmony_ci vht_cap->cap |= cap_info & own_cap.cap & 2008c2ecf20Sopenharmony_ci (IEEE80211_VHT_CAP_SHORT_GI_80 | 2018c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_SHORT_GI_160); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* remaining ones */ 2048c2ecf20Sopenharmony_ci if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) 2058c2ecf20Sopenharmony_ci vht_cap->cap |= cap_info & 2068c2ecf20Sopenharmony_ci (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | 2078c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) 2108c2ecf20Sopenharmony_ci vht_cap->cap |= cap_info & 2118c2ecf20Sopenharmony_ci (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | 2128c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) 2158c2ecf20Sopenharmony_ci vht_cap->cap |= cap_info & 2168c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) 2198c2ecf20Sopenharmony_ci vht_cap->cap |= cap_info & 2208c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (own_cap.cap & IEEE80211_VHT_CAP_TXSTBC) 2238c2ecf20Sopenharmony_ci vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_RXSTBC_MASK; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (own_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK) 2268c2ecf20Sopenharmony_ci vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_TXSTBC; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Copy peer MCS info, the driver might need them. */ 2298c2ecf20Sopenharmony_ci memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, 2308c2ecf20Sopenharmony_ci sizeof(struct ieee80211_vht_mcs_info)); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* copy EXT_NSS_BW Support value or remove the capability */ 2338c2ecf20Sopenharmony_ci if (ieee80211_hw_check(&sdata->local->hw, SUPPORTS_VHT_EXT_NSS_BW)) 2348c2ecf20Sopenharmony_ci vht_cap->cap |= (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK); 2358c2ecf20Sopenharmony_ci else 2368c2ecf20Sopenharmony_ci vht_cap->vht_mcs.tx_highest &= 2378c2ecf20Sopenharmony_ci ~cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* but also restrict MCSes */ 2408c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 2418c2ecf20Sopenharmony_ci u16 own_rx, own_tx, peer_rx, peer_tx; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci own_rx = le16_to_cpu(own_cap.vht_mcs.rx_mcs_map); 2448c2ecf20Sopenharmony_ci own_rx = (own_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci own_tx = le16_to_cpu(own_cap.vht_mcs.tx_mcs_map); 2478c2ecf20Sopenharmony_ci own_tx = (own_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci peer_rx = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); 2508c2ecf20Sopenharmony_ci peer_rx = (peer_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci peer_tx = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); 2538c2ecf20Sopenharmony_ci peer_tx = (peer_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (peer_tx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 2568c2ecf20Sopenharmony_ci if (own_rx == IEEE80211_VHT_MCS_NOT_SUPPORTED) 2578c2ecf20Sopenharmony_ci peer_tx = IEEE80211_VHT_MCS_NOT_SUPPORTED; 2588c2ecf20Sopenharmony_ci else if (own_rx < peer_tx) 2598c2ecf20Sopenharmony_ci peer_tx = own_rx; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (peer_rx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 2638c2ecf20Sopenharmony_ci if (own_tx == IEEE80211_VHT_MCS_NOT_SUPPORTED) 2648c2ecf20Sopenharmony_ci peer_rx = IEEE80211_VHT_MCS_NOT_SUPPORTED; 2658c2ecf20Sopenharmony_ci else if (own_tx < peer_rx) 2668c2ecf20Sopenharmony_ci peer_rx = own_tx; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci vht_cap->vht_mcs.rx_mcs_map &= 2708c2ecf20Sopenharmony_ci ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); 2718c2ecf20Sopenharmony_ci vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(peer_rx << i * 2); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci vht_cap->vht_mcs.tx_mcs_map &= 2748c2ecf20Sopenharmony_ci ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); 2758c2ecf20Sopenharmony_ci vht_cap->vht_mcs.tx_mcs_map |= cpu_to_le16(peer_tx << i * 2); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* 2798c2ecf20Sopenharmony_ci * This is a workaround for VHT-enabled STAs which break the spec 2808c2ecf20Sopenharmony_ci * and have the VHT-MCS Rx map filled in with value 3 for all eight 2818c2ecf20Sopenharmony_ci * spacial streams, an example is AR9462. 2828c2ecf20Sopenharmony_ci * 2838c2ecf20Sopenharmony_ci * As per spec, in section 22.1.1 Introduction to the VHT PHY 2848c2ecf20Sopenharmony_ci * A VHT STA shall support at least single spactial stream VHT-MCSs 2858c2ecf20Sopenharmony_ci * 0 to 7 (transmit and receive) in all supported channel widths. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ci if (vht_cap->vht_mcs.rx_mcs_map == cpu_to_le16(0xFFFF)) { 2888c2ecf20Sopenharmony_ci vht_cap->vht_supported = false; 2898c2ecf20Sopenharmony_ci sdata_info(sdata, "Ignoring VHT IE from %pM due to invalid rx_mcs_map\n", 2908c2ecf20Sopenharmony_ci sta->addr); 2918c2ecf20Sopenharmony_ci return; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* finally set up the bandwidth */ 2958c2ecf20Sopenharmony_ci switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { 2968c2ecf20Sopenharmony_ci case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: 2978c2ecf20Sopenharmony_ci case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: 2988c2ecf20Sopenharmony_ci sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci default: 3018c2ecf20Sopenharmony_ci sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (!(vht_cap->vht_mcs.tx_highest & 3048c2ecf20Sopenharmony_ci cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE))) 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* 3088c2ecf20Sopenharmony_ci * If this is non-zero, then it does support 160 MHz after all, 3098c2ecf20Sopenharmony_ci * in one form or the other. We don't distinguish here (or even 3108c2ecf20Sopenharmony_ci * above) between 160 and 80+80 yet. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci if (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) 3138c2ecf20Sopenharmony_ci sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci switch (vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) { 3198c2ecf20Sopenharmony_ci case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: 3208c2ecf20Sopenharmony_ci sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454; 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991: 3238c2ecf20Sopenharmony_ci sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991; 3248c2ecf20Sopenharmony_ci break; 3258c2ecf20Sopenharmony_ci case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895: 3268c2ecf20Sopenharmony_ci default: 3278c2ecf20Sopenharmony_ci sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895; 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/* FIXME: move this to some better location - parses HE now */ 3338c2ecf20Sopenharmony_cienum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; 3368c2ecf20Sopenharmony_ci struct ieee80211_sta_he_cap *he_cap = &sta->sta.he_cap; 3378c2ecf20Sopenharmony_ci u32 cap_width; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (he_cap->has_he) { 3408c2ecf20Sopenharmony_ci u8 info = he_cap->he_cap_elem.phy_cap_info[0]; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (sta->sdata->vif.bss_conf.chandef.chan->band == 3438c2ecf20Sopenharmony_ci NL80211_BAND_2GHZ) { 3448c2ecf20Sopenharmony_ci if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) 3458c2ecf20Sopenharmony_ci return IEEE80211_STA_RX_BW_40; 3468c2ecf20Sopenharmony_ci else 3478c2ecf20Sopenharmony_ci return IEEE80211_STA_RX_BW_20; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G || 3518c2ecf20Sopenharmony_ci info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) 3528c2ecf20Sopenharmony_ci return IEEE80211_STA_RX_BW_160; 3538c2ecf20Sopenharmony_ci else if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) 3548c2ecf20Sopenharmony_ci return IEEE80211_STA_RX_BW_80; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return IEEE80211_STA_RX_BW_20; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (!vht_cap->vht_supported) 3608c2ecf20Sopenharmony_ci return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 3618c2ecf20Sopenharmony_ci IEEE80211_STA_RX_BW_40 : 3628c2ecf20Sopenharmony_ci IEEE80211_STA_RX_BW_20; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ || 3678c2ecf20Sopenharmony_ci cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) 3688c2ecf20Sopenharmony_ci return IEEE80211_STA_RX_BW_160; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* 3718c2ecf20Sopenharmony_ci * If this is non-zero, then it does support 160 MHz after all, 3728c2ecf20Sopenharmony_ci * in one form or the other. We don't distinguish here (or even 3738c2ecf20Sopenharmony_ci * above) between 160 and 80+80 yet. 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_ci if (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) 3768c2ecf20Sopenharmony_ci return IEEE80211_STA_RX_BW_160; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return IEEE80211_STA_RX_BW_80; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cienum nl80211_chan_width ieee80211_sta_cap_chan_bw(struct sta_info *sta) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; 3848c2ecf20Sopenharmony_ci u32 cap_width; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (!vht_cap->vht_supported) { 3878c2ecf20Sopenharmony_ci if (!sta->sta.ht_cap.ht_supported) 3888c2ecf20Sopenharmony_ci return NL80211_CHAN_WIDTH_20_NOHT; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 3918c2ecf20Sopenharmony_ci NL80211_CHAN_WIDTH_40 : NL80211_CHAN_WIDTH_20; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) 3978c2ecf20Sopenharmony_ci return NL80211_CHAN_WIDTH_160; 3988c2ecf20Sopenharmony_ci else if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) 3998c2ecf20Sopenharmony_ci return NL80211_CHAN_WIDTH_80P80; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return NL80211_CHAN_WIDTH_80; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cienum nl80211_chan_width 4058c2ecf20Sopenharmony_ciieee80211_sta_rx_bw_to_chan_width(struct sta_info *sta) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci enum ieee80211_sta_rx_bandwidth cur_bw = sta->sta.bandwidth; 4088c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; 4098c2ecf20Sopenharmony_ci u32 cap_width; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci switch (cur_bw) { 4128c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_20: 4138c2ecf20Sopenharmony_ci if (!sta->sta.ht_cap.ht_supported) 4148c2ecf20Sopenharmony_ci return NL80211_CHAN_WIDTH_20_NOHT; 4158c2ecf20Sopenharmony_ci else 4168c2ecf20Sopenharmony_ci return NL80211_CHAN_WIDTH_20; 4178c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_40: 4188c2ecf20Sopenharmony_ci return NL80211_CHAN_WIDTH_40; 4198c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_80: 4208c2ecf20Sopenharmony_ci return NL80211_CHAN_WIDTH_80; 4218c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_160: 4228c2ecf20Sopenharmony_ci cap_width = 4238c2ecf20Sopenharmony_ci vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) 4268c2ecf20Sopenharmony_ci return NL80211_CHAN_WIDTH_160; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return NL80211_CHAN_WIDTH_80P80; 4298c2ecf20Sopenharmony_ci default: 4308c2ecf20Sopenharmony_ci return NL80211_CHAN_WIDTH_20; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cienum ieee80211_sta_rx_bandwidth 4358c2ecf20Sopenharmony_ciieee80211_chan_width_to_rx_bw(enum nl80211_chan_width width) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci switch (width) { 4388c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_20_NOHT: 4398c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_20: 4408c2ecf20Sopenharmony_ci return IEEE80211_STA_RX_BW_20; 4418c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 4428c2ecf20Sopenharmony_ci return IEEE80211_STA_RX_BW_40; 4438c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 4448c2ecf20Sopenharmony_ci return IEEE80211_STA_RX_BW_80; 4458c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 4468c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 4478c2ecf20Sopenharmony_ci return IEEE80211_STA_RX_BW_160; 4488c2ecf20Sopenharmony_ci default: 4498c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 4508c2ecf20Sopenharmony_ci return IEEE80211_STA_RX_BW_20; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci/* FIXME: rename/move - this deals with everything not just VHT */ 4558c2ecf20Sopenharmony_cienum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = sta->sdata; 4588c2ecf20Sopenharmony_ci enum ieee80211_sta_rx_bandwidth bw; 4598c2ecf20Sopenharmony_ci enum nl80211_chan_width bss_width = sdata->vif.bss_conf.chandef.width; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci bw = ieee80211_sta_cap_rx_bw(sta); 4628c2ecf20Sopenharmony_ci bw = min(bw, sta->cur_max_bandwidth); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci /* Don't consider AP's bandwidth for TDLS peers, section 11.23.1 of 4658c2ecf20Sopenharmony_ci * IEEE80211-2016 specification makes higher bandwidth operation 4668c2ecf20Sopenharmony_ci * possible on the TDLS link if the peers have wider bandwidth 4678c2ecf20Sopenharmony_ci * capability. 4688c2ecf20Sopenharmony_ci * 4698c2ecf20Sopenharmony_ci * However, in this case, and only if the TDLS peer is authorized, 4708c2ecf20Sopenharmony_ci * limit to the tdls_chandef so that the configuration here isn't 4718c2ecf20Sopenharmony_ci * wider than what's actually requested on the channel context. 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_ci if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && 4748c2ecf20Sopenharmony_ci test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) && 4758c2ecf20Sopenharmony_ci test_sta_flag(sta, WLAN_STA_AUTHORIZED) && 4768c2ecf20Sopenharmony_ci sta->tdls_chandef.chan) 4778c2ecf20Sopenharmony_ci bw = min(bw, ieee80211_chan_width_to_rx_bw(sta->tdls_chandef.width)); 4788c2ecf20Sopenharmony_ci else 4798c2ecf20Sopenharmony_ci bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width)); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return bw; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_civoid ieee80211_sta_set_rx_nss(struct sta_info *sta) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, rx_nss; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci /* if we received a notification already don't overwrite it */ 4898c2ecf20Sopenharmony_ci if (sta->sta.rx_nss) 4908c2ecf20Sopenharmony_ci return; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (sta->sta.he_cap.has_he) { 4938c2ecf20Sopenharmony_ci int i; 4948c2ecf20Sopenharmony_ci u8 rx_mcs_80 = 0, rx_mcs_160 = 0; 4958c2ecf20Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap = &sta->sta.he_cap; 4968c2ecf20Sopenharmony_ci u16 mcs_160_map = 4978c2ecf20Sopenharmony_ci le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); 4988c2ecf20Sopenharmony_ci u16 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci for (i = 7; i >= 0; i--) { 5018c2ecf20Sopenharmony_ci u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 5048c2ecf20Sopenharmony_ci rx_mcs_160 = i + 1; 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci for (i = 7; i >= 0; i--) { 5098c2ecf20Sopenharmony_ci u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 5128c2ecf20Sopenharmony_ci rx_mcs_80 = i + 1; 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci he_rx_nss = min(rx_mcs_80, rx_mcs_160); 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (sta->sta.ht_cap.ht_supported) { 5218c2ecf20Sopenharmony_ci if (sta->sta.ht_cap.mcs.rx_mask[0]) 5228c2ecf20Sopenharmony_ci ht_rx_nss++; 5238c2ecf20Sopenharmony_ci if (sta->sta.ht_cap.mcs.rx_mask[1]) 5248c2ecf20Sopenharmony_ci ht_rx_nss++; 5258c2ecf20Sopenharmony_ci if (sta->sta.ht_cap.mcs.rx_mask[2]) 5268c2ecf20Sopenharmony_ci ht_rx_nss++; 5278c2ecf20Sopenharmony_ci if (sta->sta.ht_cap.mcs.rx_mask[3]) 5288c2ecf20Sopenharmony_ci ht_rx_nss++; 5298c2ecf20Sopenharmony_ci /* FIXME: consider rx_highest? */ 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (sta->sta.vht_cap.vht_supported) { 5338c2ecf20Sopenharmony_ci int i; 5348c2ecf20Sopenharmony_ci u16 rx_mcs_map; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci rx_mcs_map = le16_to_cpu(sta->sta.vht_cap.vht_mcs.rx_mcs_map); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci for (i = 7; i >= 0; i--) { 5398c2ecf20Sopenharmony_ci u8 mcs = (rx_mcs_map >> (2 * i)) & 3; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 5428c2ecf20Sopenharmony_ci vht_rx_nss = i + 1; 5438c2ecf20Sopenharmony_ci break; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci /* FIXME: consider rx_highest? */ 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci rx_nss = max(vht_rx_nss, ht_rx_nss); 5508c2ecf20Sopenharmony_ci rx_nss = max(he_rx_nss, rx_nss); 5518c2ecf20Sopenharmony_ci sta->sta.rx_nss = max_t(u8, 1, rx_nss); 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ciu32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, 5558c2ecf20Sopenharmony_ci struct sta_info *sta, u8 opmode, 5568c2ecf20Sopenharmony_ci enum nl80211_band band) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci enum ieee80211_sta_rx_bandwidth new_bw; 5598c2ecf20Sopenharmony_ci struct sta_opmode_info sta_opmode = {}; 5608c2ecf20Sopenharmony_ci u32 changed = 0; 5618c2ecf20Sopenharmony_ci u8 nss; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci /* ignore - no support for BF yet */ 5648c2ecf20Sopenharmony_ci if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF) 5658c2ecf20Sopenharmony_ci return 0; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK; 5688c2ecf20Sopenharmony_ci nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; 5698c2ecf20Sopenharmony_ci nss += 1; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (sta->sta.rx_nss != nss) { 5728c2ecf20Sopenharmony_ci sta->sta.rx_nss = nss; 5738c2ecf20Sopenharmony_ci sta_opmode.rx_nss = nss; 5748c2ecf20Sopenharmony_ci changed |= IEEE80211_RC_NSS_CHANGED; 5758c2ecf20Sopenharmony_ci sta_opmode.changed |= STA_OPMODE_N_SS_CHANGED; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) { 5798c2ecf20Sopenharmony_ci case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ: 5808c2ecf20Sopenharmony_ci /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */ 5818c2ecf20Sopenharmony_ci sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20; 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ: 5848c2ecf20Sopenharmony_ci /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */ 5858c2ecf20Sopenharmony_ci sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40; 5868c2ecf20Sopenharmony_ci break; 5878c2ecf20Sopenharmony_ci case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ: 5888c2ecf20Sopenharmony_ci if (opmode & IEEE80211_OPMODE_NOTIF_BW_160_80P80) 5898c2ecf20Sopenharmony_ci sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; 5908c2ecf20Sopenharmony_ci else 5918c2ecf20Sopenharmony_ci sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; 5928c2ecf20Sopenharmony_ci break; 5938c2ecf20Sopenharmony_ci case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ: 5948c2ecf20Sopenharmony_ci /* legacy only, no longer used by newer spec */ 5958c2ecf20Sopenharmony_ci sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; 5968c2ecf20Sopenharmony_ci break; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci new_bw = ieee80211_sta_cur_vht_bw(sta); 6008c2ecf20Sopenharmony_ci if (new_bw != sta->sta.bandwidth) { 6018c2ecf20Sopenharmony_ci sta->sta.bandwidth = new_bw; 6028c2ecf20Sopenharmony_ci sta_opmode.bw = ieee80211_sta_rx_bw_to_chan_width(sta); 6038c2ecf20Sopenharmony_ci changed |= IEEE80211_RC_BW_CHANGED; 6048c2ecf20Sopenharmony_ci sta_opmode.changed |= STA_OPMODE_MAX_BW_CHANGED; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (sta_opmode.changed) 6088c2ecf20Sopenharmony_ci cfg80211_sta_opmode_change_notify(sdata->dev, sta->addr, 6098c2ecf20Sopenharmony_ci &sta_opmode, GFP_KERNEL); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return changed; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_civoid ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata, 6158c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (!sdata->vif.mu_mimo_owner) 6208c2ecf20Sopenharmony_ci return; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (!memcmp(mgmt->u.action.u.vht_group_notif.position, 6238c2ecf20Sopenharmony_ci bss_conf->mu_group.position, WLAN_USER_POSITION_LEN) && 6248c2ecf20Sopenharmony_ci !memcmp(mgmt->u.action.u.vht_group_notif.membership, 6258c2ecf20Sopenharmony_ci bss_conf->mu_group.membership, WLAN_MEMBERSHIP_LEN)) 6268c2ecf20Sopenharmony_ci return; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci memcpy(bss_conf->mu_group.membership, 6298c2ecf20Sopenharmony_ci mgmt->u.action.u.vht_group_notif.membership, 6308c2ecf20Sopenharmony_ci WLAN_MEMBERSHIP_LEN); 6318c2ecf20Sopenharmony_ci memcpy(bss_conf->mu_group.position, 6328c2ecf20Sopenharmony_ci mgmt->u.action.u.vht_group_notif.position, 6338c2ecf20Sopenharmony_ci WLAN_USER_POSITION_LEN); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_MU_GROUPS); 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_civoid ieee80211_update_mu_groups(struct ieee80211_vif *vif, 6398c2ecf20Sopenharmony_ci const u8 *membership, const u8 *position) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!vif->mu_mimo_owner)) 6448c2ecf20Sopenharmony_ci return; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci memcpy(bss_conf->mu_group.membership, membership, WLAN_MEMBERSHIP_LEN); 6478c2ecf20Sopenharmony_ci memcpy(bss_conf->mu_group.position, position, WLAN_USER_POSITION_LEN); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_update_mu_groups); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_civoid ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, 6528c2ecf20Sopenharmony_ci struct sta_info *sta, u8 opmode, 6538c2ecf20Sopenharmony_ci enum nl80211_band band) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 6568c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci u32 changed = __ieee80211_vht_handle_opmode(sdata, sta, opmode, band); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (changed > 0) { 6618c2ecf20Sopenharmony_ci ieee80211_recalc_min_chandef(sdata); 6628c2ecf20Sopenharmony_ci rate_control_rate_update(local, sband, sta, changed); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_civoid ieee80211_get_vht_mask_from_cap(__le16 vht_cap, 6678c2ecf20Sopenharmony_ci u16 vht_mask[NL80211_VHT_NSS_MAX]) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci int i; 6708c2ecf20Sopenharmony_ci u16 mask, cap = le16_to_cpu(vht_cap); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { 6738c2ecf20Sopenharmony_ci mask = (cap >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; 6748c2ecf20Sopenharmony_ci switch (mask) { 6758c2ecf20Sopenharmony_ci case IEEE80211_VHT_MCS_SUPPORT_0_7: 6768c2ecf20Sopenharmony_ci vht_mask[i] = 0x00FF; 6778c2ecf20Sopenharmony_ci break; 6788c2ecf20Sopenharmony_ci case IEEE80211_VHT_MCS_SUPPORT_0_8: 6798c2ecf20Sopenharmony_ci vht_mask[i] = 0x01FF; 6808c2ecf20Sopenharmony_ci break; 6818c2ecf20Sopenharmony_ci case IEEE80211_VHT_MCS_SUPPORT_0_9: 6828c2ecf20Sopenharmony_ci vht_mask[i] = 0x03FF; 6838c2ecf20Sopenharmony_ci break; 6848c2ecf20Sopenharmony_ci case IEEE80211_VHT_MCS_NOT_SUPPORTED: 6858c2ecf20Sopenharmony_ci default: 6868c2ecf20Sopenharmony_ci vht_mask[i] = 0; 6878c2ecf20Sopenharmony_ci break; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci} 691