Merge tag 'platform-drivers-x86-v6.9-3' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-block.git] / drivers / net / wireless / intel / iwlwifi / mvm / link.c
CommitLineData
55eb1c5f
MK
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2/*
b82730bf 3 * Copyright (C) 2022 - 2024 Intel Corporation
55eb1c5f
MK
4 */
5#include "mvm.h"
bf976c81 6#include "time-event.h"
55eb1c5f 7
d6f6b0d8
GG
8static u32 iwl_mvm_get_free_fw_link_id(struct iwl_mvm *mvm,
9 struct iwl_mvm_vif *mvm_vif)
10{
11 u32 link_id;
12
13 lockdep_assert_held(&mvm->mutex);
14
15 link_id = ffz(mvm->fw_link_ids_map);
16
17 /* this case can happen if there're deactivated but not removed links */
18 if (link_id > IWL_MVM_FW_MAX_LINK_ID)
19 return IWL_MVM_FW_LINK_ID_INVALID;
20
21 mvm->fw_link_ids_map |= BIT(link_id);
22 return link_id;
23}
24
25static void iwl_mvm_release_fw_link_id(struct iwl_mvm *mvm, u32 link_id)
26{
27 lockdep_assert_held(&mvm->mutex);
28
29 if (!WARN_ON(link_id > IWL_MVM_FW_MAX_LINK_ID))
30 mvm->fw_link_ids_map &= ~BIT(link_id);
31}
32
55eb1c5f
MK
33static int iwl_mvm_link_cmd_send(struct iwl_mvm *mvm,
34 struct iwl_link_config_cmd *cmd,
35 enum iwl_ctxt_action action)
36{
37 int ret;
38
39 cmd->action = cpu_to_le32(action);
40 ret = iwl_mvm_send_cmd_pdu(mvm,
41 WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD), 0,
42 sizeof(*cmd), cmd);
43 if (ret)
44 IWL_ERR(mvm, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n",
45 action, ret);
46 return ret;
47}
48
a8b5d480
IP
49int iwl_mvm_set_link_mapping(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
50 struct ieee80211_bss_conf *link_conf)
51{
52 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
53 struct iwl_mvm_vif_link_info *link_info =
54 mvmvif->link[link_conf->link_id];
55
56 if (link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID) {
57 link_info->fw_link_id = iwl_mvm_get_free_fw_link_id(mvm,
58 mvmvif);
59 if (link_info->fw_link_id >=
60 ARRAY_SIZE(mvm->link_id_to_link_conf))
61 return -EINVAL;
62
63 rcu_assign_pointer(mvm->link_id_to_link_conf[link_info->fw_link_id],
64 link_conf);
65 }
66
67 return 0;
68}
69
cacc1d42
GG
70int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
71 struct ieee80211_bss_conf *link_conf)
55eb1c5f
MK
72{
73 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
cacc1d42 74 unsigned int link_id = link_conf->link_id;
d6f6b0d8 75 struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
55eb1c5f 76 struct iwl_link_config_cmd cmd = {};
4dde4ff0
ST
77 unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD);
78 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1);
a8b5d480 79 int ret;
cacc1d42 80
d6f6b0d8 81 if (WARN_ON_ONCE(!link_info))
cacc1d42 82 return -EINVAL;
55eb1c5f 83
a8b5d480
IP
84 ret = iwl_mvm_set_link_mapping(mvm, vif, link_conf);
85 if (ret)
86 return ret;
d6f6b0d8 87
55eb1c5f
MK
88 /* Update SF - Disable if needed. if this fails, SF might still be on
89 * while many macs are bound, which is forbidden - so fail the binding.
90 */
91 if (iwl_mvm_sf_update(mvm, vif, false))
92 return -EINVAL;
93
d6f6b0d8 94 cmd.link_id = cpu_to_le32(link_info->fw_link_id);
55eb1c5f 95 cmd.mac_id = cpu_to_le32(mvmvif->id);
3f302269 96 cmd.spec_link_id = link_conf->link_id;
84ef7cbe
IP
97 WARN_ON_ONCE(link_info->phy_ctxt);
98 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
55eb1c5f 99
cacc1d42 100 memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
55eb1c5f 101
cacc1d42
GG
102 if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
103 memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
6b5a87df 104
4dde4ff0
ST
105 if (cmd_ver < 2)
106 cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac);
12bacfc2 107
55eb1c5f
MK
108 return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD);
109}
110
111int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
cacc1d42 112 struct ieee80211_bss_conf *link_conf,
55eb1c5f
MK
113 u32 changes, bool active)
114{
115 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
cacc1d42 116 unsigned int link_id = link_conf->link_id;
d6f6b0d8 117 struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
cacc1d42 118 struct iwl_mvm_phy_ctxt *phyctxt;
55eb1c5f
MK
119 struct iwl_link_config_cmd cmd = {};
120 u32 ht_flag, flags = 0, flags_mask = 0;
d6f6b0d8 121 int ret;
4dde4ff0
ST
122 unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD);
123 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1);
d6f6b0d8
GG
124
125 if (WARN_ON_ONCE(!link_info ||
126 link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID))
127 return -EINVAL;
55eb1c5f 128
bf976c81
JB
129 if (changes & LINK_CONTEXT_MODIFY_ACTIVE) {
130 /* When activating a link, phy context should be valid;
131 * when deactivating a link, it also should be valid since
132 * the link was active before. So, do nothing in this case.
133 * Since a link is added first with FW_CTXT_INVALID, then we
134 * can get here in case it's removed before it was activated.
135 */
136 if (!link_info->phy_ctxt)
137 return 0;
138
139 /* Catch early if driver tries to activate or deactivate a link
140 * twice.
141 */
142 WARN_ON_ONCE(active == link_info->active);
143
144 /* When deactivating a link session protection should
145 * be stopped
146 */
147 if (!active && vif->type == NL80211_IFTYPE_STATION)
148 iwl_mvm_stop_session_protection(mvm, vif);
149 }
150
d6f6b0d8 151 cmd.link_id = cpu_to_le32(link_info->fw_link_id);
55eb1c5f
MK
152
153 /* The phy_id, link address and listen_lmac can be modified only until
154 * the link becomes active, otherwise they will be ignored.
155 */
d6f6b0d8 156 phyctxt = link_info->phy_ctxt;
cb145863
JB
157 if (phyctxt)
158 cmd.phy_id = cpu_to_le32(phyctxt->id);
159 else
160 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
55eb1c5f
MK
161 cmd.mac_id = cpu_to_le32(mvmvif->id);
162
cacc1d42 163 memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
55eb1c5f
MK
164
165 cmd.active = cpu_to_le32(active);
166
cacc1d42
GG
167 if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
168 memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
6b5a87df 169
cacc1d42
GG
170 iwl_mvm_set_fw_basic_rates(mvm, vif, link_conf,
171 &cmd.cck_rates, &cmd.ofdm_rates);
55eb1c5f 172
cacc1d42
GG
173 cmd.cck_short_preamble = cpu_to_le32(link_conf->use_short_preamble);
174 cmd.short_slot = cpu_to_le32(link_conf->use_short_slot);
55eb1c5f
MK
175
176 /* The fw does not distinguish between ht and fat */
177 ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT;
cacc1d42
GG
178 iwl_mvm_set_fw_protection_flags(mvm, vif, link_conf,
179 &cmd.protection_flags,
55eb1c5f
MK
180 ht_flag, LINK_PROT_FLG_TGG_PROTECT);
181
77e1f3f3 182 iwl_mvm_set_fw_qos_params(mvm, vif, link_conf, cmd.ac,
cacc1d42 183 &cmd.qos_flags);
55eb1c5f 184
55eb1c5f 185
cacc1d42
GG
186 cmd.bi = cpu_to_le32(link_conf->beacon_int);
187 cmd.dtim_interval = cpu_to_le32(link_conf->beacon_int *
188 link_conf->dtim_period);
55eb1c5f 189
cacc1d42 190 if (!link_conf->he_support || iwlwifi_mod_params.disable_11ax ||
22c58834 191 (vif->type == NL80211_IFTYPE_STATION && !vif->cfg.assoc)) {
55eb1c5f
MK
192 changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS;
193 goto send_cmd;
194 }
195
cacc1d42 196 cmd.htc_trig_based_pkt_ext = link_conf->htc_trig_based_pkt_ext;
55eb1c5f 197
cacc1d42 198 if (link_conf->uora_exists) {
55eb1c5f 199 cmd.rand_alloc_ecwmin =
cacc1d42 200 link_conf->uora_ocw_range & 0x7;
55eb1c5f 201 cmd.rand_alloc_ecwmax =
cacc1d42 202 (link_conf->uora_ocw_range >> 3) & 0x7;
55eb1c5f
MK
203 }
204
205 /* TODO how to set ndp_fdbk_buff_th_exp? */
206
e119e740 207 if (iwl_mvm_set_fw_mu_edca_params(mvm, mvmvif->link[link_id],
55eb1c5f
MK
208 &cmd.trig_based_txf[0])) {
209 flags |= LINK_FLG_MU_EDCA_CW;
210 flags_mask |= LINK_FLG_MU_EDCA_CW;
211 }
212
35b9281f 213 if (changes & LINK_CONTEXT_MODIFY_EHT_PARAMS) {
b82730bf
JB
214 struct ieee80211_chanctx_conf *ctx;
215 struct cfg80211_chan_def *def = NULL;
216
217 rcu_read_lock();
218 ctx = rcu_dereference(link_conf->chanctx_conf);
219 if (ctx)
220 def = iwl_mvm_chanctx_def(mvm, ctx);
221
35b9281f 222 if (iwlwifi_mod_params.disable_11be ||
9a43c190
JB
223 !link_conf->eht_support || !def ||
224 iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1) >= 6)
35b9281f
IP
225 changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS;
226 else
b82730bf
JB
227 cmd.puncture_mask = cpu_to_le16(def->punctured);
228 rcu_read_unlock();
35b9281f 229 }
55eb1c5f 230
cacc1d42 231 cmd.bss_color = link_conf->he_bss_color.color;
55eb1c5f 232
cacc1d42 233 if (!link_conf->he_bss_color.enabled) {
55eb1c5f
MK
234 flags |= LINK_FLG_BSS_COLOR_DIS;
235 flags_mask |= LINK_FLG_BSS_COLOR_DIS;
236 }
237
cacc1d42 238 cmd.frame_time_rts_th = cpu_to_le16(link_conf->frame_time_rts_th);
55eb1c5f
MK
239
240 /* Block 26-tone RU OFDMA transmissions */
d6f6b0d8 241 if (link_info->he_ru_2mhz_block) {
55eb1c5f
MK
242 flags |= LINK_FLG_RU_2MHZ_BLOCK;
243 flags_mask |= LINK_FLG_RU_2MHZ_BLOCK;
244 }
245
cacc1d42 246 if (link_conf->nontransmitted) {
55eb1c5f 247 ether_addr_copy(cmd.ref_bssid_addr,
cacc1d42
GG
248 link_conf->transmitter_bssid);
249 cmd.bssid_index = link_conf->bssid_index;
55eb1c5f
MK
250 }
251
252send_cmd:
253 cmd.modify_mask = cpu_to_le32(changes);
254 cmd.flags = cpu_to_le32(flags);
255 cmd.flags_mask = cpu_to_le32(flags_mask);
ed0c3433 256 cmd.spec_link_id = link_conf->link_id;
4dde4ff0
ST
257 if (cmd_ver < 2)
258 cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac);
55eb1c5f 259
d6f6b0d8 260 ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_MODIFY);
9deccfcd 261 if (!ret && (changes & LINK_CONTEXT_MODIFY_ACTIVE))
d6f6b0d8 262 link_info->active = active;
d6f6b0d8
GG
263
264 return ret;
55eb1c5f
MK
265}
266
a8b5d480
IP
267int iwl_mvm_unset_link_mapping(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
268 struct ieee80211_bss_conf *link_conf)
269{
270 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
271 struct iwl_mvm_vif_link_info *link_info =
272 mvmvif->link[link_conf->link_id];
273
274 /* mac80211 thought we have the link, but it was never configured */
275 if (WARN_ON(!link_info ||
276 link_info->fw_link_id >=
277 ARRAY_SIZE(mvm->link_id_to_link_conf)))
278 return -EINVAL;
279
280 RCU_INIT_POINTER(mvm->link_id_to_link_conf[link_info->fw_link_id],
281 NULL);
282 return 0;
283}
284
cacc1d42
GG
285int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
286 struct ieee80211_bss_conf *link_conf)
55eb1c5f
MK
287{
288 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
d6f6b0d8
GG
289 unsigned int link_id = link_conf->link_id;
290 struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
55eb1c5f
MK
291 struct iwl_link_config_cmd cmd = {};
292 int ret;
293
a8b5d480
IP
294 ret = iwl_mvm_unset_link_mapping(mvm, vif, link_conf);
295 if (ret)
ac139aa3 296 return 0;
d6f6b0d8
GG
297
298 cmd.link_id = cpu_to_le32(link_info->fw_link_id);
299 iwl_mvm_release_fw_link_id(mvm, link_info->fw_link_id);
300 link_info->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;
ed0c3433 301 cmd.spec_link_id = link_conf->link_id;
f3276ff0 302 cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
d6f6b0d8 303
55eb1c5f
MK
304 ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE);
305
306 if (!ret)
307 if (iwl_mvm_sf_update(mvm, vif, true))
308 IWL_ERR(mvm, "Failed to update SF state\n");
309
310 return ret;
311}
bf976c81
JB
312
313/* link should be deactivated before removal, so in most cases we need to
314 * perform these two operations together
315 */
316int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
317 struct ieee80211_bss_conf *link_conf)
318{
319 int ret;
320
321 ret = iwl_mvm_link_changed(mvm, vif, link_conf,
322 LINK_CONTEXT_MODIFY_ACTIVE, false);
323 if (ret)
324 return ret;
325
326 ret = iwl_mvm_remove_link(mvm, vif, link_conf);
327 if (ret)
328 return ret;
329
330 return ret;
331}