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