1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3 * Copyright (C) 2024-2026 Intel Corporation
4 */
5
6 #include "constants.h"
7 #include "link.h"
8 #include "iface.h"
9 #include "mlo.h"
10 #include "hcmd.h"
11 #include "phy.h"
12 #include "fw/api/rs.h"
13 #include "fw/api/txq.h"
14 #include "fw/api/mac.h"
15
16 #include "fw/api/context.h"
17 #include "fw/dbg.h"
18
iwl_mld_send_link_cmd(struct iwl_mld * mld,struct iwl_link_config_cmd * cmd,enum iwl_ctxt_action action)19 static int iwl_mld_send_link_cmd(struct iwl_mld *mld,
20 struct iwl_link_config_cmd *cmd,
21 enum iwl_ctxt_action action)
22 {
23 int ret;
24
25 lockdep_assert_wiphy(mld->wiphy);
26
27 cmd->action = cpu_to_le32(action);
28 ret = iwl_mld_send_cmd_pdu(mld,
29 WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD),
30 cmd);
31 if (ret)
32 IWL_ERR(mld, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n",
33 action, ret);
34 return ret;
35 }
36
iwl_mld_add_link_to_fw(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf)37 static int iwl_mld_add_link_to_fw(struct iwl_mld *mld,
38 struct ieee80211_bss_conf *link_conf)
39 {
40 struct ieee80211_vif *vif = link_conf->vif;
41 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
42 struct iwl_mld_link *link = iwl_mld_link_from_mac80211(link_conf);
43 struct iwl_link_config_cmd cmd = {};
44
45 lockdep_assert_wiphy(mld->wiphy);
46
47 if (WARN_ON(!link))
48 return -EINVAL;
49
50 cmd.link_id = cpu_to_le32(link->fw_id);
51 cmd.mac_id = cpu_to_le32(mld_vif->fw_id);
52 cmd.spec_link_id = link_conf->link_id;
53 cmd.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID);
54
55 ether_addr_copy(cmd.local_link_addr, link_conf->addr);
56
57 if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
58 ether_addr_copy(cmd.ibss_bssid_addr, link_conf->bssid);
59
60 return iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_ADD);
61 }
62
63 /* Get the basic rates of the used band and add the mandatory ones */
iwl_mld_fill_rates(struct iwl_mld * mld,struct ieee80211_bss_conf * link,struct ieee80211_chanctx_conf * chan_ctx,__le32 * cck_rates,__le32 * ofdm_rates)64 static void iwl_mld_fill_rates(struct iwl_mld *mld,
65 struct ieee80211_bss_conf *link,
66 struct ieee80211_chanctx_conf *chan_ctx,
67 __le32 *cck_rates, __le32 *ofdm_rates)
68 {
69 struct cfg80211_chan_def *chandef =
70 iwl_mld_get_chandef_from_chanctx(mld, chan_ctx);
71 struct ieee80211_supported_band *sband =
72 mld->hw->wiphy->bands[chandef->chan->band];
73 unsigned long basic = link->basic_rates;
74 int lowest_present_ofdm = 100;
75 int lowest_present_cck = 100;
76 u32 cck = 0;
77 u32 ofdm = 0;
78 int i;
79
80 for_each_set_bit(i, &basic, BITS_PER_LONG) {
81 int hw = sband->bitrates[i].hw_value;
82
83 if (hw >= IWL_FIRST_OFDM_RATE) {
84 ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE);
85 if (lowest_present_ofdm > hw)
86 lowest_present_ofdm = hw;
87 } else {
88 BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
89
90 cck |= BIT(hw);
91 if (lowest_present_cck > hw)
92 lowest_present_cck = hw;
93 }
94 }
95
96 /* Now we've got the basic rates as bitmaps in the ofdm and cck
97 * variables. This isn't sufficient though, as there might not
98 * be all the right rates in the bitmap. E.g. if the only basic
99 * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps
100 * and 6 Mbps because the 802.11-2007 standard says in 9.6:
101 *
102 * [...] a STA responding to a received frame shall transmit
103 * its Control Response frame [...] at the highest rate in the
104 * BSSBasicRateSet parameter that is less than or equal to the
105 * rate of the immediately previous frame in the frame exchange
106 * sequence ([...]) and that is of the same modulation class
107 * ([...]) as the received frame. If no rate contained in the
108 * BSSBasicRateSet parameter meets these conditions, then the
109 * control frame sent in response to a received frame shall be
110 * transmitted at the highest mandatory rate of the PHY that is
111 * less than or equal to the rate of the received frame, and
112 * that is of the same modulation class as the received frame.
113 *
114 * As a consequence, we need to add all mandatory rates that are
115 * lower than all of the basic rates to these bitmaps.
116 */
117
118 if (lowest_present_ofdm > IWL_RATE_24M_INDEX)
119 ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE;
120 if (lowest_present_ofdm > IWL_RATE_12M_INDEX)
121 ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE;
122 /* 6M already there or needed so always add */
123 ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE;
124
125 /* CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP.
126 * Note, however:
127 * - if no CCK rates are basic, it must be ERP since there must
128 * be some basic rates at all, so they're OFDM => ERP PHY
129 * (or we're in 5 GHz, and the cck bitmap will never be used)
130 * - if 11M is a basic rate, it must be ERP as well, so add 5.5M
131 * - if 5.5M is basic, 1M and 2M are mandatory
132 * - if 2M is basic, 1M is mandatory
133 * - if 1M is basic, that's the only valid ACK rate.
134 * As a consequence, it's not as complicated as it sounds, just add
135 * any lower rates to the ACK rate bitmap.
136 */
137 if (lowest_present_cck > IWL_RATE_11M_INDEX)
138 cck |= IWL_RATE_BIT_MSK(11) >> IWL_FIRST_CCK_RATE;
139 if (lowest_present_cck > IWL_RATE_5M_INDEX)
140 cck |= IWL_RATE_BIT_MSK(5) >> IWL_FIRST_CCK_RATE;
141 if (lowest_present_cck > IWL_RATE_2M_INDEX)
142 cck |= IWL_RATE_BIT_MSK(2) >> IWL_FIRST_CCK_RATE;
143 /* 1M already there or needed so always add */
144 cck |= IWL_RATE_BIT_MSK(1) >> IWL_FIRST_CCK_RATE;
145
146 *cck_rates = cpu_to_le32((u32)cck);
147 *ofdm_rates = cpu_to_le32((u32)ofdm);
148 }
149
iwl_mld_fill_protection_flags(struct iwl_mld * mld,struct ieee80211_bss_conf * link,__le32 * protection_flags)150 static void iwl_mld_fill_protection_flags(struct iwl_mld *mld,
151 struct ieee80211_bss_conf *link,
152 __le32 *protection_flags)
153 {
154 u8 protection_mode = link->ht_operation_mode &
155 IEEE80211_HT_OP_MODE_PROTECTION;
156 u8 ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT;
157
158 IWL_DEBUG_RATE(mld, "HT protection mode: %d\n", protection_mode);
159
160 if (link->use_cts_prot)
161 *protection_flags |= cpu_to_le32(LINK_PROT_FLG_TGG_PROTECT);
162
163 /* See section 9.23.3.1 of IEEE 80211-2012.
164 * Nongreenfield HT STAs Present is not supported.
165 */
166 switch (protection_mode) {
167 case IEEE80211_HT_OP_MODE_PROTECTION_NONE:
168 break;
169 case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
170 case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
171 *protection_flags |= cpu_to_le32(ht_flag);
172 break;
173 case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
174 /* Protect when channel wider than 20MHz */
175 if (link->chanreq.oper.width > NL80211_CHAN_WIDTH_20)
176 *protection_flags |= cpu_to_le32(ht_flag);
177 break;
178 }
179 }
180
iwl_mld_mac80211_ac_to_fw_ac(enum ieee80211_ac_numbers ac)181 static u8 iwl_mld_mac80211_ac_to_fw_ac(enum ieee80211_ac_numbers ac)
182 {
183 static const u8 mac80211_ac_to_fw[] = {
184 AC_VO,
185 AC_VI,
186 AC_BE,
187 AC_BK
188 };
189
190 return mac80211_ac_to_fw[ac];
191 }
192
iwl_mld_fill_qos_params(struct ieee80211_bss_conf * link,struct iwl_ac_qos * ac,__le32 * qos_flags)193 static void iwl_mld_fill_qos_params(struct ieee80211_bss_conf *link,
194 struct iwl_ac_qos *ac, __le32 *qos_flags)
195 {
196 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
197
198 /* no need to check mld_link since it is done in the caller */
199
200 for (int mac_ac = 0; mac_ac < IEEE80211_NUM_ACS; mac_ac++) {
201 u8 txf = iwl_mld_mac80211_ac_to_fw_tx_fifo(mac_ac);
202 u8 fw_ac = iwl_mld_mac80211_ac_to_fw_ac(mac_ac);
203
204 ac[fw_ac].cw_min =
205 cpu_to_le16(mld_link->queue_params[mac_ac].cw_min);
206 ac[fw_ac].cw_max =
207 cpu_to_le16(mld_link->queue_params[mac_ac].cw_max);
208 ac[fw_ac].edca_txop =
209 cpu_to_le16(mld_link->queue_params[mac_ac].txop * 32);
210 ac[fw_ac].aifsn = mld_link->queue_params[mac_ac].aifs;
211 ac[fw_ac].fifos_mask = BIT(txf);
212 }
213
214 if (link->qos)
215 *qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
216
217 if (link->chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT)
218 *qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN);
219 }
220
iwl_mld_fill_mu_edca(struct iwl_mld * mld,const struct iwl_mld_link * mld_link,struct iwl_he_backoff_conf * trig_based_txf)221 static bool iwl_mld_fill_mu_edca(struct iwl_mld *mld,
222 const struct iwl_mld_link *mld_link,
223 struct iwl_he_backoff_conf *trig_based_txf)
224 {
225 for (int mac_ac = 0; mac_ac < IEEE80211_NUM_ACS; mac_ac++) {
226 const struct ieee80211_he_mu_edca_param_ac_rec *mu_edca =
227 &mld_link->queue_params[mac_ac].mu_edca_param_rec;
228 u8 fw_ac = iwl_mld_mac80211_ac_to_fw_ac(mac_ac);
229
230 if (!mld_link->queue_params[mac_ac].mu_edca)
231 return false;
232
233 trig_based_txf[fw_ac].cwmin =
234 cpu_to_le16(mu_edca->ecw_min_max & 0xf);
235 trig_based_txf[fw_ac].cwmax =
236 cpu_to_le16((mu_edca->ecw_min_max & 0xf0) >> 4);
237 trig_based_txf[fw_ac].aifsn =
238 cpu_to_le16(mu_edca->aifsn & 0xf);
239 trig_based_txf[fw_ac].mu_time =
240 cpu_to_le16(mu_edca->mu_edca_timer);
241 }
242 return true;
243 }
244
245 int
iwl_mld_change_link_in_fw(struct iwl_mld * mld,struct ieee80211_bss_conf * link,u32 changes)246 iwl_mld_change_link_in_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link,
247 u32 changes)
248 {
249 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
250 struct ieee80211_vif *vif = link->vif;
251 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
252 struct ieee80211_chanctx_conf *chan_ctx;
253 struct iwl_link_config_cmd cmd = {};
254 u32 flags = 0;
255
256 lockdep_assert_wiphy(mld->wiphy);
257
258 if (WARN_ON(!mld_link))
259 return -EINVAL;
260
261 cmd.link_id = cpu_to_le32(mld_link->fw_id);
262 cmd.spec_link_id = link->link_id;
263 cmd.mac_id = cpu_to_le32(mld_vif->fw_id);
264
265 chan_ctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx);
266
267 cmd.phy_id = cpu_to_le32(chan_ctx ?
268 iwl_mld_phy_from_mac80211(chan_ctx)->fw_id :
269 FW_CTXT_ID_INVALID);
270
271 ether_addr_copy(cmd.local_link_addr, link->addr);
272
273 cmd.active = cpu_to_le32(mld_link->active);
274
275 if ((changes & LINK_CONTEXT_MODIFY_ACTIVE) && !mld_link->active &&
276 mld_link->silent_deactivation) {
277 /* We are de-activating a link that is having CSA with
278 * immediate quiet in EMLSR. Tell the firmware not to send any
279 * frame.
280 */
281 cmd.block_tx = 1;
282 mld_link->silent_deactivation = false;
283 }
284
285 if (vif->type == NL80211_IFTYPE_ADHOC && link->bssid)
286 ether_addr_copy(cmd.ibss_bssid_addr, link->bssid);
287
288 /* Channel context is needed to get the rates */
289 if (chan_ctx)
290 iwl_mld_fill_rates(mld, link, chan_ctx, &cmd.cck_rates,
291 &cmd.ofdm_rates);
292
293 cmd.cck_short_preamble = cpu_to_le32(link->use_short_preamble);
294 cmd.short_slot = cpu_to_le32(link->use_short_slot);
295
296 iwl_mld_fill_protection_flags(mld, link, &cmd.protection_flags);
297
298 iwl_mld_fill_qos_params(link, cmd.ac, &cmd.qos_flags);
299
300 cmd.bi = cpu_to_le32(link->beacon_int);
301 cmd.dtim_interval = cpu_to_le32(link->beacon_int * link->dtim_period);
302
303 /* Configure HE parameters only if HE is supported, and only after
304 * the parameters are set in mac80211 (meaning after assoc)
305 */
306 if (!link->he_support || iwlwifi_mod_params.disable_11ax ||
307 (vif->type == NL80211_IFTYPE_STATION && !vif->cfg.assoc)) {
308 changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS;
309 goto send_cmd;
310 }
311
312 /* ap_sta may be NULL if we're disconnecting */
313 if (mld_vif->ap_sta) {
314 struct ieee80211_link_sta *link_sta =
315 link_sta_dereference_check(mld_vif->ap_sta,
316 link->link_id);
317
318 if (!WARN_ON(!link_sta) && link_sta->he_cap.has_he &&
319 link_sta->he_cap.he_cap_elem.mac_cap_info[5] &
320 IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX)
321 cmd.ul_mu_data_disable = 1;
322 }
323
324 cmd.htc_trig_based_pkt_ext = link->htc_trig_based_pkt_ext;
325
326 if (link->uora_exists) {
327 cmd.rand_alloc_ecwmin = link->uora_ocw_range & 0x7;
328 cmd.rand_alloc_ecwmax = (link->uora_ocw_range >> 3) & 0x7;
329 }
330
331 if (iwl_mld_fill_mu_edca(mld, mld_link, cmd.trig_based_txf))
332 flags |= LINK_FLG_MU_EDCA_CW;
333
334 cmd.bss_color = link->he_bss_color.color;
335
336 if (!link->he_bss_color.enabled)
337 flags |= LINK_FLG_BSS_COLOR_DIS;
338
339 cmd.frame_time_rts_th = cpu_to_le16(link->frame_time_rts_th);
340
341 /* Block 26-tone RU OFDMA transmissions */
342 if (mld_link->he_ru_2mhz_block)
343 flags |= LINK_FLG_RU_2MHZ_BLOCK;
344
345 if (link->nontransmitted) {
346 ether_addr_copy(cmd.ref_bssid_addr, link->transmitter_bssid);
347 cmd.bssid_index = link->bssid_index;
348 }
349
350 /* The only EHT parameter is puncturing, and starting from PHY cmd
351 * version 6 - it is sent there. For older versions of the PHY cmd,
352 * puncturing is not needed at all.
353 */
354 if (WARN_ON(changes & LINK_CONTEXT_MODIFY_EHT_PARAMS))
355 changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS;
356
357 send_cmd:
358 cmd.modify_mask = cpu_to_le32(changes);
359 cmd.flags = cpu_to_le32(flags);
360
361 return iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_MODIFY);
362 }
363
iwl_mld_activate_link(struct iwl_mld * mld,struct ieee80211_bss_conf * link)364 int iwl_mld_activate_link(struct iwl_mld *mld,
365 struct ieee80211_bss_conf *link)
366 {
367 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
368 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(link->vif);
369 int ret;
370
371 lockdep_assert_wiphy(mld->wiphy);
372
373 if (WARN_ON(!mld_link || mld_link->active))
374 return -EINVAL;
375
376 mld_link->active = true;
377
378 ret = iwl_mld_change_link_in_fw(mld, link,
379 LINK_CONTEXT_MODIFY_ACTIVE);
380 if (ret)
381 mld_link->active = false;
382 else
383 mld_vif->last_link_activation_time =
384 ktime_get_boottime_seconds();
385
386 return ret;
387 }
388
iwl_mld_deactivate_link(struct iwl_mld * mld,struct ieee80211_bss_conf * link)389 void iwl_mld_deactivate_link(struct iwl_mld *mld,
390 struct ieee80211_bss_conf *link)
391 {
392 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
393 struct iwl_probe_resp_data *probe_data;
394
395 lockdep_assert_wiphy(mld->wiphy);
396
397 if (WARN_ON(!mld_link || !mld_link->active))
398 return;
399
400 iwl_mld_cancel_session_protection(mld, link->vif, link->link_id);
401
402 /* If we deactivate the link, we will probably remove it, or switch
403 * channel. In both cases, the CSA or Notice of Absence information is
404 * now irrelevant. Remove the data here.
405 */
406 probe_data = wiphy_dereference(mld->wiphy, mld_link->probe_resp_data);
407 RCU_INIT_POINTER(mld_link->probe_resp_data, NULL);
408 if (probe_data)
409 kfree_rcu(probe_data, rcu_head);
410
411 mld_link->active = false;
412
413 iwl_mld_change_link_in_fw(mld, link, LINK_CONTEXT_MODIFY_ACTIVE);
414
415 /* Now that the link is not active in FW, we don't expect any new
416 * notifications for it. Cancel the ones that are already pending
417 */
418 iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_LINK,
419 mld_link->fw_id);
420 }
421
422 static void
iwl_mld_rm_link_from_fw(struct iwl_mld * mld,struct ieee80211_bss_conf * link)423 iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link)
424 {
425 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
426 struct iwl_link_config_cmd cmd = {};
427
428 lockdep_assert_wiphy(mld->wiphy);
429
430 if (WARN_ON(!mld_link))
431 return;
432
433 cmd.link_id = cpu_to_le32(mld_link->fw_id);
434 cmd.spec_link_id = link->link_id;
435 cmd.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID);
436
437 iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE);
438 }
439
IWL_MLD_ALLOC_FN(link,bss_conf)440 static IWL_MLD_ALLOC_FN(link, bss_conf)
441
442 /* Constructor function for struct iwl_mld_link */
443 static int
444 iwl_mld_init_link(struct iwl_mld *mld, struct ieee80211_bss_conf *link,
445 struct iwl_mld_link *mld_link)
446 {
447 mld_link->average_beacon_energy = 0;
448
449 iwl_mld_init_internal_sta(&mld_link->bcast_sta);
450 iwl_mld_init_internal_sta(&mld_link->mcast_sta);
451 iwl_mld_init_internal_sta(&mld_link->mon_sta);
452
453 return iwl_mld_allocate_link_fw_id(mld, &mld_link->fw_id, link);
454 }
455
456 /* Initializes the link structure, maps fw id to the ieee80211_bss_conf, and
457 * adds a link to the fw
458 */
iwl_mld_add_link(struct iwl_mld * mld,struct ieee80211_bss_conf * bss_conf)459 int iwl_mld_add_link(struct iwl_mld *mld,
460 struct ieee80211_bss_conf *bss_conf)
461 {
462 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif);
463 struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf);
464 bool is_deflink = bss_conf == &bss_conf->vif->bss_conf;
465 int ret;
466
467 if (!link) {
468 if (is_deflink) {
469 link = &mld_vif->deflink;
470 } else {
471 link = kzalloc_obj(*link);
472 if (!link)
473 return -ENOMEM;
474 }
475 } else {
476 WARN_ON(!mld->fw_status.in_hw_restart);
477 }
478
479 ret = iwl_mld_init_link(mld, bss_conf, link);
480 if (ret)
481 goto free;
482
483 rcu_assign_pointer(mld_vif->link[bss_conf->link_id], link);
484
485 ret = iwl_mld_add_link_to_fw(mld, bss_conf);
486 if (ret) {
487 RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL);
488 RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL);
489 goto free;
490 }
491
492 return ret;
493
494 free:
495 if (!is_deflink)
496 kfree(link);
497 return ret;
498 }
499
500 /* Remove link from fw, unmap the bss_conf, and destroy the link structure */
iwl_mld_remove_link(struct iwl_mld * mld,struct ieee80211_bss_conf * bss_conf)501 void iwl_mld_remove_link(struct iwl_mld *mld,
502 struct ieee80211_bss_conf *bss_conf)
503 {
504 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif);
505 struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf);
506 bool is_deflink = link == &mld_vif->deflink;
507
508 if (WARN_ON(!link || link->active))
509 return;
510
511 iwl_mld_rm_link_from_fw(mld, bss_conf);
512 /* Continue cleanup on failure */
513
514 RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL);
515
516 if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links))
517 return;
518
519 RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL);
520
521 if (!is_deflink)
522 kfree_rcu(link, rcu_head);
523 }
524
iwl_mld_handle_missed_beacon_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)525 void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
526 struct iwl_rx_packet *pkt)
527 {
528 const struct iwl_missed_beacons_notif *notif = (const void *)pkt->data;
529 union iwl_dbg_tlv_tp_data tp_data = { .fw_pkt = pkt };
530 u32 fw_link_id = le32_to_cpu(notif->link_id);
531 u32 missed_bcon = le32_to_cpu(notif->consec_missed_beacons);
532 u32 missed_bcon_since_rx =
533 le32_to_cpu(notif->consec_missed_beacons_since_last_rx);
534 u32 scnd_lnk_bcn_lost =
535 le32_to_cpu(notif->consec_missed_beacons_other_link);
536 struct ieee80211_bss_conf *link_conf =
537 iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
538 struct ieee80211_bss_conf *other_link;
539 u32 bss_param_ch_cnt_link_id, other_link_fw_id;
540 struct ieee80211_vif *vif;
541 u8 link_id;
542
543 if (WARN_ON(!link_conf))
544 return;
545
546 vif = link_conf->vif;
547 link_id = link_conf->link_id;
548 bss_param_ch_cnt_link_id = link_conf->bss_param_ch_cnt_link_id;
549
550 IWL_DEBUG_INFO(mld,
551 "missed bcn link_id=%u, %u consecutive=%u\n",
552 link_id, missed_bcon, missed_bcon_since_rx);
553
554 if (WARN_ON(!vif))
555 return;
556
557 iwl_dbg_tlv_time_point(&mld->fwrt,
558 IWL_FW_INI_TIME_POINT_MISSED_BEACONS, &tp_data);
559
560 if (missed_bcon >= IWL_MLD_MISSED_BEACONS_THRESHOLD_LONG) {
561 if (missed_bcon_since_rx >=
562 IWL_MLD_MISSED_BEACONS_SINCE_RX_THOLD) {
563 ieee80211_connection_loss(vif);
564 return;
565 }
566 IWL_WARN(mld,
567 "missed beacons exceeds threshold, but receiving data. Stay connected, Expect bugs.\n");
568 return;
569 }
570
571 if (missed_bcon_since_rx > IWL_MLD_MISSED_BEACONS_THRESHOLD) {
572 ieee80211_cqm_beacon_loss_notify(vif, GFP_ATOMIC);
573
574 /* Not in EMLSR and we can't hear the link.
575 * Try to switch to a better link. EMLSR case is handled below.
576 */
577 if (!iwl_mld_emlsr_active(vif)) {
578 IWL_DEBUG_EHT(mld,
579 "missed beacons exceeds threshold. link_id=%u. Try to switch to a better link.\n",
580 link_id);
581 iwl_mld_int_mlo_scan(mld, vif);
582 }
583 }
584
585 /* no more logic if we're not in EMLSR */
586 if (hweight16(vif->active_links) <= 1)
587 return;
588
589 /* We are processing a notification before link activation */
590 if (le32_to_cpu(notif->other_link_id) == FW_CTXT_ID_INVALID)
591 return;
592
593 other_link_fw_id = le32_to_cpu(notif->other_link_id);
594 other_link = iwl_mld_fw_id_to_link_conf(mld, other_link_fw_id);
595
596 if (IWL_FW_CHECK(mld, !other_link, "link doesn't exist for: %d\n",
597 other_link_fw_id))
598 return;
599
600 IWL_DEBUG_EHT(mld,
601 "missed bcn link_id=%u: %u consecutive=%u, other link_id=%u: %u\n",
602 link_id, missed_bcon, missed_bcon_since_rx,
603 other_link->link_id, scnd_lnk_bcn_lost);
604
605 /* Exit EMLSR if we lost more than
606 * IWL_MLD_MISSED_BEACONS_EXIT_ESR_THRESH beacons on boths links
607 * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH on current link.
608 * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED
609 * on current link and the link's bss_param_ch_count has changed on
610 * the other link's beacon.
611 */
612 if ((missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS &&
613 scnd_lnk_bcn_lost >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) ||
614 missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH ||
615 (bss_param_ch_cnt_link_id != link_id &&
616 missed_bcon >=
617 IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED)) {
618 iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_MISSED_BEACON,
619 iwl_mld_get_primary_link(vif));
620 }
621 }
622 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_handle_missed_beacon_notif);
623
iwl_mld_cancel_missed_beacon_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt,u32 removed_link_id)624 bool iwl_mld_cancel_missed_beacon_notif(struct iwl_mld *mld,
625 struct iwl_rx_packet *pkt,
626 u32 removed_link_id)
627 {
628 struct iwl_missed_beacons_notif *notif = (void *)pkt->data;
629
630 if (le32_to_cpu(notif->other_link_id) == removed_link_id) {
631 /* Second link is being removed. Don't cancel the notification,
632 * but mark second link as invalid.
633 */
634 notif->other_link_id = cpu_to_le32(FW_CTXT_ID_INVALID);
635 }
636
637 /* If the primary link is removed, cancel the notification */
638 return le32_to_cpu(notif->link_id) == removed_link_id;
639 }
640
iwl_mld_link_set_associated(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link)641 int iwl_mld_link_set_associated(struct iwl_mld *mld, struct ieee80211_vif *vif,
642 struct ieee80211_bss_conf *link)
643 {
644 return iwl_mld_change_link_in_fw(mld, link, LINK_CONTEXT_MODIFY_ALL &
645 ~(LINK_CONTEXT_MODIFY_ACTIVE |
646 LINK_CONTEXT_MODIFY_EHT_PARAMS));
647 }
648
649 struct iwl_mld_rssi_to_grade {
650 s8 rssi[2];
651 u16 grade;
652 };
653
654 #define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \
655 { \
656 .rssi = {_lb, _hb_uhb}, \
657 .grade = _grade \
658 }
659
660 /*
661 * This array must be sorted by increasing RSSI for proper functionality.
662 * The grades are actually estimated throughput, represented as fixed-point
663 * with a scale factor of 1/10.
664 */
665 static const struct iwl_mld_rssi_to_grade rssi_to_grade_map[] = {
666 RSSI_TO_GRADE_LINE(-85, -89, 172),
667 RSSI_TO_GRADE_LINE(-83, -86, 344),
668 RSSI_TO_GRADE_LINE(-82, -85, 516),
669 RSSI_TO_GRADE_LINE(-80, -83, 688),
670 RSSI_TO_GRADE_LINE(-77, -79, 1032),
671 RSSI_TO_GRADE_LINE(-73, -76, 1376),
672 RSSI_TO_GRADE_LINE(-70, -74, 1548),
673 RSSI_TO_GRADE_LINE(-69, -72, 1720),
674 RSSI_TO_GRADE_LINE(-65, -68, 2064),
675 RSSI_TO_GRADE_LINE(-61, -66, 2294),
676 RSSI_TO_GRADE_LINE(-58, -61, 2580),
677 RSSI_TO_GRADE_LINE(-55, -58, 2868),
678 RSSI_TO_GRADE_LINE(-46, -55, 3098),
679 RSSI_TO_GRADE_LINE(-43, -54, 3442)
680 };
681
682 #define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade)
683
684 #define DEFAULT_CHAN_LOAD_2GHZ 30
685 #define DEFAULT_CHAN_LOAD_5GHZ 15
686 #define DEFAULT_CHAN_LOAD_6GHZ 0
687
688 /* Factors calculation is done with fixed-point with a scaling factor of 1/256 */
689 #define SCALE_FACTOR 256
690 #define MAX_CHAN_LOAD 256
691
692 static unsigned int
iwl_mld_get_n_subchannels(const struct ieee80211_bss_conf * link_conf)693 iwl_mld_get_n_subchannels(const struct ieee80211_bss_conf *link_conf)
694 {
695 enum nl80211_chan_width chan_width =
696 link_conf->chanreq.oper.width;
697 int mhz = nl80211_chan_width_to_mhz(chan_width);
698 unsigned int n_subchannels;
699
700 if (WARN_ONCE(mhz < 20 || mhz > 320,
701 "Invalid channel width : (%d)\n", mhz))
702 return 1;
703
704 /* total number of subchannels */
705 n_subchannels = mhz / 20;
706
707 /* No puncturing if less than 80 MHz */
708 if (mhz >= 80)
709 n_subchannels -= hweight16(link_conf->chanreq.oper.punctured);
710
711 return n_subchannels;
712 }
713
714 static int
iwl_mld_get_chan_load_from_element(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf)715 iwl_mld_get_chan_load_from_element(struct iwl_mld *mld,
716 struct ieee80211_bss_conf *link_conf)
717 {
718 const struct cfg80211_bss_ies *ies;
719 const struct element *bss_load_elem = NULL;
720 const struct ieee80211_bss_load_elem *bss_load;
721
722 guard(rcu)();
723
724 ies = rcu_dereference(link_conf->bss->beacon_ies);
725 if (ies)
726 bss_load_elem = cfg80211_find_elem(WLAN_EID_QBSS_LOAD,
727 ies->data, ies->len);
728
729 if (!bss_load_elem ||
730 bss_load_elem->datalen != sizeof(*bss_load))
731 return -EINVAL;
732
733 bss_load = (const void *)bss_load_elem->data;
734
735 return bss_load->channel_util;
736 }
737
738 static unsigned int
iwl_mld_get_chan_load_by_us(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf,bool expect_active_link)739 iwl_mld_get_chan_load_by_us(struct iwl_mld *mld,
740 struct ieee80211_bss_conf *link_conf,
741 bool expect_active_link)
742 {
743 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
744 struct ieee80211_chanctx_conf *chan_ctx;
745 struct iwl_mld_phy *phy;
746
747 if (!mld_link || !mld_link->active) {
748 WARN_ON(expect_active_link);
749 return 0;
750 }
751
752 if (WARN_ONCE(!rcu_access_pointer(mld_link->chan_ctx),
753 "Active link (%u) without channel ctxt assigned!\n",
754 link_conf->link_id))
755 return 0;
756
757 chan_ctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx);
758 phy = iwl_mld_phy_from_mac80211(chan_ctx);
759
760 return phy->channel_load_by_us;
761 }
762
763 /* Returns error if the channel utilization element is invalid/unavailable */
iwl_mld_get_chan_load_by_others(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf,bool expect_active_link)764 int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld,
765 struct ieee80211_bss_conf *link_conf,
766 bool expect_active_link)
767 {
768 int chan_load;
769 unsigned int chan_load_by_us;
770
771 /* get overall load */
772 chan_load = iwl_mld_get_chan_load_from_element(mld, link_conf);
773 if (chan_load < 0)
774 return chan_load;
775
776 chan_load_by_us = iwl_mld_get_chan_load_by_us(mld, link_conf,
777 expect_active_link);
778
779 /* channel load by us is given in percentage */
780 chan_load_by_us =
781 NORMALIZE_PERCENT_TO_255(chan_load_by_us);
782
783 /* Use only values that firmware sends that can possibly be valid */
784 if (chan_load_by_us <= chan_load)
785 chan_load -= chan_load_by_us;
786
787 return chan_load;
788 }
789
790 static unsigned int
iwl_mld_get_default_chan_load(struct ieee80211_bss_conf * link_conf)791 iwl_mld_get_default_chan_load(struct ieee80211_bss_conf *link_conf)
792 {
793 enum nl80211_band band = link_conf->chanreq.oper.chan->band;
794
795 switch (band) {
796 case NL80211_BAND_2GHZ:
797 return DEFAULT_CHAN_LOAD_2GHZ;
798 case NL80211_BAND_5GHZ:
799 return DEFAULT_CHAN_LOAD_5GHZ;
800 case NL80211_BAND_6GHZ:
801 return DEFAULT_CHAN_LOAD_6GHZ;
802 default:
803 WARN_ON(1);
804 return 0;
805 }
806 }
807
iwl_mld_get_chan_load(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf)808 unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld,
809 struct ieee80211_bss_conf *link_conf)
810 {
811 int chan_load;
812
813 chan_load = iwl_mld_get_chan_load_by_others(mld, link_conf, false);
814 if (chan_load >= 0)
815 return chan_load;
816
817 /* No information from the element, take the defaults */
818 chan_load = iwl_mld_get_default_chan_load(link_conf);
819
820 /* The defaults are given in percentage */
821 return NORMALIZE_PERCENT_TO_255(chan_load);
822 }
823
824 static unsigned int
iwl_mld_get_avail_chan_load(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf)825 iwl_mld_get_avail_chan_load(struct iwl_mld *mld,
826 struct ieee80211_bss_conf *link_conf)
827 {
828 return MAX_CHAN_LOAD - iwl_mld_get_chan_load(mld, link_conf);
829 }
830
831 /* This function calculates the grade of a link. Returns 0 in error case */
iwl_mld_get_link_grade(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf)832 unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
833 struct ieee80211_bss_conf *link_conf)
834 {
835 enum nl80211_band band;
836 int rssi_idx;
837 s32 link_rssi;
838 unsigned int grade = MAX_GRADE;
839
840 if (WARN_ON_ONCE(!link_conf))
841 return 0;
842
843 band = link_conf->chanreq.oper.chan->band;
844 if (WARN_ONCE(band != NL80211_BAND_2GHZ &&
845 band != NL80211_BAND_5GHZ &&
846 band != NL80211_BAND_6GHZ,
847 "Invalid band (%u)\n", band))
848 return 0;
849
850 link_rssi = MBM_TO_DBM(link_conf->bss->signal);
851 /*
852 * For 6 GHz the RSSI of the beacons is lower than
853 * the RSSI of the data.
854 */
855 if (band == NL80211_BAND_6GHZ && link_rssi)
856 link_rssi += 4;
857
858 rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1;
859
860 /* No valid RSSI - take the lowest grade */
861 if (!link_rssi)
862 link_rssi = rssi_to_grade_map[0].rssi[rssi_idx];
863
864 IWL_DEBUG_EHT(mld,
865 "Calculating grade of link %d: band = %d, bandwidth = %d, punctured subchannels =0x%x RSSI = %d\n",
866 link_conf->link_id, band,
867 link_conf->chanreq.oper.width,
868 link_conf->chanreq.oper.punctured, link_rssi);
869
870 /* Get grade based on RSSI */
871 for (int i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) {
872 const struct iwl_mld_rssi_to_grade *line =
873 &rssi_to_grade_map[i];
874
875 if (link_rssi > line->rssi[rssi_idx])
876 continue;
877 grade = line->grade;
878 break;
879 }
880
881 /* Apply the channel load and puncturing factors */
882 grade = grade * iwl_mld_get_avail_chan_load(mld, link_conf) / SCALE_FACTOR;
883 grade = grade * iwl_mld_get_n_subchannels(link_conf);
884
885 IWL_DEBUG_EHT(mld, "Link %d's grade: %d\n", link_conf->link_id, grade);
886
887 return grade;
888 }
889 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_link_grade);
890
iwl_mld_handle_beacon_filter_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)891 void iwl_mld_handle_beacon_filter_notif(struct iwl_mld *mld,
892 struct iwl_rx_packet *pkt)
893 {
894 const struct iwl_beacon_filter_notif *notif = (const void *)pkt->data;
895 u32 link_id = le32_to_cpu(notif->link_id);
896 struct ieee80211_bss_conf *link_conf =
897 iwl_mld_fw_id_to_link_conf(mld, link_id);
898 struct iwl_mld_link *mld_link;
899
900 if (IWL_FW_CHECK(mld, !link_conf, "invalid link ID %d\n", link_id))
901 return;
902
903 mld_link = iwl_mld_link_from_mac80211(link_conf);
904 if (WARN_ON_ONCE(!mld_link))
905 return;
906
907 mld_link->average_beacon_energy = le32_to_cpu(notif->average_energy);
908 }
909