1*6b627f88SBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2*6b627f88SBjoern A. Zeeb /*
3*6b627f88SBjoern A. Zeeb * Copyright (C) 2024-2025 Intel Corporation
4*6b627f88SBjoern A. Zeeb */
5*6b627f88SBjoern A. Zeeb
6*6b627f88SBjoern A. Zeeb #include <net/mac80211.h>
7*6b627f88SBjoern A. Zeeb
8*6b627f88SBjoern A. Zeeb #include "tlc.h"
9*6b627f88SBjoern A. Zeeb #include "hcmd.h"
10*6b627f88SBjoern A. Zeeb #include "sta.h"
11*6b627f88SBjoern A. Zeeb
12*6b627f88SBjoern A. Zeeb #include "fw/api/rs.h"
13*6b627f88SBjoern A. Zeeb #include "fw/api/context.h"
14*6b627f88SBjoern A. Zeeb #include "fw/api/dhc.h"
15*6b627f88SBjoern A. Zeeb
iwl_mld_fw_bw_from_sta_bw(const struct ieee80211_link_sta * link_sta)16*6b627f88SBjoern A. Zeeb static u8 iwl_mld_fw_bw_from_sta_bw(const struct ieee80211_link_sta *link_sta)
17*6b627f88SBjoern A. Zeeb {
18*6b627f88SBjoern A. Zeeb switch (link_sta->bandwidth) {
19*6b627f88SBjoern A. Zeeb case IEEE80211_STA_RX_BW_320:
20*6b627f88SBjoern A. Zeeb return IWL_TLC_MNG_CH_WIDTH_320MHZ;
21*6b627f88SBjoern A. Zeeb case IEEE80211_STA_RX_BW_160:
22*6b627f88SBjoern A. Zeeb return IWL_TLC_MNG_CH_WIDTH_160MHZ;
23*6b627f88SBjoern A. Zeeb case IEEE80211_STA_RX_BW_80:
24*6b627f88SBjoern A. Zeeb return IWL_TLC_MNG_CH_WIDTH_80MHZ;
25*6b627f88SBjoern A. Zeeb case IEEE80211_STA_RX_BW_40:
26*6b627f88SBjoern A. Zeeb return IWL_TLC_MNG_CH_WIDTH_40MHZ;
27*6b627f88SBjoern A. Zeeb case IEEE80211_STA_RX_BW_20:
28*6b627f88SBjoern A. Zeeb default:
29*6b627f88SBjoern A. Zeeb return IWL_TLC_MNG_CH_WIDTH_20MHZ;
30*6b627f88SBjoern A. Zeeb }
31*6b627f88SBjoern A. Zeeb }
32*6b627f88SBjoern A. Zeeb
33*6b627f88SBjoern A. Zeeb static __le16
iwl_mld_get_tlc_cmd_flags(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_link_sta * link_sta,const struct ieee80211_sta_he_cap * own_he_cap,const struct ieee80211_sta_eht_cap * own_eht_cap)34*6b627f88SBjoern A. Zeeb iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld,
35*6b627f88SBjoern A. Zeeb struct ieee80211_vif *vif,
36*6b627f88SBjoern A. Zeeb struct ieee80211_link_sta *link_sta,
37*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_he_cap *own_he_cap,
38*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_eht_cap *own_eht_cap)
39*6b627f88SBjoern A. Zeeb {
40*6b627f88SBjoern A. Zeeb struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
41*6b627f88SBjoern A. Zeeb struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
42*6b627f88SBjoern A. Zeeb struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
43*6b627f88SBjoern A. Zeeb bool has_vht = vht_cap->vht_supported;
44*6b627f88SBjoern A. Zeeb u16 flags = 0;
45*6b627f88SBjoern A. Zeeb
46*6b627f88SBjoern A. Zeeb /* STBC flags */
47*6b627f88SBjoern A. Zeeb if (mld->cfg->ht_params.stbc &&
48*6b627f88SBjoern A. Zeeb (hweight8(iwl_mld_get_valid_tx_ant(mld)) > 1)) {
49*6b627f88SBjoern A. Zeeb if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] &
50*6b627f88SBjoern A. Zeeb IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ)
51*6b627f88SBjoern A. Zeeb flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
52*6b627f88SBjoern A. Zeeb else if (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)
53*6b627f88SBjoern A. Zeeb flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
54*6b627f88SBjoern A. Zeeb else if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)
55*6b627f88SBjoern A. Zeeb flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
56*6b627f88SBjoern A. Zeeb }
57*6b627f88SBjoern A. Zeeb
58*6b627f88SBjoern A. Zeeb /* LDPC */
59*6b627f88SBjoern A. Zeeb if (mld->cfg->ht_params.ldpc &&
60*6b627f88SBjoern A. Zeeb ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) ||
61*6b627f88SBjoern A. Zeeb (has_vht && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))))
62*6b627f88SBjoern A. Zeeb flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
63*6b627f88SBjoern A. Zeeb
64*6b627f88SBjoern A. Zeeb if (he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[1] &
65*6b627f88SBjoern A. Zeeb IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
66*6b627f88SBjoern A. Zeeb flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
67*6b627f88SBjoern A. Zeeb
68*6b627f88SBjoern A. Zeeb if (own_he_cap &&
69*6b627f88SBjoern A. Zeeb !(own_he_cap->he_cap_elem.phy_cap_info[1] &
70*6b627f88SBjoern A. Zeeb IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
71*6b627f88SBjoern A. Zeeb flags &= ~IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
72*6b627f88SBjoern A. Zeeb
73*6b627f88SBjoern A. Zeeb /* DCM */
74*6b627f88SBjoern A. Zeeb if (he_cap->has_he &&
75*6b627f88SBjoern A. Zeeb (he_cap->he_cap_elem.phy_cap_info[3] &
76*6b627f88SBjoern A. Zeeb IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK &&
77*6b627f88SBjoern A. Zeeb own_he_cap &&
78*6b627f88SBjoern A. Zeeb own_he_cap->he_cap_elem.phy_cap_info[3] &
79*6b627f88SBjoern A. Zeeb IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK))
80*6b627f88SBjoern A. Zeeb flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK;
81*6b627f88SBjoern A. Zeeb
82*6b627f88SBjoern A. Zeeb /* Extra EHT LTF */
83*6b627f88SBjoern A. Zeeb if (own_eht_cap &&
84*6b627f88SBjoern A. Zeeb own_eht_cap->eht_cap_elem.phy_cap_info[5] &
85*6b627f88SBjoern A. Zeeb IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF &&
86*6b627f88SBjoern A. Zeeb link_sta->eht_cap.has_eht &&
87*6b627f88SBjoern A. Zeeb link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] &
88*6b627f88SBjoern A. Zeeb IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF) {
89*6b627f88SBjoern A. Zeeb flags |= IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK;
90*6b627f88SBjoern A. Zeeb }
91*6b627f88SBjoern A. Zeeb
92*6b627f88SBjoern A. Zeeb return cpu_to_le16(flags);
93*6b627f88SBjoern A. Zeeb }
94*6b627f88SBjoern A. Zeeb
iwl_mld_get_fw_chains(struct iwl_mld * mld)95*6b627f88SBjoern A. Zeeb static u8 iwl_mld_get_fw_chains(struct iwl_mld *mld)
96*6b627f88SBjoern A. Zeeb {
97*6b627f88SBjoern A. Zeeb u8 chains = iwl_mld_get_valid_tx_ant(mld);
98*6b627f88SBjoern A. Zeeb u8 fw_chains = 0;
99*6b627f88SBjoern A. Zeeb
100*6b627f88SBjoern A. Zeeb if (chains & ANT_A)
101*6b627f88SBjoern A. Zeeb fw_chains |= IWL_TLC_MNG_CHAIN_A_MSK;
102*6b627f88SBjoern A. Zeeb if (chains & ANT_B)
103*6b627f88SBjoern A. Zeeb fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK;
104*6b627f88SBjoern A. Zeeb
105*6b627f88SBjoern A. Zeeb return fw_chains;
106*6b627f88SBjoern A. Zeeb }
107*6b627f88SBjoern A. Zeeb
iwl_mld_get_fw_sgi(struct ieee80211_link_sta * link_sta)108*6b627f88SBjoern A. Zeeb static u8 iwl_mld_get_fw_sgi(struct ieee80211_link_sta *link_sta)
109*6b627f88SBjoern A. Zeeb {
110*6b627f88SBjoern A. Zeeb struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
111*6b627f88SBjoern A. Zeeb struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
112*6b627f88SBjoern A. Zeeb struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
113*6b627f88SBjoern A. Zeeb u8 sgi_chwidths = 0;
114*6b627f88SBjoern A. Zeeb
115*6b627f88SBjoern A. Zeeb /* If the association supports HE, HT/VHT rates will never be used for
116*6b627f88SBjoern A. Zeeb * Tx and therefor there's no need to set the
117*6b627f88SBjoern A. Zeeb * sgi-per-channel-width-support bits
118*6b627f88SBjoern A. Zeeb */
119*6b627f88SBjoern A. Zeeb if (he_cap->has_he)
120*6b627f88SBjoern A. Zeeb return 0;
121*6b627f88SBjoern A. Zeeb
122*6b627f88SBjoern A. Zeeb if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
123*6b627f88SBjoern A. Zeeb sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ);
124*6b627f88SBjoern A. Zeeb if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
125*6b627f88SBjoern A. Zeeb sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_40MHZ);
126*6b627f88SBjoern A. Zeeb if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80)
127*6b627f88SBjoern A. Zeeb sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_80MHZ);
128*6b627f88SBjoern A. Zeeb if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160)
129*6b627f88SBjoern A. Zeeb sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_160MHZ);
130*6b627f88SBjoern A. Zeeb
131*6b627f88SBjoern A. Zeeb return sgi_chwidths;
132*6b627f88SBjoern A. Zeeb }
133*6b627f88SBjoern A. Zeeb
134*6b627f88SBjoern A. Zeeb static int
iwl_mld_get_highest_fw_mcs(const struct ieee80211_sta_vht_cap * vht_cap,int nss)135*6b627f88SBjoern A. Zeeb iwl_mld_get_highest_fw_mcs(const struct ieee80211_sta_vht_cap *vht_cap,
136*6b627f88SBjoern A. Zeeb int nss)
137*6b627f88SBjoern A. Zeeb {
138*6b627f88SBjoern A. Zeeb u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) &
139*6b627f88SBjoern A. Zeeb (0x3 << (2 * (nss - 1)));
140*6b627f88SBjoern A. Zeeb rx_mcs >>= (2 * (nss - 1));
141*6b627f88SBjoern A. Zeeb
142*6b627f88SBjoern A. Zeeb switch (rx_mcs) {
143*6b627f88SBjoern A. Zeeb case IEEE80211_VHT_MCS_SUPPORT_0_7:
144*6b627f88SBjoern A. Zeeb return IWL_TLC_MNG_HT_RATE_MCS7;
145*6b627f88SBjoern A. Zeeb case IEEE80211_VHT_MCS_SUPPORT_0_8:
146*6b627f88SBjoern A. Zeeb return IWL_TLC_MNG_HT_RATE_MCS8;
147*6b627f88SBjoern A. Zeeb case IEEE80211_VHT_MCS_SUPPORT_0_9:
148*6b627f88SBjoern A. Zeeb return IWL_TLC_MNG_HT_RATE_MCS9;
149*6b627f88SBjoern A. Zeeb default:
150*6b627f88SBjoern A. Zeeb WARN_ON_ONCE(1);
151*6b627f88SBjoern A. Zeeb break;
152*6b627f88SBjoern A. Zeeb }
153*6b627f88SBjoern A. Zeeb
154*6b627f88SBjoern A. Zeeb return 0;
155*6b627f88SBjoern A. Zeeb }
156*6b627f88SBjoern A. Zeeb
157*6b627f88SBjoern A. Zeeb static void
iwl_mld_fill_vht_rates(const struct ieee80211_link_sta * link_sta,const struct ieee80211_sta_vht_cap * vht_cap,struct iwl_tlc_config_cmd_v4 * cmd)158*6b627f88SBjoern A. Zeeb iwl_mld_fill_vht_rates(const struct ieee80211_link_sta *link_sta,
159*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_vht_cap *vht_cap,
160*6b627f88SBjoern A. Zeeb struct iwl_tlc_config_cmd_v4 *cmd)
161*6b627f88SBjoern A. Zeeb {
162*6b627f88SBjoern A. Zeeb u16 supp;
163*6b627f88SBjoern A. Zeeb int i, highest_mcs;
164*6b627f88SBjoern A. Zeeb u8 max_nss = link_sta->rx_nss;
165*6b627f88SBjoern A. Zeeb struct ieee80211_vht_cap ieee_vht_cap = {
166*6b627f88SBjoern A. Zeeb .vht_cap_info = cpu_to_le32(vht_cap->cap),
167*6b627f88SBjoern A. Zeeb .supp_mcs = vht_cap->vht_mcs,
168*6b627f88SBjoern A. Zeeb };
169*6b627f88SBjoern A. Zeeb
170*6b627f88SBjoern A. Zeeb /* the station support only a single receive chain */
171*6b627f88SBjoern A. Zeeb if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)
172*6b627f88SBjoern A. Zeeb max_nss = 1;
173*6b627f88SBjoern A. Zeeb
174*6b627f88SBjoern A. Zeeb for (i = 0; i < max_nss && i < IWL_TLC_NSS_MAX; i++) {
175*6b627f88SBjoern A. Zeeb int nss = i + 1;
176*6b627f88SBjoern A. Zeeb
177*6b627f88SBjoern A. Zeeb highest_mcs = iwl_mld_get_highest_fw_mcs(vht_cap, nss);
178*6b627f88SBjoern A. Zeeb if (!highest_mcs)
179*6b627f88SBjoern A. Zeeb continue;
180*6b627f88SBjoern A. Zeeb
181*6b627f88SBjoern A. Zeeb supp = BIT(highest_mcs + 1) - 1;
182*6b627f88SBjoern A. Zeeb if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20)
183*6b627f88SBjoern A. Zeeb supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9);
184*6b627f88SBjoern A. Zeeb
185*6b627f88SBjoern A. Zeeb cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = cpu_to_le16(supp);
186*6b627f88SBjoern A. Zeeb /* Check if VHT extended NSS indicates that the bandwidth/NSS
187*6b627f88SBjoern A. Zeeb * configuration is supported - only for MCS 0 since we already
188*6b627f88SBjoern A. Zeeb * decoded the MCS bits anyway ourselves.
189*6b627f88SBjoern A. Zeeb */
190*6b627f88SBjoern A. Zeeb if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160 &&
191*6b627f88SBjoern A. Zeeb ieee80211_get_vht_max_nss(&ieee_vht_cap,
192*6b627f88SBjoern A. Zeeb IEEE80211_VHT_CHANWIDTH_160MHZ,
193*6b627f88SBjoern A. Zeeb 0, true, nss) >= nss)
194*6b627f88SBjoern A. Zeeb cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] =
195*6b627f88SBjoern A. Zeeb cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80];
196*6b627f88SBjoern A. Zeeb }
197*6b627f88SBjoern A. Zeeb }
198*6b627f88SBjoern A. Zeeb
iwl_mld_he_mac80211_mcs_to_fw_mcs(u16 mcs)199*6b627f88SBjoern A. Zeeb static u16 iwl_mld_he_mac80211_mcs_to_fw_mcs(u16 mcs)
200*6b627f88SBjoern A. Zeeb {
201*6b627f88SBjoern A. Zeeb switch (mcs) {
202*6b627f88SBjoern A. Zeeb case IEEE80211_HE_MCS_SUPPORT_0_7:
203*6b627f88SBjoern A. Zeeb return BIT(IWL_TLC_MNG_HT_RATE_MCS7 + 1) - 1;
204*6b627f88SBjoern A. Zeeb case IEEE80211_HE_MCS_SUPPORT_0_9:
205*6b627f88SBjoern A. Zeeb return BIT(IWL_TLC_MNG_HT_RATE_MCS9 + 1) - 1;
206*6b627f88SBjoern A. Zeeb case IEEE80211_HE_MCS_SUPPORT_0_11:
207*6b627f88SBjoern A. Zeeb return BIT(IWL_TLC_MNG_HT_RATE_MCS11 + 1) - 1;
208*6b627f88SBjoern A. Zeeb case IEEE80211_HE_MCS_NOT_SUPPORTED:
209*6b627f88SBjoern A. Zeeb return 0;
210*6b627f88SBjoern A. Zeeb }
211*6b627f88SBjoern A. Zeeb
212*6b627f88SBjoern A. Zeeb WARN(1, "invalid HE MCS %d\n", mcs);
213*6b627f88SBjoern A. Zeeb return 0;
214*6b627f88SBjoern A. Zeeb }
215*6b627f88SBjoern A. Zeeb
216*6b627f88SBjoern A. Zeeb static void
iwl_mld_fill_he_rates(const struct ieee80211_link_sta * link_sta,const struct ieee80211_sta_he_cap * own_he_cap,struct iwl_tlc_config_cmd_v4 * cmd)217*6b627f88SBjoern A. Zeeb iwl_mld_fill_he_rates(const struct ieee80211_link_sta *link_sta,
218*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_he_cap *own_he_cap,
219*6b627f88SBjoern A. Zeeb struct iwl_tlc_config_cmd_v4 *cmd)
220*6b627f88SBjoern A. Zeeb {
221*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
222*6b627f88SBjoern A. Zeeb u16 mcs_160 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
223*6b627f88SBjoern A. Zeeb u16 mcs_80 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
224*6b627f88SBjoern A. Zeeb u16 tx_mcs_80 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_80);
225*6b627f88SBjoern A. Zeeb u16 tx_mcs_160 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_160);
226*6b627f88SBjoern A. Zeeb int i;
227*6b627f88SBjoern A. Zeeb u8 nss = link_sta->rx_nss;
228*6b627f88SBjoern A. Zeeb
229*6b627f88SBjoern A. Zeeb /* the station support only a single receive chain */
230*6b627f88SBjoern A. Zeeb if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)
231*6b627f88SBjoern A. Zeeb nss = 1;
232*6b627f88SBjoern A. Zeeb
233*6b627f88SBjoern A. Zeeb for (i = 0; i < nss && i < IWL_TLC_NSS_MAX; i++) {
234*6b627f88SBjoern A. Zeeb u16 _mcs_160 = (mcs_160 >> (2 * i)) & 0x3;
235*6b627f88SBjoern A. Zeeb u16 _mcs_80 = (mcs_80 >> (2 * i)) & 0x3;
236*6b627f88SBjoern A. Zeeb u16 _tx_mcs_160 = (tx_mcs_160 >> (2 * i)) & 0x3;
237*6b627f88SBjoern A. Zeeb u16 _tx_mcs_80 = (tx_mcs_80 >> (2 * i)) & 0x3;
238*6b627f88SBjoern A. Zeeb
239*6b627f88SBjoern A. Zeeb /* If one side doesn't support - mark both as not supporting */
240*6b627f88SBjoern A. Zeeb if (_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED ||
241*6b627f88SBjoern A. Zeeb _tx_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED) {
242*6b627f88SBjoern A. Zeeb _mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED;
243*6b627f88SBjoern A. Zeeb _tx_mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED;
244*6b627f88SBjoern A. Zeeb }
245*6b627f88SBjoern A. Zeeb if (_mcs_80 > _tx_mcs_80)
246*6b627f88SBjoern A. Zeeb _mcs_80 = _tx_mcs_80;
247*6b627f88SBjoern A. Zeeb cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] =
248*6b627f88SBjoern A. Zeeb cpu_to_le16(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_80));
249*6b627f88SBjoern A. Zeeb
250*6b627f88SBjoern A. Zeeb /* If one side doesn't support - mark both as not supporting */
251*6b627f88SBjoern A. Zeeb if (_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED ||
252*6b627f88SBjoern A. Zeeb _tx_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED) {
253*6b627f88SBjoern A. Zeeb _mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED;
254*6b627f88SBjoern A. Zeeb _tx_mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED;
255*6b627f88SBjoern A. Zeeb }
256*6b627f88SBjoern A. Zeeb if (_mcs_160 > _tx_mcs_160)
257*6b627f88SBjoern A. Zeeb _mcs_160 = _tx_mcs_160;
258*6b627f88SBjoern A. Zeeb cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] =
259*6b627f88SBjoern A. Zeeb cpu_to_le16(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_160));
260*6b627f88SBjoern A. Zeeb }
261*6b627f88SBjoern A. Zeeb }
262*6b627f88SBjoern A. Zeeb
iwl_mld_set_eht_mcs(__le16 ht_rates[][3],enum IWL_TLC_MCS_PER_BW bw,u8 max_nss,u16 mcs_msk)263*6b627f88SBjoern A. Zeeb static void iwl_mld_set_eht_mcs(__le16 ht_rates[][3],
264*6b627f88SBjoern A. Zeeb enum IWL_TLC_MCS_PER_BW bw,
265*6b627f88SBjoern A. Zeeb u8 max_nss, u16 mcs_msk)
266*6b627f88SBjoern A. Zeeb {
267*6b627f88SBjoern A. Zeeb if (max_nss >= 2)
268*6b627f88SBjoern A. Zeeb ht_rates[IWL_TLC_NSS_2][bw] |= cpu_to_le16(mcs_msk);
269*6b627f88SBjoern A. Zeeb
270*6b627f88SBjoern A. Zeeb if (max_nss >= 1)
271*6b627f88SBjoern A. Zeeb ht_rates[IWL_TLC_NSS_1][bw] |= cpu_to_le16(mcs_msk);
272*6b627f88SBjoern A. Zeeb }
273*6b627f88SBjoern A. Zeeb
274*6b627f88SBjoern A. Zeeb static const
275*6b627f88SBjoern A. Zeeb struct ieee80211_eht_mcs_nss_supp_bw *
iwl_mld_get_eht_mcs_of_bw(enum IWL_TLC_MCS_PER_BW bw,const struct ieee80211_eht_mcs_nss_supp * eht_mcs)276*6b627f88SBjoern A. Zeeb iwl_mld_get_eht_mcs_of_bw(enum IWL_TLC_MCS_PER_BW bw,
277*6b627f88SBjoern A. Zeeb const struct ieee80211_eht_mcs_nss_supp *eht_mcs)
278*6b627f88SBjoern A. Zeeb {
279*6b627f88SBjoern A. Zeeb switch (bw) {
280*6b627f88SBjoern A. Zeeb case IWL_TLC_MCS_PER_BW_80:
281*6b627f88SBjoern A. Zeeb return &eht_mcs->bw._80;
282*6b627f88SBjoern A. Zeeb case IWL_TLC_MCS_PER_BW_160:
283*6b627f88SBjoern A. Zeeb return &eht_mcs->bw._160;
284*6b627f88SBjoern A. Zeeb case IWL_TLC_MCS_PER_BW_320:
285*6b627f88SBjoern A. Zeeb return &eht_mcs->bw._320;
286*6b627f88SBjoern A. Zeeb default:
287*6b627f88SBjoern A. Zeeb return NULL;
288*6b627f88SBjoern A. Zeeb }
289*6b627f88SBjoern A. Zeeb }
290*6b627f88SBjoern A. Zeeb
iwl_mld_get_eht_max_nss(u8 rx_nss,u8 tx_nss)291*6b627f88SBjoern A. Zeeb static u8 iwl_mld_get_eht_max_nss(u8 rx_nss, u8 tx_nss)
292*6b627f88SBjoern A. Zeeb {
293*6b627f88SBjoern A. Zeeb u8 tx = u8_get_bits(tx_nss, IEEE80211_EHT_MCS_NSS_TX);
294*6b627f88SBjoern A. Zeeb u8 rx = u8_get_bits(rx_nss, IEEE80211_EHT_MCS_NSS_RX);
295*6b627f88SBjoern A. Zeeb /* the max nss that can be used,
296*6b627f88SBjoern A. Zeeb * is the min with our tx capa and the peer rx capa.
297*6b627f88SBjoern A. Zeeb */
298*6b627f88SBjoern A. Zeeb return min(tx, rx);
299*6b627f88SBjoern A. Zeeb }
300*6b627f88SBjoern A. Zeeb
301*6b627f88SBjoern A. Zeeb #define MAX_NSS_MCS(mcs_num, rx, tx) \
302*6b627f88SBjoern A. Zeeb iwl_mld_get_eht_max_nss((rx)->rx_tx_mcs ##mcs_num## _max_nss, \
303*6b627f88SBjoern A. Zeeb (tx)->rx_tx_mcs ##mcs_num## _max_nss)
304*6b627f88SBjoern A. Zeeb
305*6b627f88SBjoern A. Zeeb static void
iwl_mld_fill_eht_rates(struct ieee80211_vif * vif,const struct ieee80211_link_sta * link_sta,const struct ieee80211_sta_he_cap * own_he_cap,const struct ieee80211_sta_eht_cap * own_eht_cap,struct iwl_tlc_config_cmd_v4 * cmd)306*6b627f88SBjoern A. Zeeb iwl_mld_fill_eht_rates(struct ieee80211_vif *vif,
307*6b627f88SBjoern A. Zeeb const struct ieee80211_link_sta *link_sta,
308*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_he_cap *own_he_cap,
309*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_eht_cap *own_eht_cap,
310*6b627f88SBjoern A. Zeeb struct iwl_tlc_config_cmd_v4 *cmd)
311*6b627f88SBjoern A. Zeeb {
312*6b627f88SBjoern A. Zeeb /* peer RX mcs capa */
313*6b627f88SBjoern A. Zeeb const struct ieee80211_eht_mcs_nss_supp *eht_rx_mcs =
314*6b627f88SBjoern A. Zeeb &link_sta->eht_cap.eht_mcs_nss_supp;
315*6b627f88SBjoern A. Zeeb /* our TX mcs capa */
316*6b627f88SBjoern A. Zeeb const struct ieee80211_eht_mcs_nss_supp *eht_tx_mcs =
317*6b627f88SBjoern A. Zeeb &own_eht_cap->eht_mcs_nss_supp;
318*6b627f88SBjoern A. Zeeb
319*6b627f88SBjoern A. Zeeb enum IWL_TLC_MCS_PER_BW bw;
320*6b627f88SBjoern A. Zeeb struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_rx_20;
321*6b627f88SBjoern A. Zeeb struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_tx_20;
322*6b627f88SBjoern A. Zeeb
323*6b627f88SBjoern A. Zeeb /* peer is 20 MHz only */
324*6b627f88SBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_AP &&
325*6b627f88SBjoern A. Zeeb !(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
326*6b627f88SBjoern A. Zeeb IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
327*6b627f88SBjoern A. Zeeb mcs_rx_20 = eht_rx_mcs->only_20mhz;
328*6b627f88SBjoern A. Zeeb } else {
329*6b627f88SBjoern A. Zeeb mcs_rx_20.rx_tx_mcs7_max_nss =
330*6b627f88SBjoern A. Zeeb eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss;
331*6b627f88SBjoern A. Zeeb mcs_rx_20.rx_tx_mcs9_max_nss =
332*6b627f88SBjoern A. Zeeb eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss;
333*6b627f88SBjoern A. Zeeb mcs_rx_20.rx_tx_mcs11_max_nss =
334*6b627f88SBjoern A. Zeeb eht_rx_mcs->bw._80.rx_tx_mcs11_max_nss;
335*6b627f88SBjoern A. Zeeb mcs_rx_20.rx_tx_mcs13_max_nss =
336*6b627f88SBjoern A. Zeeb eht_rx_mcs->bw._80.rx_tx_mcs13_max_nss;
337*6b627f88SBjoern A. Zeeb }
338*6b627f88SBjoern A. Zeeb
339*6b627f88SBjoern A. Zeeb /* NIC is capable of 20 MHz only */
340*6b627f88SBjoern A. Zeeb if (!(own_he_cap->he_cap_elem.phy_cap_info[0] &
341*6b627f88SBjoern A. Zeeb IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
342*6b627f88SBjoern A. Zeeb mcs_tx_20 = eht_tx_mcs->only_20mhz;
343*6b627f88SBjoern A. Zeeb } else {
344*6b627f88SBjoern A. Zeeb mcs_tx_20.rx_tx_mcs7_max_nss =
345*6b627f88SBjoern A. Zeeb eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss;
346*6b627f88SBjoern A. Zeeb mcs_tx_20.rx_tx_mcs9_max_nss =
347*6b627f88SBjoern A. Zeeb eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss;
348*6b627f88SBjoern A. Zeeb mcs_tx_20.rx_tx_mcs11_max_nss =
349*6b627f88SBjoern A. Zeeb eht_tx_mcs->bw._80.rx_tx_mcs11_max_nss;
350*6b627f88SBjoern A. Zeeb mcs_tx_20.rx_tx_mcs13_max_nss =
351*6b627f88SBjoern A. Zeeb eht_tx_mcs->bw._80.rx_tx_mcs13_max_nss;
352*6b627f88SBjoern A. Zeeb }
353*6b627f88SBjoern A. Zeeb
354*6b627f88SBjoern A. Zeeb /* rates for 20/40/80 MHz */
355*6b627f88SBjoern A. Zeeb bw = IWL_TLC_MCS_PER_BW_80;
356*6b627f88SBjoern A. Zeeb iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
357*6b627f88SBjoern A. Zeeb MAX_NSS_MCS(7, &mcs_rx_20, &mcs_tx_20),
358*6b627f88SBjoern A. Zeeb GENMASK(7, 0));
359*6b627f88SBjoern A. Zeeb iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
360*6b627f88SBjoern A. Zeeb MAX_NSS_MCS(9, &mcs_rx_20, &mcs_tx_20),
361*6b627f88SBjoern A. Zeeb GENMASK(9, 8));
362*6b627f88SBjoern A. Zeeb iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
363*6b627f88SBjoern A. Zeeb MAX_NSS_MCS(11, &mcs_rx_20, &mcs_tx_20),
364*6b627f88SBjoern A. Zeeb GENMASK(11, 10));
365*6b627f88SBjoern A. Zeeb iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
366*6b627f88SBjoern A. Zeeb MAX_NSS_MCS(13, &mcs_rx_20, &mcs_tx_20),
367*6b627f88SBjoern A. Zeeb GENMASK(13, 12));
368*6b627f88SBjoern A. Zeeb
369*6b627f88SBjoern A. Zeeb /* rates for 160/320 MHz */
370*6b627f88SBjoern A. Zeeb for (bw = IWL_TLC_MCS_PER_BW_160; bw <= IWL_TLC_MCS_PER_BW_320; bw++) {
371*6b627f88SBjoern A. Zeeb const struct ieee80211_eht_mcs_nss_supp_bw *mcs_rx =
372*6b627f88SBjoern A. Zeeb iwl_mld_get_eht_mcs_of_bw(bw, eht_rx_mcs);
373*6b627f88SBjoern A. Zeeb const struct ieee80211_eht_mcs_nss_supp_bw *mcs_tx =
374*6b627f88SBjoern A. Zeeb iwl_mld_get_eht_mcs_of_bw(bw, eht_tx_mcs);
375*6b627f88SBjoern A. Zeeb
376*6b627f88SBjoern A. Zeeb /* got unsupported index for bw */
377*6b627f88SBjoern A. Zeeb if (!mcs_rx || !mcs_tx)
378*6b627f88SBjoern A. Zeeb continue;
379*6b627f88SBjoern A. Zeeb
380*6b627f88SBjoern A. Zeeb /* break out if we don't support the bandwidth */
381*6b627f88SBjoern A. Zeeb if (cmd->max_ch_width < (bw + IWL_TLC_MNG_CH_WIDTH_80MHZ))
382*6b627f88SBjoern A. Zeeb break;
383*6b627f88SBjoern A. Zeeb
384*6b627f88SBjoern A. Zeeb iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
385*6b627f88SBjoern A. Zeeb MAX_NSS_MCS(9, mcs_rx, mcs_tx),
386*6b627f88SBjoern A. Zeeb GENMASK(9, 0));
387*6b627f88SBjoern A. Zeeb iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
388*6b627f88SBjoern A. Zeeb MAX_NSS_MCS(11, mcs_rx, mcs_tx),
389*6b627f88SBjoern A. Zeeb GENMASK(11, 10));
390*6b627f88SBjoern A. Zeeb iwl_mld_set_eht_mcs(cmd->ht_rates, bw,
391*6b627f88SBjoern A. Zeeb MAX_NSS_MCS(13, mcs_rx, mcs_tx),
392*6b627f88SBjoern A. Zeeb GENMASK(13, 12));
393*6b627f88SBjoern A. Zeeb }
394*6b627f88SBjoern A. Zeeb
395*6b627f88SBjoern A. Zeeb /* the station support only a single receive chain */
396*6b627f88SBjoern A. Zeeb if (link_sta->smps_mode == IEEE80211_SMPS_STATIC ||
397*6b627f88SBjoern A. Zeeb link_sta->rx_nss < 2)
398*6b627f88SBjoern A. Zeeb memset(cmd->ht_rates[IWL_TLC_NSS_2], 0,
399*6b627f88SBjoern A. Zeeb sizeof(cmd->ht_rates[IWL_TLC_NSS_2]));
400*6b627f88SBjoern A. Zeeb }
401*6b627f88SBjoern A. Zeeb
402*6b627f88SBjoern A. Zeeb static void
iwl_mld_fill_supp_rates(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_link_sta * link_sta,struct ieee80211_supported_band * sband,const struct ieee80211_sta_he_cap * own_he_cap,const struct ieee80211_sta_eht_cap * own_eht_cap,struct iwl_tlc_config_cmd_v4 * cmd)403*6b627f88SBjoern A. Zeeb iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif,
404*6b627f88SBjoern A. Zeeb struct ieee80211_link_sta *link_sta,
405*6b627f88SBjoern A. Zeeb struct ieee80211_supported_band *sband,
406*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_he_cap *own_he_cap,
407*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_eht_cap *own_eht_cap,
408*6b627f88SBjoern A. Zeeb struct iwl_tlc_config_cmd_v4 *cmd)
409*6b627f88SBjoern A. Zeeb {
410*6b627f88SBjoern A. Zeeb int i;
411*6b627f88SBjoern A. Zeeb u16 non_ht_rates = 0;
412*6b627f88SBjoern A. Zeeb unsigned long rates_bitmap;
413*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
414*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
415*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
416*6b627f88SBjoern A. Zeeb
417*6b627f88SBjoern A. Zeeb /* non HT rates */
418*6b627f88SBjoern A. Zeeb rates_bitmap = link_sta->supp_rates[sband->band];
419*6b627f88SBjoern A. Zeeb for_each_set_bit(i, &rates_bitmap, BITS_PER_LONG)
420*6b627f88SBjoern A. Zeeb non_ht_rates |= BIT(sband->bitrates[i].hw_value);
421*6b627f88SBjoern A. Zeeb
422*6b627f88SBjoern A. Zeeb cmd->non_ht_rates = cpu_to_le16(non_ht_rates);
423*6b627f88SBjoern A. Zeeb cmd->mode = IWL_TLC_MNG_MODE_NON_HT;
424*6b627f88SBjoern A. Zeeb
425*6b627f88SBjoern A. Zeeb if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) {
426*6b627f88SBjoern A. Zeeb cmd->mode = IWL_TLC_MNG_MODE_EHT;
427*6b627f88SBjoern A. Zeeb iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap,
428*6b627f88SBjoern A. Zeeb own_eht_cap, cmd);
429*6b627f88SBjoern A. Zeeb } else if (he_cap->has_he && own_he_cap) {
430*6b627f88SBjoern A. Zeeb cmd->mode = IWL_TLC_MNG_MODE_HE;
431*6b627f88SBjoern A. Zeeb iwl_mld_fill_he_rates(link_sta, own_he_cap, cmd);
432*6b627f88SBjoern A. Zeeb } else if (vht_cap->vht_supported) {
433*6b627f88SBjoern A. Zeeb cmd->mode = IWL_TLC_MNG_MODE_VHT;
434*6b627f88SBjoern A. Zeeb iwl_mld_fill_vht_rates(link_sta, vht_cap, cmd);
435*6b627f88SBjoern A. Zeeb } else if (ht_cap->ht_supported) {
436*6b627f88SBjoern A. Zeeb cmd->mode = IWL_TLC_MNG_MODE_HT;
437*6b627f88SBjoern A. Zeeb cmd->ht_rates[IWL_TLC_NSS_1][IWL_TLC_MCS_PER_BW_80] =
438*6b627f88SBjoern A. Zeeb cpu_to_le16(ht_cap->mcs.rx_mask[0]);
439*6b627f88SBjoern A. Zeeb
440*6b627f88SBjoern A. Zeeb /* the station support only a single receive chain */
441*6b627f88SBjoern A. Zeeb if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)
442*6b627f88SBjoern A. Zeeb cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] =
443*6b627f88SBjoern A. Zeeb 0;
444*6b627f88SBjoern A. Zeeb else
445*6b627f88SBjoern A. Zeeb cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] =
446*6b627f88SBjoern A. Zeeb cpu_to_le16(ht_cap->mcs.rx_mask[1]);
447*6b627f88SBjoern A. Zeeb }
448*6b627f88SBjoern A. Zeeb }
449*6b627f88SBjoern A. Zeeb
iwl_mld_send_tlc_cmd(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_link_sta * link_sta,enum nl80211_band band)450*6b627f88SBjoern A. Zeeb static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
451*6b627f88SBjoern A. Zeeb struct ieee80211_vif *vif,
452*6b627f88SBjoern A. Zeeb struct ieee80211_link_sta *link_sta,
453*6b627f88SBjoern A. Zeeb enum nl80211_band band)
454*6b627f88SBjoern A. Zeeb {
455*6b627f88SBjoern A. Zeeb struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
456*6b627f88SBjoern A. Zeeb struct ieee80211_supported_band *sband = mld->hw->wiphy->bands[band];
457*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_he_cap *own_he_cap =
458*6b627f88SBjoern A. Zeeb ieee80211_get_he_iftype_cap_vif(sband, vif);
459*6b627f88SBjoern A. Zeeb const struct ieee80211_sta_eht_cap *own_eht_cap =
460*6b627f88SBjoern A. Zeeb ieee80211_get_eht_iftype_cap_vif(sband, vif);
461*6b627f88SBjoern A. Zeeb struct iwl_tlc_config_cmd_v4 cmd = {
462*6b627f88SBjoern A. Zeeb /* For AP mode, use 20 MHz until the STA is authorized */
463*6b627f88SBjoern A. Zeeb .max_ch_width = mld_sta->sta_state > IEEE80211_STA_ASSOC ?
464*6b627f88SBjoern A. Zeeb iwl_mld_fw_bw_from_sta_bw(link_sta) :
465*6b627f88SBjoern A. Zeeb IWL_TLC_MNG_CH_WIDTH_20MHZ,
466*6b627f88SBjoern A. Zeeb .flags = iwl_mld_get_tlc_cmd_flags(mld, vif, link_sta,
467*6b627f88SBjoern A. Zeeb own_he_cap, own_eht_cap),
468*6b627f88SBjoern A. Zeeb .chains = iwl_mld_get_fw_chains(mld),
469*6b627f88SBjoern A. Zeeb .sgi_ch_width_supp = iwl_mld_get_fw_sgi(link_sta),
470*6b627f88SBjoern A. Zeeb .max_mpdu_len = cpu_to_le16(link_sta->agg.max_amsdu_len),
471*6b627f88SBjoern A. Zeeb };
472*6b627f88SBjoern A. Zeeb int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
473*6b627f88SBjoern A. Zeeb int ret;
474*6b627f88SBjoern A. Zeeb
475*6b627f88SBjoern A. Zeeb if (fw_sta_id < 0)
476*6b627f88SBjoern A. Zeeb return;
477*6b627f88SBjoern A. Zeeb
478*6b627f88SBjoern A. Zeeb cmd.sta_id = fw_sta_id;
479*6b627f88SBjoern A. Zeeb
480*6b627f88SBjoern A. Zeeb iwl_mld_fill_supp_rates(mld, vif, link_sta, sband,
481*6b627f88SBjoern A. Zeeb own_he_cap, own_eht_cap,
482*6b627f88SBjoern A. Zeeb &cmd);
483*6b627f88SBjoern A. Zeeb
484*6b627f88SBjoern A. Zeeb IWL_DEBUG_RATE(mld,
485*6b627f88SBjoern A. Zeeb "TLC CONFIG CMD, sta_id=%d, max_ch_width=%d, mode=%d\n",
486*6b627f88SBjoern A. Zeeb cmd.sta_id, cmd.max_ch_width, cmd.mode);
487*6b627f88SBjoern A. Zeeb
488*6b627f88SBjoern A. Zeeb /* Send async since this can be called within a RCU-read section */
489*6b627f88SBjoern A. Zeeb ret = iwl_mld_send_cmd_with_flags_pdu(mld, WIDE_ID(DATA_PATH_GROUP,
490*6b627f88SBjoern A. Zeeb TLC_MNG_CONFIG_CMD),
491*6b627f88SBjoern A. Zeeb CMD_ASYNC, &cmd);
492*6b627f88SBjoern A. Zeeb if (ret)
493*6b627f88SBjoern A. Zeeb IWL_ERR(mld, "Failed to send TLC cmd (%d)\n", ret);
494*6b627f88SBjoern A. Zeeb }
495*6b627f88SBjoern A. Zeeb
iwl_mld_send_tlc_dhc(struct iwl_mld * mld,u8 sta_id,u32 type,u32 data)496*6b627f88SBjoern A. Zeeb int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data)
497*6b627f88SBjoern A. Zeeb {
498*6b627f88SBjoern A. Zeeb struct {
499*6b627f88SBjoern A. Zeeb struct iwl_dhc_cmd dhc;
500*6b627f88SBjoern A. Zeeb struct iwl_dhc_tlc_cmd tlc;
501*6b627f88SBjoern A. Zeeb } __packed cmd = {
502*6b627f88SBjoern A. Zeeb .tlc.sta_id = sta_id,
503*6b627f88SBjoern A. Zeeb .tlc.type = cpu_to_le32(type),
504*6b627f88SBjoern A. Zeeb .tlc.data[0] = cpu_to_le32(data),
505*6b627f88SBjoern A. Zeeb .dhc.length = cpu_to_le32(sizeof(cmd.tlc) >> 2),
506*6b627f88SBjoern A. Zeeb .dhc.index_and_mask =
507*6b627f88SBjoern A. Zeeb cpu_to_le32(DHC_TABLE_INTEGRATION | DHC_TARGET_UMAC |
508*6b627f88SBjoern A. Zeeb DHC_INTEGRATION_TLC_DEBUG_CONFIG),
509*6b627f88SBjoern A. Zeeb };
510*6b627f88SBjoern A. Zeeb int ret;
511*6b627f88SBjoern A. Zeeb
512*6b627f88SBjoern A. Zeeb ret = iwl_mld_send_cmd_with_flags_pdu(mld,
513*6b627f88SBjoern A. Zeeb WIDE_ID(IWL_ALWAYS_LONG_GROUP,
514*6b627f88SBjoern A. Zeeb DEBUG_HOST_COMMAND),
515*6b627f88SBjoern A. Zeeb CMD_ASYNC, &cmd);
516*6b627f88SBjoern A. Zeeb IWL_DEBUG_RATE(mld, "sta_id %d, type: 0x%X, value: 0x%X, ret%d\n",
517*6b627f88SBjoern A. Zeeb sta_id, type, data, ret);
518*6b627f88SBjoern A. Zeeb return ret;
519*6b627f88SBjoern A. Zeeb }
520*6b627f88SBjoern A. Zeeb
iwl_mld_config_tlc_link(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf,struct ieee80211_link_sta * link_sta)521*6b627f88SBjoern A. Zeeb void iwl_mld_config_tlc_link(struct iwl_mld *mld,
522*6b627f88SBjoern A. Zeeb struct ieee80211_vif *vif,
523*6b627f88SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf,
524*6b627f88SBjoern A. Zeeb struct ieee80211_link_sta *link_sta)
525*6b627f88SBjoern A. Zeeb {
526*6b627f88SBjoern A. Zeeb struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
527*6b627f88SBjoern A. Zeeb enum nl80211_band band;
528*6b627f88SBjoern A. Zeeb
529*6b627f88SBjoern A. Zeeb if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan))
530*6b627f88SBjoern A. Zeeb return;
531*6b627f88SBjoern A. Zeeb
532*6b627f88SBjoern A. Zeeb /* Before we have information about a station, configure the A-MSDU RC
533*6b627f88SBjoern A. Zeeb * limit such that iwlmd and mac80211 would not be allowed to build
534*6b627f88SBjoern A. Zeeb * A-MSDUs.
535*6b627f88SBjoern A. Zeeb */
536*6b627f88SBjoern A. Zeeb if (mld_sta->sta_state < IEEE80211_STA_ASSOC) {
537*6b627f88SBjoern A. Zeeb link_sta->agg.max_rc_amsdu_len = 1;
538*6b627f88SBjoern A. Zeeb ieee80211_sta_recalc_aggregates(link_sta->sta);
539*6b627f88SBjoern A. Zeeb }
540*6b627f88SBjoern A. Zeeb
541*6b627f88SBjoern A. Zeeb band = link_conf->chanreq.oper.chan->band;
542*6b627f88SBjoern A. Zeeb iwl_mld_send_tlc_cmd(mld, vif, link_sta, band);
543*6b627f88SBjoern A. Zeeb }
544*6b627f88SBjoern A. Zeeb
iwl_mld_config_tlc(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_sta * sta)545*6b627f88SBjoern A. Zeeb void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif,
546*6b627f88SBjoern A. Zeeb struct ieee80211_sta *sta)
547*6b627f88SBjoern A. Zeeb {
548*6b627f88SBjoern A. Zeeb struct ieee80211_bss_conf *link;
549*6b627f88SBjoern A. Zeeb int link_id;
550*6b627f88SBjoern A. Zeeb
551*6b627f88SBjoern A. Zeeb lockdep_assert_wiphy(mld->wiphy);
552*6b627f88SBjoern A. Zeeb
553*6b627f88SBjoern A. Zeeb for_each_vif_active_link(vif, link, link_id) {
554*6b627f88SBjoern A. Zeeb struct ieee80211_link_sta *link_sta =
555*6b627f88SBjoern A. Zeeb link_sta_dereference_check(sta, link_id);
556*6b627f88SBjoern A. Zeeb
557*6b627f88SBjoern A. Zeeb if (!link || !link_sta)
558*6b627f88SBjoern A. Zeeb continue;
559*6b627f88SBjoern A. Zeeb
560*6b627f88SBjoern A. Zeeb iwl_mld_config_tlc_link(mld, vif, link, link_sta);
561*6b627f88SBjoern A. Zeeb }
562*6b627f88SBjoern A. Zeeb }
563*6b627f88SBjoern A. Zeeb
564*6b627f88SBjoern A. Zeeb static u16
iwl_mld_get_amsdu_size_of_tid(struct iwl_mld * mld,struct ieee80211_link_sta * link_sta,unsigned int tid)565*6b627f88SBjoern A. Zeeb iwl_mld_get_amsdu_size_of_tid(struct iwl_mld *mld,
566*6b627f88SBjoern A. Zeeb struct ieee80211_link_sta *link_sta,
567*6b627f88SBjoern A. Zeeb unsigned int tid)
568*6b627f88SBjoern A. Zeeb {
569*6b627f88SBjoern A. Zeeb struct ieee80211_sta *sta = link_sta->sta;
570*6b627f88SBjoern A. Zeeb struct ieee80211_vif *vif = iwl_mld_sta_from_mac80211(sta)->vif;
571*6b627f88SBjoern A. Zeeb const u8 tid_to_mac80211_ac[] = {
572*6b627f88SBjoern A. Zeeb IEEE80211_AC_BE,
573*6b627f88SBjoern A. Zeeb IEEE80211_AC_BK,
574*6b627f88SBjoern A. Zeeb IEEE80211_AC_BK,
575*6b627f88SBjoern A. Zeeb IEEE80211_AC_BE,
576*6b627f88SBjoern A. Zeeb IEEE80211_AC_VI,
577*6b627f88SBjoern A. Zeeb IEEE80211_AC_VI,
578*6b627f88SBjoern A. Zeeb IEEE80211_AC_VO,
579*6b627f88SBjoern A. Zeeb IEEE80211_AC_VO,
580*6b627f88SBjoern A. Zeeb };
581*6b627f88SBjoern A. Zeeb unsigned int result = link_sta->agg.max_rc_amsdu_len;
582*6b627f88SBjoern A. Zeeb u8 ac, txf, lmac;
583*6b627f88SBjoern A. Zeeb
584*6b627f88SBjoern A. Zeeb lockdep_assert_wiphy(mld->wiphy);
585*6b627f88SBjoern A. Zeeb
586*6b627f88SBjoern A. Zeeb /* Don't send an AMSDU that will be longer than the TXF.
587*6b627f88SBjoern A. Zeeb * Add a security margin of 256 for the TX command + headers.
588*6b627f88SBjoern A. Zeeb * We also want to have the start of the next packet inside the
589*6b627f88SBjoern A. Zeeb * fifo to be able to send bursts.
590*6b627f88SBjoern A. Zeeb */
591*6b627f88SBjoern A. Zeeb
592*6b627f88SBjoern A. Zeeb if (WARN_ON(tid >= ARRAY_SIZE(tid_to_mac80211_ac)))
593*6b627f88SBjoern A. Zeeb return 0;
594*6b627f88SBjoern A. Zeeb
595*6b627f88SBjoern A. Zeeb ac = tid_to_mac80211_ac[tid];
596*6b627f88SBjoern A. Zeeb
597*6b627f88SBjoern A. Zeeb /* For HE redirect to trigger based fifos */
598*6b627f88SBjoern A. Zeeb if (link_sta->he_cap.has_he)
599*6b627f88SBjoern A. Zeeb ac += 4;
600*6b627f88SBjoern A. Zeeb
601*6b627f88SBjoern A. Zeeb txf = iwl_mld_mac80211_ac_to_fw_tx_fifo(ac);
602*6b627f88SBjoern A. Zeeb
603*6b627f88SBjoern A. Zeeb /* Only one link: take the lmac according to the band */
604*6b627f88SBjoern A. Zeeb if (hweight16(sta->valid_links) <= 1) {
605*6b627f88SBjoern A. Zeeb enum nl80211_band band;
606*6b627f88SBjoern A. Zeeb struct ieee80211_bss_conf *link =
607*6b627f88SBjoern A. Zeeb wiphy_dereference(mld->wiphy,
608*6b627f88SBjoern A. Zeeb vif->link_conf[link_sta->link_id]);
609*6b627f88SBjoern A. Zeeb
610*6b627f88SBjoern A. Zeeb if (WARN_ON(!link || !link->chanreq.oper.chan))
611*6b627f88SBjoern A. Zeeb band = NL80211_BAND_2GHZ;
612*6b627f88SBjoern A. Zeeb else
613*6b627f88SBjoern A. Zeeb band = link->chanreq.oper.chan->band;
614*6b627f88SBjoern A. Zeeb lmac = iwl_mld_get_lmac_id(mld, band);
615*6b627f88SBjoern A. Zeeb
616*6b627f88SBjoern A. Zeeb /* More than one link but with 2 lmacs: take the minimum */
617*6b627f88SBjoern A. Zeeb } else if (fw_has_capa(&mld->fw->ucode_capa,
618*6b627f88SBjoern A. Zeeb IWL_UCODE_TLV_CAPA_CDB_SUPPORT)) {
619*6b627f88SBjoern A. Zeeb lmac = IWL_LMAC_5G_INDEX;
620*6b627f88SBjoern A. Zeeb result = min_t(unsigned int, result,
621*6b627f88SBjoern A. Zeeb mld->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256);
622*6b627f88SBjoern A. Zeeb lmac = IWL_LMAC_24G_INDEX;
623*6b627f88SBjoern A. Zeeb /* More than one link but only one lmac */
624*6b627f88SBjoern A. Zeeb } else {
625*6b627f88SBjoern A. Zeeb lmac = IWL_LMAC_24G_INDEX;
626*6b627f88SBjoern A. Zeeb }
627*6b627f88SBjoern A. Zeeb
628*6b627f88SBjoern A. Zeeb return min_t(unsigned int, result,
629*6b627f88SBjoern A. Zeeb mld->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256);
630*6b627f88SBjoern A. Zeeb }
631*6b627f88SBjoern A. Zeeb
iwl_mld_handle_tlc_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)632*6b627f88SBjoern A. Zeeb void iwl_mld_handle_tlc_notif(struct iwl_mld *mld,
633*6b627f88SBjoern A. Zeeb struct iwl_rx_packet *pkt)
634*6b627f88SBjoern A. Zeeb {
635*6b627f88SBjoern A. Zeeb struct iwl_tlc_update_notif *notif = (void *)pkt->data;
636*6b627f88SBjoern A. Zeeb struct ieee80211_link_sta *link_sta;
637*6b627f88SBjoern A. Zeeb u32 flags = le32_to_cpu(notif->flags);
638*6b627f88SBjoern A. Zeeb u32 enabled;
639*6b627f88SBjoern A. Zeeb u16 size;
640*6b627f88SBjoern A. Zeeb
641*6b627f88SBjoern A. Zeeb if (IWL_FW_CHECK(mld, notif->sta_id >= mld->fw->ucode_capa.num_stations,
642*6b627f88SBjoern A. Zeeb "Invalid sta id (%d) in TLC notification\n",
643*6b627f88SBjoern A. Zeeb notif->sta_id))
644*6b627f88SBjoern A. Zeeb return;
645*6b627f88SBjoern A. Zeeb
646*6b627f88SBjoern A. Zeeb link_sta = wiphy_dereference(mld->wiphy,
647*6b627f88SBjoern A. Zeeb mld->fw_id_to_link_sta[notif->sta_id]);
648*6b627f88SBjoern A. Zeeb
649*6b627f88SBjoern A. Zeeb if (WARN(IS_ERR_OR_NULL(link_sta),
650*6b627f88SBjoern A. Zeeb "link_sta of sta id (%d) doesn't exist\n", notif->sta_id))
651*6b627f88SBjoern A. Zeeb return;
652*6b627f88SBjoern A. Zeeb
653*6b627f88SBjoern A. Zeeb if (flags & IWL_TLC_NOTIF_FLAG_RATE) {
654*6b627f88SBjoern A. Zeeb struct iwl_mld_link_sta *mld_link_sta =
655*6b627f88SBjoern A. Zeeb iwl_mld_link_sta_from_mac80211(link_sta);
656*6b627f88SBjoern A. Zeeb char pretty_rate[100];
657*6b627f88SBjoern A. Zeeb
658*6b627f88SBjoern A. Zeeb if (WARN_ON(!mld_link_sta))
659*6b627f88SBjoern A. Zeeb return;
660*6b627f88SBjoern A. Zeeb
661*6b627f88SBjoern A. Zeeb mld_link_sta->last_rate_n_flags =
662*6b627f88SBjoern A. Zeeb iwl_v3_rate_from_v2_v3(notif->rate,
663*6b627f88SBjoern A. Zeeb mld->fw_rates_ver_3);
664*6b627f88SBjoern A. Zeeb
665*6b627f88SBjoern A. Zeeb rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate),
666*6b627f88SBjoern A. Zeeb mld_link_sta->last_rate_n_flags);
667*6b627f88SBjoern A. Zeeb IWL_DEBUG_RATE(mld, "TLC notif: new rate = %s\n", pretty_rate);
668*6b627f88SBjoern A. Zeeb }
669*6b627f88SBjoern A. Zeeb
670*6b627f88SBjoern A. Zeeb /* We are done processing the notif */
671*6b627f88SBjoern A. Zeeb if (!(flags & IWL_TLC_NOTIF_FLAG_AMSDU))
672*6b627f88SBjoern A. Zeeb return;
673*6b627f88SBjoern A. Zeeb
674*6b627f88SBjoern A. Zeeb enabled = le32_to_cpu(notif->amsdu_enabled);
675*6b627f88SBjoern A. Zeeb size = le32_to_cpu(notif->amsdu_size);
676*6b627f88SBjoern A. Zeeb
677*6b627f88SBjoern A. Zeeb if (size < 2000) {
678*6b627f88SBjoern A. Zeeb size = 0;
679*6b627f88SBjoern A. Zeeb enabled = 0;
680*6b627f88SBjoern A. Zeeb }
681*6b627f88SBjoern A. Zeeb
682*6b627f88SBjoern A. Zeeb if (IWL_FW_CHECK(mld, size > link_sta->agg.max_amsdu_len,
683*6b627f88SBjoern A. Zeeb "Invalid AMSDU len in TLC notif: %d (Max AMSDU len: %d)\n",
684*6b627f88SBjoern A. Zeeb size, link_sta->agg.max_amsdu_len))
685*6b627f88SBjoern A. Zeeb return;
686*6b627f88SBjoern A. Zeeb
687*6b627f88SBjoern A. Zeeb link_sta->agg.max_rc_amsdu_len = size;
688*6b627f88SBjoern A. Zeeb
689*6b627f88SBjoern A. Zeeb for (int i = 0; i < IWL_MAX_TID_COUNT; i++) {
690*6b627f88SBjoern A. Zeeb if (enabled & BIT(i))
691*6b627f88SBjoern A. Zeeb link_sta->agg.max_tid_amsdu_len[i] =
692*6b627f88SBjoern A. Zeeb iwl_mld_get_amsdu_size_of_tid(mld, link_sta, i);
693*6b627f88SBjoern A. Zeeb else
694*6b627f88SBjoern A. Zeeb link_sta->agg.max_tid_amsdu_len[i] = 1;
695*6b627f88SBjoern A. Zeeb }
696*6b627f88SBjoern A. Zeeb
697*6b627f88SBjoern A. Zeeb ieee80211_sta_recalc_aggregates(link_sta->sta);
698*6b627f88SBjoern A. Zeeb
699*6b627f88SBjoern A. Zeeb IWL_DEBUG_RATE(mld,
700*6b627f88SBjoern A. Zeeb "AMSDU update. AMSDU size: %d, AMSDU selected size: %d, AMSDU TID bitmap 0x%X\n",
701*6b627f88SBjoern A. Zeeb le32_to_cpu(notif->amsdu_size), size, enabled);
702*6b627f88SBjoern A. Zeeb }
703