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