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