xref: /openbmc/linux/net/mac80211/he.c (revision 6c71a0574249f5e5a45fe055ab5f837023d5eeca)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
241cbb0f5SLuca Coelho /*
341cbb0f5SLuca Coelho  * HE handling
441cbb0f5SLuca Coelho  *
541cbb0f5SLuca Coelho  * Copyright(c) 2017 Intel Deutschland GmbH
6c37ab22bSJohannes Berg  * Copyright(c) 2019 - 2023 Intel Corporation
741cbb0f5SLuca Coelho  */
841cbb0f5SLuca Coelho 
941cbb0f5SLuca Coelho #include "ieee80211_i.h"
1041cbb0f5SLuca Coelho 
111bb9a8a4SJohannes Berg static void
ieee80211_update_from_he_6ghz_capa(const struct ieee80211_he_6ghz_capa * he_6ghz_capa,struct link_sta_info * link_sta)121bb9a8a4SJohannes Berg ieee80211_update_from_he_6ghz_capa(const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
13c71420dbSJohannes Berg 				   struct link_sta_info *link_sta)
141bb9a8a4SJohannes Berg {
15c71420dbSJohannes Berg 	struct sta_info *sta = link_sta->sta;
161bb9a8a4SJohannes Berg 	enum ieee80211_smps_mode smps_mode;
171bb9a8a4SJohannes Berg 
181bb9a8a4SJohannes Berg 	if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
191bb9a8a4SJohannes Berg 	    sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
201bb9a8a4SJohannes Berg 		switch (le16_get_bits(he_6ghz_capa->capa,
211bb9a8a4SJohannes Berg 				      IEEE80211_HE_6GHZ_CAP_SM_PS)) {
221bb9a8a4SJohannes Berg 		case WLAN_HT_CAP_SM_PS_INVALID:
231bb9a8a4SJohannes Berg 		case WLAN_HT_CAP_SM_PS_STATIC:
241bb9a8a4SJohannes Berg 			smps_mode = IEEE80211_SMPS_STATIC;
251bb9a8a4SJohannes Berg 			break;
261bb9a8a4SJohannes Berg 		case WLAN_HT_CAP_SM_PS_DYNAMIC:
271bb9a8a4SJohannes Berg 			smps_mode = IEEE80211_SMPS_DYNAMIC;
281bb9a8a4SJohannes Berg 			break;
291bb9a8a4SJohannes Berg 		case WLAN_HT_CAP_SM_PS_DISABLED:
301bb9a8a4SJohannes Berg 			smps_mode = IEEE80211_SMPS_OFF;
311bb9a8a4SJohannes Berg 			break;
321bb9a8a4SJohannes Berg 		}
331bb9a8a4SJohannes Berg 
34261ce887SBenjamin Berg 		link_sta->pub->smps_mode = smps_mode;
351bb9a8a4SJohannes Berg 	} else {
36261ce887SBenjamin Berg 		link_sta->pub->smps_mode = IEEE80211_SMPS_OFF;
371bb9a8a4SJohannes Berg 	}
381bb9a8a4SJohannes Berg 
391bb9a8a4SJohannes Berg 	switch (le16_get_bits(he_6ghz_capa->capa,
401bb9a8a4SJohannes Berg 			      IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN)) {
411bb9a8a4SJohannes Berg 	case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
424c51541dSBenjamin Berg 		link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454;
431bb9a8a4SJohannes Berg 		break;
441bb9a8a4SJohannes Berg 	case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
454c51541dSBenjamin Berg 		link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991;
461bb9a8a4SJohannes Berg 		break;
471bb9a8a4SJohannes Berg 	case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895:
481bb9a8a4SJohannes Berg 	default:
494c51541dSBenjamin Berg 		link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895;
501bb9a8a4SJohannes Berg 		break;
511bb9a8a4SJohannes Berg 	}
521bb9a8a4SJohannes Berg 
534c51541dSBenjamin Berg 	ieee80211_sta_recalc_aggregates(&sta->sta);
544c51541dSBenjamin Berg 
55c71420dbSJohannes Berg 	link_sta->pub->he_6ghz_capa = *he_6ghz_capa;
561bb9a8a4SJohannes Berg }
571bb9a8a4SJohannes Berg 
ieee80211_he_mcs_disable(__le16 * he_mcs)587f7aa94bSWen Gong static void ieee80211_he_mcs_disable(__le16 *he_mcs)
597f7aa94bSWen Gong {
607f7aa94bSWen Gong 	u32 i;
617f7aa94bSWen Gong 
627f7aa94bSWen Gong 	for (i = 0; i < 8; i++)
637f7aa94bSWen Gong 		*he_mcs |= cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << i * 2);
647f7aa94bSWen Gong }
657f7aa94bSWen Gong 
ieee80211_he_mcs_intersection(__le16 * he_own_rx,__le16 * he_peer_rx,__le16 * he_own_tx,__le16 * he_peer_tx)667f7aa94bSWen Gong static void ieee80211_he_mcs_intersection(__le16 *he_own_rx, __le16 *he_peer_rx,
677f7aa94bSWen Gong 					  __le16 *he_own_tx, __le16 *he_peer_tx)
687f7aa94bSWen Gong {
697f7aa94bSWen Gong 	u32 i;
707f7aa94bSWen Gong 	u16 own_rx, own_tx, peer_rx, peer_tx;
717f7aa94bSWen Gong 
727f7aa94bSWen Gong 	for (i = 0; i < 8; i++) {
737f7aa94bSWen Gong 		own_rx = le16_to_cpu(*he_own_rx);
747f7aa94bSWen Gong 		own_rx = (own_rx >> i * 2) & IEEE80211_HE_MCS_NOT_SUPPORTED;
757f7aa94bSWen Gong 
767f7aa94bSWen Gong 		own_tx = le16_to_cpu(*he_own_tx);
777f7aa94bSWen Gong 		own_tx = (own_tx >> i * 2) & IEEE80211_HE_MCS_NOT_SUPPORTED;
787f7aa94bSWen Gong 
797f7aa94bSWen Gong 		peer_rx = le16_to_cpu(*he_peer_rx);
807f7aa94bSWen Gong 		peer_rx = (peer_rx >> i * 2) & IEEE80211_HE_MCS_NOT_SUPPORTED;
817f7aa94bSWen Gong 
827f7aa94bSWen Gong 		peer_tx = le16_to_cpu(*he_peer_tx);
837f7aa94bSWen Gong 		peer_tx = (peer_tx >> i * 2) & IEEE80211_HE_MCS_NOT_SUPPORTED;
847f7aa94bSWen Gong 
857f7aa94bSWen Gong 		if (peer_tx != IEEE80211_HE_MCS_NOT_SUPPORTED) {
867f7aa94bSWen Gong 			if (own_rx == IEEE80211_HE_MCS_NOT_SUPPORTED)
877f7aa94bSWen Gong 				peer_tx = IEEE80211_HE_MCS_NOT_SUPPORTED;
887f7aa94bSWen Gong 			else if (own_rx < peer_tx)
897f7aa94bSWen Gong 				peer_tx = own_rx;
907f7aa94bSWen Gong 		}
917f7aa94bSWen Gong 
927f7aa94bSWen Gong 		if (peer_rx != IEEE80211_HE_MCS_NOT_SUPPORTED) {
937f7aa94bSWen Gong 			if (own_tx == IEEE80211_HE_MCS_NOT_SUPPORTED)
947f7aa94bSWen Gong 				peer_rx = IEEE80211_HE_MCS_NOT_SUPPORTED;
957f7aa94bSWen Gong 			else if (own_tx < peer_rx)
967f7aa94bSWen Gong 				peer_rx = own_tx;
977f7aa94bSWen Gong 		}
987f7aa94bSWen Gong 
997f7aa94bSWen Gong 		*he_peer_rx &=
1007f7aa94bSWen Gong 			~cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << i * 2);
1017f7aa94bSWen Gong 		*he_peer_rx |= cpu_to_le16(peer_rx << i * 2);
1027f7aa94bSWen Gong 
1037f7aa94bSWen Gong 		*he_peer_tx &=
1047f7aa94bSWen Gong 			~cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << i * 2);
1057f7aa94bSWen Gong 		*he_peer_tx |= cpu_to_le16(peer_tx << i * 2);
1067f7aa94bSWen Gong 	}
1077f7aa94bSWen Gong }
1087f7aa94bSWen Gong 
10941cbb0f5SLuca Coelho void
ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data * sdata,struct ieee80211_supported_band * sband,const u8 * he_cap_ie,u8 he_cap_len,const struct ieee80211_he_6ghz_capa * he_6ghz_capa,struct link_sta_info * link_sta)11041cbb0f5SLuca Coelho ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
11141cbb0f5SLuca Coelho 				  struct ieee80211_supported_band *sband,
11241cbb0f5SLuca Coelho 				  const u8 *he_cap_ie, u8 he_cap_len,
1131bb9a8a4SJohannes Berg 				  const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
114c71420dbSJohannes Berg 				  struct link_sta_info *link_sta)
11541cbb0f5SLuca Coelho {
116c71420dbSJohannes Berg 	struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
117c37ab22bSJohannes Berg 	const struct ieee80211_sta_he_cap *own_he_cap_ptr;
11895f83ee8SAbinaya Kalaiselvan 	struct ieee80211_sta_he_cap own_he_cap;
11941cbb0f5SLuca Coelho 	struct ieee80211_he_cap_elem *he_cap_ie_elem = (void *)he_cap_ie;
12041cbb0f5SLuca Coelho 	u8 he_ppe_size;
12141cbb0f5SLuca Coelho 	u8 mcs_nss_size;
12241cbb0f5SLuca Coelho 	u8 he_total_size;
1237f7aa94bSWen Gong 	bool own_160, peer_160, own_80p80, peer_80p80;
12441cbb0f5SLuca Coelho 
12541cbb0f5SLuca Coelho 	memset(he_cap, 0, sizeof(*he_cap));
12641cbb0f5SLuca Coelho 
127c37ab22bSJohannes Berg 	if (!he_cap_ie)
12841cbb0f5SLuca Coelho 		return;
12941cbb0f5SLuca Coelho 
130c37ab22bSJohannes Berg 	own_he_cap_ptr =
1311ec7291eSJohannes Berg 		ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
132c37ab22bSJohannes Berg 	if (!own_he_cap_ptr)
133c37ab22bSJohannes Berg 		return;
134c37ab22bSJohannes Berg 
135c37ab22bSJohannes Berg 	own_he_cap = *own_he_cap_ptr;
13695f83ee8SAbinaya Kalaiselvan 
13741cbb0f5SLuca Coelho 	/* Make sure size is OK */
13841cbb0f5SLuca Coelho 	mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap_ie_elem);
13941cbb0f5SLuca Coelho 	he_ppe_size =
14041cbb0f5SLuca Coelho 		ieee80211_he_ppe_size(he_cap_ie[sizeof(he_cap->he_cap_elem) +
14141cbb0f5SLuca Coelho 						mcs_nss_size],
14241cbb0f5SLuca Coelho 				      he_cap_ie_elem->phy_cap_info);
14341cbb0f5SLuca Coelho 	he_total_size = sizeof(he_cap->he_cap_elem) + mcs_nss_size +
14441cbb0f5SLuca Coelho 			he_ppe_size;
14541cbb0f5SLuca Coelho 	if (he_cap_len < he_total_size)
14641cbb0f5SLuca Coelho 		return;
14741cbb0f5SLuca Coelho 
14841cbb0f5SLuca Coelho 	memcpy(&he_cap->he_cap_elem, he_cap_ie, sizeof(he_cap->he_cap_elem));
14941cbb0f5SLuca Coelho 
15041cbb0f5SLuca Coelho 	/* HE Tx/Rx HE MCS NSS Support Field */
15141cbb0f5SLuca Coelho 	memcpy(&he_cap->he_mcs_nss_supp,
15241cbb0f5SLuca Coelho 	       &he_cap_ie[sizeof(he_cap->he_cap_elem)], mcs_nss_size);
15341cbb0f5SLuca Coelho 
15441cbb0f5SLuca Coelho 	/* Check if there are (optional) PPE Thresholds */
15541cbb0f5SLuca Coelho 	if (he_cap->he_cap_elem.phy_cap_info[6] &
15641cbb0f5SLuca Coelho 	    IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT)
15741cbb0f5SLuca Coelho 		memcpy(he_cap->ppe_thres,
15841cbb0f5SLuca Coelho 		       &he_cap_ie[sizeof(he_cap->he_cap_elem) + mcs_nss_size],
15941cbb0f5SLuca Coelho 		       he_ppe_size);
16041cbb0f5SLuca Coelho 
16141cbb0f5SLuca Coelho 	he_cap->has_he = true;
162cf2c9cc3SJohannes Berg 
163c71420dbSJohannes Berg 	link_sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(link_sta);
164c71420dbSJohannes Berg 	link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta);
1651bb9a8a4SJohannes Berg 
1661bb9a8a4SJohannes Berg 	if (sband->band == NL80211_BAND_6GHZ && he_6ghz_capa)
167c71420dbSJohannes Berg 		ieee80211_update_from_he_6ghz_capa(he_6ghz_capa, link_sta);
1687f7aa94bSWen Gong 
1697f7aa94bSWen Gong 	ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_80,
1707f7aa94bSWen Gong 				      &he_cap->he_mcs_nss_supp.rx_mcs_80,
1717f7aa94bSWen Gong 				      &own_he_cap.he_mcs_nss_supp.tx_mcs_80,
1727f7aa94bSWen Gong 				      &he_cap->he_mcs_nss_supp.tx_mcs_80);
1737f7aa94bSWen Gong 
1747f7aa94bSWen Gong 	own_160 = own_he_cap.he_cap_elem.phy_cap_info[0] &
1757f7aa94bSWen Gong 		  IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
1767f7aa94bSWen Gong 	peer_160 = he_cap->he_cap_elem.phy_cap_info[0] &
1777f7aa94bSWen Gong 		   IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
1787f7aa94bSWen Gong 
1797f7aa94bSWen Gong 	if (peer_160 && own_160) {
1807f7aa94bSWen Gong 		ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_160,
1817f7aa94bSWen Gong 					      &he_cap->he_mcs_nss_supp.rx_mcs_160,
1827f7aa94bSWen Gong 					      &own_he_cap.he_mcs_nss_supp.tx_mcs_160,
1837f7aa94bSWen Gong 					      &he_cap->he_mcs_nss_supp.tx_mcs_160);
1847f7aa94bSWen Gong 	} else if (peer_160 && !own_160) {
1857f7aa94bSWen Gong 		ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.rx_mcs_160);
1867f7aa94bSWen Gong 		ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.tx_mcs_160);
1877f7aa94bSWen Gong 		he_cap->he_cap_elem.phy_cap_info[0] &=
1887f7aa94bSWen Gong 			~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
1897f7aa94bSWen Gong 	}
1907f7aa94bSWen Gong 
1917f7aa94bSWen Gong 	own_80p80 = own_he_cap.he_cap_elem.phy_cap_info[0] &
1927f7aa94bSWen Gong 		    IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
1937f7aa94bSWen Gong 	peer_80p80 = he_cap->he_cap_elem.phy_cap_info[0] &
1947f7aa94bSWen Gong 		     IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
1957f7aa94bSWen Gong 
1967f7aa94bSWen Gong 	if (peer_80p80 && own_80p80) {
1977f7aa94bSWen Gong 		ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_80p80,
1987f7aa94bSWen Gong 					      &he_cap->he_mcs_nss_supp.rx_mcs_80p80,
1997f7aa94bSWen Gong 					      &own_he_cap.he_mcs_nss_supp.tx_mcs_80p80,
2007f7aa94bSWen Gong 					      &he_cap->he_mcs_nss_supp.tx_mcs_80p80);
2017f7aa94bSWen Gong 	} else if (peer_80p80 && !own_80p80) {
2027f7aa94bSWen Gong 		ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.rx_mcs_80p80);
2037f7aa94bSWen Gong 		ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.tx_mcs_80p80);
2047f7aa94bSWen Gong 		he_cap->he_cap_elem.phy_cap_info[0] &=
2057f7aa94bSWen Gong 			~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
2067f7aa94bSWen Gong 	}
20741cbb0f5SLuca Coelho }
208697f6c50SJohn Crispin 
209697f6c50SJohn Crispin void
ieee80211_he_op_ie_to_bss_conf(struct ieee80211_vif * vif,const struct ieee80211_he_operation * he_op_ie)210697f6c50SJohn Crispin ieee80211_he_op_ie_to_bss_conf(struct ieee80211_vif *vif,
21160689de4SRajkumar Manoharan 			const struct ieee80211_he_operation *he_op_ie)
212697f6c50SJohn Crispin {
21360689de4SRajkumar Manoharan 	memset(&vif->bss_conf.he_oper, 0, sizeof(vif->bss_conf.he_oper));
21460689de4SRajkumar Manoharan 	if (!he_op_ie)
215697f6c50SJohn Crispin 		return;
216697f6c50SJohn Crispin 
21760689de4SRajkumar Manoharan 	vif->bss_conf.he_oper.params = __le32_to_cpu(he_op_ie->he_oper_params);
21860689de4SRajkumar Manoharan 	vif->bss_conf.he_oper.nss_set = __le16_to_cpu(he_op_ie->he_mcs_nss_set);
219697f6c50SJohn Crispin }
2201ced169cSJohn Crispin 
2211ced169cSJohn Crispin void
ieee80211_he_spr_ie_to_bss_conf(struct ieee80211_vif * vif,const struct ieee80211_he_spr * he_spr_ie_elem)2221ced169cSJohn Crispin ieee80211_he_spr_ie_to_bss_conf(struct ieee80211_vif *vif,
2231ced169cSJohn Crispin 				const struct ieee80211_he_spr *he_spr_ie_elem)
2241ced169cSJohn Crispin {
2251ced169cSJohn Crispin 	struct ieee80211_he_obss_pd *he_obss_pd =
2261ced169cSJohn Crispin 					&vif->bss_conf.he_obss_pd;
2275db16ba8SJohn Crispin 	const u8 *data;
2281ced169cSJohn Crispin 
2291ced169cSJohn Crispin 	memset(he_obss_pd, 0, sizeof(*he_obss_pd));
2301ced169cSJohn Crispin 
2311ced169cSJohn Crispin 	if (!he_spr_ie_elem)
2321ced169cSJohn Crispin 		return;
233*2f467fefSLingbo Kong 
234*2f467fefSLingbo Kong 	he_obss_pd->sr_ctrl = he_spr_ie_elem->he_sr_control;
2355db16ba8SJohn Crispin 	data = he_spr_ie_elem->optional;
2361ced169cSJohn Crispin 
2371ced169cSJohn Crispin 	if (he_spr_ie_elem->he_sr_control &
2381ced169cSJohn Crispin 	    IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT)
239*2f467fefSLingbo Kong 		he_obss_pd->non_srg_max_offset = *data++;
240*2f467fefSLingbo Kong 
2411ced169cSJohn Crispin 	if (he_spr_ie_elem->he_sr_control &
2421ced169cSJohn Crispin 	    IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT) {
2431ced169cSJohn Crispin 		he_obss_pd->min_offset = *data++;
244*2f467fefSLingbo Kong 		he_obss_pd->max_offset = *data++;
245*2f467fefSLingbo Kong 		memcpy(he_obss_pd->bss_color_bitmap, data, 8);
246*2f467fefSLingbo Kong 		data += 8;
247*2f467fefSLingbo Kong 		memcpy(he_obss_pd->partial_bssid_bitmap, data, 8);
2481ced169cSJohn Crispin 		he_obss_pd->enable = true;
2491ced169cSJohn Crispin 	}
2501ced169cSJohn Crispin }
251