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