1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3 * Copyright (C) 2024-2025 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
iwl_mld_sta_rx_bw_to_fw(enum ieee80211_sta_rx_bandwidth bw)245 static u8 iwl_mld_sta_rx_bw_to_fw(enum ieee80211_sta_rx_bandwidth bw)
246 {
247 switch (bw) {
248 default: /* potential future values not supported by this hw/driver */
249 case IEEE80211_STA_RX_BW_20:
250 return IWL_LINK_MODIFY_BW_20;
251 case IEEE80211_STA_RX_BW_40:
252 return IWL_LINK_MODIFY_BW_40;
253 case IEEE80211_STA_RX_BW_80:
254 return IWL_LINK_MODIFY_BW_80;
255 case IEEE80211_STA_RX_BW_160:
256 return IWL_LINK_MODIFY_BW_160;
257 case IEEE80211_STA_RX_BW_320:
258 return IWL_LINK_MODIFY_BW_320;
259 }
260 }
261
_iwl_mld_change_link_in_fw(struct iwl_mld * mld,struct ieee80211_bss_conf * link,enum ieee80211_sta_rx_bandwidth bw,u32 changes)262 static int _iwl_mld_change_link_in_fw(struct iwl_mld *mld,
263 struct ieee80211_bss_conf *link,
264 enum ieee80211_sta_rx_bandwidth bw,
265 u32 changes)
266 {
267 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
268 struct ieee80211_vif *vif = link->vif;
269 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
270 struct ieee80211_chanctx_conf *chan_ctx;
271 struct iwl_link_config_cmd cmd = {};
272 u32 flags = 0;
273
274 lockdep_assert_wiphy(mld->wiphy);
275
276 if (WARN_ON(!mld_link))
277 return -EINVAL;
278
279 cmd.link_id = cpu_to_le32(mld_link->fw_id);
280 cmd.spec_link_id = link->link_id;
281 cmd.mac_id = cpu_to_le32(mld_vif->fw_id);
282
283 chan_ctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx);
284
285 cmd.phy_id = cpu_to_le32(chan_ctx ?
286 iwl_mld_phy_from_mac80211(chan_ctx)->fw_id :
287 FW_CTXT_ID_INVALID);
288
289 ether_addr_copy(cmd.local_link_addr, link->addr);
290
291 cmd.active = cpu_to_le32(mld_link->active);
292
293 if ((changes & LINK_CONTEXT_MODIFY_ACTIVE) && !mld_link->active &&
294 mld_link->silent_deactivation) {
295 /* We are de-activating a link that is having CSA with
296 * immediate quiet in EMLSR. Tell the firmware not to send any
297 * frame.
298 */
299 cmd.block_tx = 1;
300 mld_link->silent_deactivation = false;
301 }
302
303 if (vif->type == NL80211_IFTYPE_ADHOC && link->bssid)
304 ether_addr_copy(cmd.ibss_bssid_addr, link->bssid);
305
306 /* Channel context is needed to get the rates */
307 if (chan_ctx)
308 iwl_mld_fill_rates(mld, link, chan_ctx, &cmd.cck_rates,
309 &cmd.ofdm_rates);
310
311 cmd.cck_short_preamble = cpu_to_le32(link->use_short_preamble);
312 cmd.short_slot = cpu_to_le32(link->use_short_slot);
313
314 iwl_mld_fill_protection_flags(mld, link, &cmd.protection_flags);
315
316 iwl_mld_fill_qos_params(link, cmd.ac, &cmd.qos_flags);
317
318 cmd.bi = cpu_to_le32(link->beacon_int);
319 cmd.dtim_interval = cpu_to_le32(link->beacon_int * link->dtim_period);
320
321 if (changes & LINK_CONTEXT_MODIFY_BANDWIDTH)
322 cmd.modify_bandwidth = iwl_mld_sta_rx_bw_to_fw(bw);
323
324 /* Configure HE parameters only if HE is supported, and only after
325 * the parameters are set in mac80211 (meaning after assoc)
326 */
327 if (!link->he_support || iwlwifi_mod_params.disable_11ax ||
328 (vif->type == NL80211_IFTYPE_STATION && !vif->cfg.assoc)) {
329 changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS;
330 goto send_cmd;
331 }
332
333 /* ap_sta may be NULL if we're disconnecting */
334 if (mld_vif->ap_sta) {
335 struct ieee80211_link_sta *link_sta =
336 link_sta_dereference_check(mld_vif->ap_sta,
337 link->link_id);
338
339 if (!WARN_ON(!link_sta) && link_sta->he_cap.has_he &&
340 link_sta->he_cap.he_cap_elem.mac_cap_info[5] &
341 IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX)
342 cmd.ul_mu_data_disable = 1;
343 }
344
345 cmd.htc_trig_based_pkt_ext = link->htc_trig_based_pkt_ext;
346
347 if (link->uora_exists) {
348 cmd.rand_alloc_ecwmin = link->uora_ocw_range & 0x7;
349 cmd.rand_alloc_ecwmax = (link->uora_ocw_range >> 3) & 0x7;
350 }
351
352 if (iwl_mld_fill_mu_edca(mld, mld_link, cmd.trig_based_txf))
353 flags |= LINK_FLG_MU_EDCA_CW;
354
355 cmd.bss_color = link->he_bss_color.color;
356
357 if (!link->he_bss_color.enabled)
358 flags |= LINK_FLG_BSS_COLOR_DIS;
359
360 cmd.frame_time_rts_th = cpu_to_le16(link->frame_time_rts_th);
361
362 /* Block 26-tone RU OFDMA transmissions */
363 if (mld_link->he_ru_2mhz_block)
364 flags |= LINK_FLG_RU_2MHZ_BLOCK;
365
366 if (link->nontransmitted) {
367 ether_addr_copy(cmd.ref_bssid_addr, link->transmitter_bssid);
368 cmd.bssid_index = link->bssid_index;
369 }
370
371 /* The only EHT parameter is puncturing, and starting from PHY cmd
372 * version 6 - it is sent there. For older versions of the PHY cmd,
373 * puncturing is not needed at all.
374 */
375 if (WARN_ON(changes & LINK_CONTEXT_MODIFY_EHT_PARAMS))
376 changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS;
377
378 send_cmd:
379 cmd.modify_mask = cpu_to_le32(changes);
380 cmd.flags = cpu_to_le32(flags);
381
382 return iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_MODIFY);
383 }
384
iwl_mld_change_link_in_fw(struct iwl_mld * mld,struct ieee80211_bss_conf * link,u32 changes)385 int iwl_mld_change_link_in_fw(struct iwl_mld *mld,
386 struct ieee80211_bss_conf *link,
387 u32 changes)
388 {
389 if (WARN_ON(changes & LINK_CONTEXT_MODIFY_BANDWIDTH))
390 changes &= ~LINK_CONTEXT_MODIFY_BANDWIDTH;
391
392 return _iwl_mld_change_link_in_fw(mld, link, 0, changes);
393 }
394
iwl_mld_change_link_omi_bw(struct iwl_mld * mld,struct ieee80211_bss_conf * link,enum ieee80211_sta_rx_bandwidth bw)395 int iwl_mld_change_link_omi_bw(struct iwl_mld *mld,
396 struct ieee80211_bss_conf *link,
397 enum ieee80211_sta_rx_bandwidth bw)
398 {
399 return _iwl_mld_change_link_in_fw(mld, link, bw,
400 LINK_CONTEXT_MODIFY_BANDWIDTH);
401 }
402
iwl_mld_activate_link(struct iwl_mld * mld,struct ieee80211_bss_conf * link)403 int iwl_mld_activate_link(struct iwl_mld *mld,
404 struct ieee80211_bss_conf *link)
405 {
406 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
407 int ret;
408
409 lockdep_assert_wiphy(mld->wiphy);
410
411 if (WARN_ON(!mld_link || mld_link->active))
412 return -EINVAL;
413
414 mld_link->rx_omi.exit_ts = jiffies;
415 mld_link->active = true;
416
417 ret = iwl_mld_change_link_in_fw(mld, link,
418 LINK_CONTEXT_MODIFY_ACTIVE);
419 if (ret)
420 mld_link->active = false;
421
422 return ret;
423 }
424
iwl_mld_deactivate_link(struct iwl_mld * mld,struct ieee80211_bss_conf * link)425 void iwl_mld_deactivate_link(struct iwl_mld *mld,
426 struct ieee80211_bss_conf *link)
427 {
428 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
429 struct iwl_probe_resp_data *probe_data;
430
431 lockdep_assert_wiphy(mld->wiphy);
432
433 if (WARN_ON(!mld_link || !mld_link->active))
434 return;
435
436 iwl_mld_cancel_session_protection(mld, link->vif, link->link_id);
437
438 /* If we deactivate the link, we will probably remove it, or switch
439 * channel. In both cases, the CSA or Notice of Absence information is
440 * now irrelevant. Remove the data here.
441 */
442 probe_data = wiphy_dereference(mld->wiphy, mld_link->probe_resp_data);
443 RCU_INIT_POINTER(mld_link->probe_resp_data, NULL);
444 if (probe_data)
445 kfree_rcu(probe_data, rcu_head);
446
447 mld_link->active = false;
448
449 iwl_mld_change_link_in_fw(mld, link, LINK_CONTEXT_MODIFY_ACTIVE);
450
451 /* Now that the link is not active in FW, we don't expect any new
452 * notifications for it. Cancel the ones that are already pending
453 */
454 iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_LINK,
455 mld_link->fw_id);
456 }
457
458 static void
iwl_mld_rm_link_from_fw(struct iwl_mld * mld,struct ieee80211_bss_conf * link)459 iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link)
460 {
461 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
462 struct iwl_link_config_cmd cmd = {};
463
464 lockdep_assert_wiphy(mld->wiphy);
465
466 if (WARN_ON(!mld_link))
467 return;
468
469 cmd.link_id = cpu_to_le32(mld_link->fw_id);
470 cmd.spec_link_id = link->link_id;
471 cmd.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID);
472
473 iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE);
474 }
475
iwl_mld_omi_bw_update(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf,struct iwl_mld_link * mld_link,struct ieee80211_link_sta * link_sta,enum ieee80211_sta_rx_bandwidth bw,bool ap_update)476 static void iwl_mld_omi_bw_update(struct iwl_mld *mld,
477 struct ieee80211_bss_conf *link_conf,
478 struct iwl_mld_link *mld_link,
479 struct ieee80211_link_sta *link_sta,
480 enum ieee80211_sta_rx_bandwidth bw,
481 bool ap_update)
482 {
483 enum ieee80211_sta_rx_bandwidth apply_bw;
484
485 mld_link->rx_omi.desired_bw = bw;
486
487 /* Can't update OMI while already in progress, desired_bw was
488 * set so on FW notification the worker will see the change
489 * and apply new the new desired bw.
490 */
491 if (mld_link->rx_omi.bw_in_progress)
492 return;
493
494 if (bw == IEEE80211_STA_RX_BW_MAX)
495 apply_bw = ieee80211_chan_width_to_rx_bw(link_conf->chanreq.oper.width);
496 else
497 apply_bw = bw;
498
499 if (!ap_update) {
500 /* The update isn't due to AP tracking after leaving OMI,
501 * where the AP could increase BW and then we must tell
502 * it that we can do the increased BW as well, if we did
503 * update the chandef.
504 * In this case, if we want MAX, then we will need to send
505 * a new OMI to the AP if it increases its own bandwidth as
506 * we can (due to internal and FW limitations, and being
507 * worried the AP might break) only send to what we're doing
508 * at the moment. In this case, set last_max_bw; otherwise
509 * if we really want to decrease our bandwidth set it to 0
510 * to indicate no updates are needed if the AP changes.
511 */
512 if (bw != IEEE80211_STA_RX_BW_MAX)
513 mld_link->rx_omi.last_max_bw = apply_bw;
514 else
515 mld_link->rx_omi.last_max_bw = 0;
516 } else {
517 /* Otherwise, if we're already trying to do maximum and
518 * the AP is changing, set last_max_bw to the new max the
519 * AP is using, we'll only get to this code path if the
520 * new bandwidth of the AP is bigger than what we sent it
521 * previously. This avoids repeatedly sending updates if
522 * it changes bandwidth, only doing it once on an increase.
523 */
524 mld_link->rx_omi.last_max_bw = apply_bw;
525 }
526
527 if (ieee80211_prepare_rx_omi_bw(link_sta, bw)) {
528 mld_link->rx_omi.bw_in_progress = apply_bw;
529 iwl_mld_change_link_omi_bw(mld, link_conf, apply_bw);
530 }
531 }
532
iwl_mld_omi_bw_finished_work(struct wiphy * wiphy,struct wiphy_work * work)533 static void iwl_mld_omi_bw_finished_work(struct wiphy *wiphy,
534 struct wiphy_work *work)
535 {
536 struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
537 struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
538 struct iwl_mld_link *mld_link =
539 container_of(work, typeof(*mld_link), rx_omi.finished_work.work);
540 enum ieee80211_sta_rx_bandwidth desired_bw, switched_to_bw;
541 struct ieee80211_vif *vif = mld_link->vif;
542 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
543 struct ieee80211_bss_conf *link_conf;
544 struct ieee80211_link_sta *link_sta;
545
546 if (!mld_vif->ap_sta)
547 return;
548
549 link_sta = wiphy_dereference(mld->wiphy,
550 mld_vif->ap_sta->link[mld_link->link_id]);
551 if (WARN_ON_ONCE(!link_sta))
552 return;
553
554 link_conf = link_conf_dereference_protected(vif, link_sta->link_id);
555 if (WARN_ON_ONCE(!link_conf))
556 return;
557
558 if (WARN_ON(!mld_link->rx_omi.bw_in_progress))
559 return;
560
561 desired_bw = mld_link->rx_omi.desired_bw;
562 switched_to_bw = mld_link->rx_omi.bw_in_progress;
563
564 ieee80211_finalize_rx_omi_bw(link_sta);
565 mld_link->rx_omi.bw_in_progress = 0;
566
567 if (desired_bw != switched_to_bw)
568 iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta,
569 desired_bw, false);
570 }
571
572 static struct ieee80211_vif *
iwl_mld_get_omi_bw_reduction_pointers(struct iwl_mld * mld,struct ieee80211_link_sta ** link_sta,struct iwl_mld_link ** mld_link)573 iwl_mld_get_omi_bw_reduction_pointers(struct iwl_mld *mld,
574 struct ieee80211_link_sta **link_sta,
575 struct iwl_mld_link **mld_link)
576 {
577 struct iwl_mld_vif *mld_vif;
578 struct ieee80211_vif *vif;
579 int n_link_stas = 0;
580
581 *link_sta = NULL;
582
583 if (mld->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_SC)
584 return NULL;
585
586 vif = iwl_mld_get_bss_vif(mld);
587 if (!vif)
588 return NULL;
589
590 for (int i = 0; i < ARRAY_SIZE(mld->fw_id_to_link_sta); i++) {
591 struct ieee80211_link_sta *tmp;
592
593 tmp = wiphy_dereference(mld->wiphy, mld->fw_id_to_link_sta[i]);
594 if (IS_ERR_OR_NULL(tmp))
595 continue;
596
597 n_link_stas++;
598 *link_sta = tmp;
599 }
600
601 /* can't do anything if we have TDLS peers or EMLSR */
602 if (n_link_stas != 1)
603 return NULL;
604
605 mld_vif = iwl_mld_vif_from_mac80211(vif);
606 *mld_link = iwl_mld_link_dereference_check(mld_vif,
607 (*link_sta)->link_id);
608 if (WARN_ON(!*mld_link))
609 return NULL;
610
611 return vif;
612 }
613
iwl_mld_omi_ap_changed_bw(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf,enum ieee80211_sta_rx_bandwidth bw)614 void iwl_mld_omi_ap_changed_bw(struct iwl_mld *mld,
615 struct ieee80211_bss_conf *link_conf,
616 enum ieee80211_sta_rx_bandwidth bw)
617 {
618 struct ieee80211_link_sta *link_sta;
619 struct iwl_mld_link *mld_link;
620 struct ieee80211_vif *vif;
621
622 vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link);
623 if (!vif)
624 return;
625
626 if (WARN_ON(link_conf->vif != vif))
627 return;
628
629 /* This is 0 if we requested an OMI BW reduction and don't want to
630 * be sending an OMI when the AP's bandwidth changes.
631 */
632 if (!mld_link->rx_omi.last_max_bw)
633 return;
634
635 /* We only need to tell the AP if it increases BW over what we last
636 * told it we were using, if it reduces then our last OMI to it will
637 * not get used anyway (e.g. we said we want 160 but it's doing 80.)
638 */
639 if (bw < mld_link->rx_omi.last_max_bw)
640 return;
641
642 iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, bw, true);
643 }
644
iwl_mld_handle_omi_status_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)645 void iwl_mld_handle_omi_status_notif(struct iwl_mld *mld,
646 struct iwl_rx_packet *pkt)
647 {
648 struct ieee80211_link_sta *link_sta;
649 struct iwl_mld_link *mld_link;
650 struct ieee80211_vif *vif;
651
652 vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link);
653 if (IWL_FW_CHECK(mld, !vif, "unexpected OMI notification\n"))
654 return;
655
656 if (IWL_FW_CHECK(mld, !mld_link->rx_omi.bw_in_progress,
657 "OMI notification when not requested\n"))
658 return;
659
660 wiphy_delayed_work_queue(mld->hw->wiphy,
661 &mld_link->rx_omi.finished_work,
662 msecs_to_jiffies(IWL_MLD_OMI_AP_SETTLE_DELAY));
663 }
664
iwl_mld_leave_omi_bw_reduction(struct iwl_mld * mld)665 void iwl_mld_leave_omi_bw_reduction(struct iwl_mld *mld)
666 {
667 struct ieee80211_bss_conf *link_conf;
668 struct ieee80211_link_sta *link_sta;
669 struct iwl_mld_link *mld_link;
670 struct ieee80211_vif *vif;
671
672 vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link);
673 if (!vif)
674 return;
675
676 link_conf = link_conf_dereference_protected(vif, link_sta->link_id);
677 if (WARN_ON_ONCE(!link_conf))
678 return;
679
680 if (!link_conf->he_support)
681 return;
682
683 mld_link->rx_omi.exit_ts = jiffies;
684
685 iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta,
686 IEEE80211_STA_RX_BW_MAX, false);
687 }
688
iwl_mld_check_omi_bw_reduction(struct iwl_mld * mld)689 void iwl_mld_check_omi_bw_reduction(struct iwl_mld *mld)
690 {
691 enum ieee80211_sta_rx_bandwidth bw = IEEE80211_STA_RX_BW_MAX;
692 struct ieee80211_chanctx_conf *chanctx;
693 struct ieee80211_bss_conf *link_conf;
694 struct ieee80211_link_sta *link_sta;
695 struct cfg80211_chan_def chandef;
696 struct iwl_mld_link *mld_link;
697 struct iwl_mld_vif *mld_vif;
698 struct ieee80211_vif *vif;
699 struct iwl_mld_phy *phy;
700 u16 punctured;
701 int exit_thr;
702
703 /* not allowed in CAM mode */
704 if (iwlmld_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
705 return;
706
707 /* must have one BSS connection (no P2P), no TDLS, nor EMLSR */
708 vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link);
709 if (!vif)
710 return;
711
712 link_conf = link_conf_dereference_protected(vif, link_sta->link_id);
713 if (WARN_ON_ONCE(!link_conf))
714 return;
715
716 if (!link_conf->he_support)
717 return;
718
719 chanctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx);
720 if (WARN_ON(!chanctx))
721 return;
722
723 mld_vif = iwl_mld_vif_from_mac80211(vif);
724 if (!mld_vif->authorized)
725 goto apply;
726
727 /* must not be in low-latency mode */
728 if (iwl_mld_vif_low_latency(mld_vif))
729 goto apply;
730
731 chandef = link_conf->chanreq.oper;
732
733 switch (chandef.width) {
734 case NL80211_CHAN_WIDTH_320:
735 exit_thr = IWL_MLD_OMI_EXIT_CHAN_LOAD_320;
736 break;
737 case NL80211_CHAN_WIDTH_160:
738 exit_thr = IWL_MLD_OMI_EXIT_CHAN_LOAD_160;
739 break;
740 default:
741 /* since we reduce to 80 MHz, must have more to start with */
742 goto apply;
743 }
744
745 /* not to be done if primary 80 MHz is punctured */
746 if (cfg80211_chandef_primary(&chandef, NL80211_CHAN_WIDTH_80,
747 &punctured) < 0 ||
748 punctured != 0)
749 goto apply;
750
751 phy = iwl_mld_phy_from_mac80211(chanctx);
752
753 if (phy->channel_load_by_us > exit_thr) {
754 /* send OMI for max bandwidth */
755 goto apply;
756 }
757
758 if (phy->channel_load_by_us > IWL_MLD_OMI_ENTER_CHAN_LOAD) {
759 /* no changes between enter/exit thresholds */
760 return;
761 }
762
763 if (time_is_after_jiffies(mld_link->rx_omi.exit_ts +
764 msecs_to_jiffies(IWL_MLD_OMI_EXIT_PROTECTION)))
765 return;
766
767 /* reduce bandwidth to 80 MHz to save power */
768 bw = IEEE80211_STA_RX_BW_80;
769 apply:
770 iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, bw, false);
771 }
772
IWL_MLD_ALLOC_FN(link,bss_conf)773 IWL_MLD_ALLOC_FN(link, bss_conf)
774
775 /* Constructor function for struct iwl_mld_link */
776 static int
777 iwl_mld_init_link(struct iwl_mld *mld, struct ieee80211_bss_conf *link,
778 struct iwl_mld_link *mld_link)
779 {
780 mld_link->vif = link->vif;
781 mld_link->link_id = link->link_id;
782
783 iwl_mld_init_internal_sta(&mld_link->bcast_sta);
784 iwl_mld_init_internal_sta(&mld_link->mcast_sta);
785 iwl_mld_init_internal_sta(&mld_link->aux_sta);
786
787 wiphy_delayed_work_init(&mld_link->rx_omi.finished_work,
788 iwl_mld_omi_bw_finished_work);
789
790 return iwl_mld_allocate_link_fw_id(mld, &mld_link->fw_id, link);
791 }
792
793 /* Initializes the link structure, maps fw id to the ieee80211_bss_conf, and
794 * adds a link to the fw
795 */
iwl_mld_add_link(struct iwl_mld * mld,struct ieee80211_bss_conf * bss_conf)796 int iwl_mld_add_link(struct iwl_mld *mld,
797 struct ieee80211_bss_conf *bss_conf)
798 {
799 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif);
800 struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf);
801 bool is_deflink = bss_conf == &bss_conf->vif->bss_conf;
802 int ret;
803
804 if (!link) {
805 if (is_deflink)
806 link = &mld_vif->deflink;
807 else
808 link = kzalloc(sizeof(*link), GFP_KERNEL);
809 } else {
810 WARN_ON(!mld->fw_status.in_hw_restart);
811 }
812
813 ret = iwl_mld_init_link(mld, bss_conf, link);
814 if (ret)
815 goto free;
816
817 rcu_assign_pointer(mld_vif->link[bss_conf->link_id], link);
818
819 ret = iwl_mld_add_link_to_fw(mld, bss_conf);
820 if (ret) {
821 RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL);
822 RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL);
823 goto free;
824 }
825
826 return ret;
827
828 free:
829 if (!is_deflink)
830 kfree(link);
831 return ret;
832 }
833
834 /* 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)835 void iwl_mld_remove_link(struct iwl_mld *mld,
836 struct ieee80211_bss_conf *bss_conf)
837 {
838 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif);
839 struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf);
840 bool is_deflink = link == &mld_vif->deflink;
841
842 if (WARN_ON(!link || link->active))
843 return;
844
845 iwl_mld_rm_link_from_fw(mld, bss_conf);
846 /* Continue cleanup on failure */
847
848 if (!is_deflink)
849 kfree_rcu(link, rcu_head);
850
851 RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL);
852
853 wiphy_delayed_work_cancel(mld->wiphy, &link->rx_omi.finished_work);
854
855 if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links))
856 return;
857
858 RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL);
859 }
860
iwl_mld_handle_missed_beacon_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)861 void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
862 struct iwl_rx_packet *pkt)
863 {
864 const struct iwl_missed_beacons_notif *notif = (const void *)pkt->data;
865 union iwl_dbg_tlv_tp_data tp_data = { .fw_pkt = pkt };
866 u32 link_id = le32_to_cpu(notif->link_id);
867 u32 missed_bcon = le32_to_cpu(notif->consec_missed_beacons);
868 u32 missed_bcon_since_rx =
869 le32_to_cpu(notif->consec_missed_beacons_since_last_rx);
870 u32 scnd_lnk_bcn_lost =
871 le32_to_cpu(notif->consec_missed_beacons_other_link);
872 struct ieee80211_bss_conf *link_conf =
873 iwl_mld_fw_id_to_link_conf(mld, link_id);
874 u32 bss_param_ch_cnt_link_id;
875 struct ieee80211_vif *vif;
876
877 if (WARN_ON(!link_conf))
878 return;
879
880 vif = link_conf->vif;
881 bss_param_ch_cnt_link_id = link_conf->bss_param_ch_cnt_link_id;
882
883 IWL_DEBUG_INFO(mld,
884 "missed bcn link_id=%u, %u consecutive=%u\n",
885 link_id, missed_bcon, missed_bcon_since_rx);
886
887 if (WARN_ON(!vif))
888 return;
889
890 mld->trans->dbg.dump_file_name_ext_valid = true;
891 snprintf(mld->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME,
892 "LinkId_%d_MacType_%d", link_id,
893 iwl_mld_mac80211_iftype_to_fw(vif));
894
895 iwl_dbg_tlv_time_point(&mld->fwrt,
896 IWL_FW_INI_TIME_POINT_MISSED_BEACONS, &tp_data);
897
898 if (missed_bcon >= IWL_MLD_MISSED_BEACONS_THRESHOLD_LONG) {
899 if (missed_bcon_since_rx >=
900 IWL_MLD_MISSED_BEACONS_SINCE_RX_THOLD) {
901 ieee80211_connection_loss(vif);
902 return;
903 }
904 IWL_WARN(mld,
905 "missed beacons exceeds threshold, but receiving data. Stay connected, Expect bugs.\n");
906 return;
907 }
908
909 if (missed_bcon_since_rx > IWL_MLD_MISSED_BEACONS_THRESHOLD) {
910 ieee80211_cqm_beacon_loss_notify(vif, GFP_ATOMIC);
911
912 /* try to switch links, no-op if we don't have MLO */
913 iwl_mld_int_mlo_scan(mld, vif);
914 }
915
916 /* no more logic if we're not in EMLSR */
917 if (hweight16(vif->active_links) <= 1)
918 return;
919
920 /* We are processing a notification before link activation */
921 if (le32_to_cpu(notif->other_link_id) == FW_CTXT_ID_INVALID)
922 return;
923
924 /* Exit EMLSR if we lost more than
925 * IWL_MLD_MISSED_BEACONS_EXIT_ESR_THRESH beacons on boths links
926 * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH on current link.
927 * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED
928 * on current link and the link's bss_param_ch_count has changed on
929 * the other link's beacon.
930 */
931 if ((missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS &&
932 scnd_lnk_bcn_lost >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) ||
933 missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH ||
934 (bss_param_ch_cnt_link_id != link_id &&
935 missed_bcon >=
936 IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED)) {
937 iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_MISSED_BEACON,
938 iwl_mld_get_primary_link(vif));
939 }
940 }
941 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_handle_missed_beacon_notif);
942
iwl_mld_cancel_missed_beacon_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt,u32 removed_link_id)943 bool iwl_mld_cancel_missed_beacon_notif(struct iwl_mld *mld,
944 struct iwl_rx_packet *pkt,
945 u32 removed_link_id)
946 {
947 struct iwl_missed_beacons_notif *notif = (void *)pkt->data;
948
949 if (le32_to_cpu(notif->other_link_id) == removed_link_id) {
950 /* Second link is being removed. Don't cancel the notification,
951 * but mark second link as invalid.
952 */
953 notif->other_link_id = cpu_to_le32(FW_CTXT_ID_INVALID);
954 }
955
956 /* If the primary link is removed, cancel the notification */
957 return le32_to_cpu(notif->link_id) == removed_link_id;
958 }
959
iwl_mld_link_set_associated(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link)960 int iwl_mld_link_set_associated(struct iwl_mld *mld, struct ieee80211_vif *vif,
961 struct ieee80211_bss_conf *link)
962 {
963 return iwl_mld_change_link_in_fw(mld, link, LINK_CONTEXT_MODIFY_ALL &
964 ~(LINK_CONTEXT_MODIFY_ACTIVE |
965 LINK_CONTEXT_MODIFY_EHT_PARAMS));
966 }
967
968 struct iwl_mld_rssi_to_grade {
969 s8 rssi[2];
970 u16 grade;
971 };
972
973 #define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \
974 { \
975 .rssi = {_lb, _hb_uhb}, \
976 .grade = _grade \
977 }
978
979 /*
980 * This array must be sorted by increasing RSSI for proper functionality.
981 * The grades are actually estimated throughput, represented as fixed-point
982 * with a scale factor of 1/10.
983 */
984 static const struct iwl_mld_rssi_to_grade rssi_to_grade_map[] = {
985 RSSI_TO_GRADE_LINE(-85, -89, 172),
986 RSSI_TO_GRADE_LINE(-83, -86, 344),
987 RSSI_TO_GRADE_LINE(-82, -85, 516),
988 RSSI_TO_GRADE_LINE(-80, -83, 688),
989 RSSI_TO_GRADE_LINE(-77, -79, 1032),
990 RSSI_TO_GRADE_LINE(-73, -76, 1376),
991 RSSI_TO_GRADE_LINE(-70, -74, 1548),
992 RSSI_TO_GRADE_LINE(-69, -72, 1720),
993 RSSI_TO_GRADE_LINE(-65, -68, 2064),
994 RSSI_TO_GRADE_LINE(-61, -66, 2294),
995 RSSI_TO_GRADE_LINE(-58, -61, 2580),
996 RSSI_TO_GRADE_LINE(-55, -58, 2868),
997 RSSI_TO_GRADE_LINE(-46, -55, 3098),
998 RSSI_TO_GRADE_LINE(-43, -54, 3442)
999 };
1000
1001 #define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade)
1002
1003 #define DEFAULT_CHAN_LOAD_2GHZ 30
1004 #define DEFAULT_CHAN_LOAD_5GHZ 15
1005 #define DEFAULT_CHAN_LOAD_6GHZ 0
1006
1007 /* Factors calculation is done with fixed-point with a scaling factor of 1/256 */
1008 #define SCALE_FACTOR 256
1009 #define MAX_CHAN_LOAD 256
1010
1011 static unsigned int
iwl_mld_get_n_subchannels(const struct ieee80211_bss_conf * link_conf)1012 iwl_mld_get_n_subchannels(const struct ieee80211_bss_conf *link_conf)
1013 {
1014 enum nl80211_chan_width chan_width =
1015 link_conf->chanreq.oper.width;
1016 int mhz = nl80211_chan_width_to_mhz(chan_width);
1017 unsigned int n_subchannels;
1018
1019 if (WARN_ONCE(mhz < 20 || mhz > 320,
1020 "Invalid channel width : (%d)\n", mhz))
1021 return 1;
1022
1023 /* total number of subchannels */
1024 n_subchannels = mhz / 20;
1025
1026 /* No puncturing if less than 80 MHz */
1027 if (mhz >= 80)
1028 n_subchannels -= hweight16(link_conf->chanreq.oper.punctured);
1029
1030 return n_subchannels;
1031 }
1032
1033 static int
iwl_mld_get_chan_load_from_element(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf)1034 iwl_mld_get_chan_load_from_element(struct iwl_mld *mld,
1035 struct ieee80211_bss_conf *link_conf)
1036 {
1037 struct ieee80211_vif *vif = link_conf->vif;
1038 const struct cfg80211_bss_ies *ies;
1039 const struct element *bss_load_elem = NULL;
1040 const struct ieee80211_bss_load_elem *bss_load;
1041
1042 guard(rcu)();
1043
1044 if (ieee80211_vif_link_active(vif, link_conf->link_id))
1045 ies = rcu_dereference(link_conf->bss->beacon_ies);
1046 else
1047 ies = rcu_dereference(link_conf->bss->ies);
1048
1049 if (ies)
1050 bss_load_elem = cfg80211_find_elem(WLAN_EID_QBSS_LOAD,
1051 ies->data, ies->len);
1052
1053 if (!bss_load_elem ||
1054 bss_load_elem->datalen != sizeof(*bss_load))
1055 return -EINVAL;
1056
1057 bss_load = (const void *)bss_load_elem->data;
1058
1059 return bss_load->channel_util;
1060 }
1061
1062 static unsigned int
iwl_mld_get_chan_load_by_us(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf,bool expect_active_link)1063 iwl_mld_get_chan_load_by_us(struct iwl_mld *mld,
1064 struct ieee80211_bss_conf *link_conf,
1065 bool expect_active_link)
1066 {
1067 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
1068 struct ieee80211_chanctx_conf *chan_ctx;
1069 struct iwl_mld_phy *phy;
1070
1071 if (!mld_link || !mld_link->active) {
1072 WARN_ON(expect_active_link);
1073 return 0;
1074 }
1075
1076 if (WARN_ONCE(!rcu_access_pointer(mld_link->chan_ctx),
1077 "Active link (%u) without channel ctxt assigned!\n",
1078 link_conf->link_id))
1079 return 0;
1080
1081 chan_ctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx);
1082 phy = iwl_mld_phy_from_mac80211(chan_ctx);
1083
1084 return phy->channel_load_by_us;
1085 }
1086
1087 /* 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)1088 int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld,
1089 struct ieee80211_bss_conf *link_conf,
1090 bool expect_active_link)
1091 {
1092 int chan_load;
1093 unsigned int chan_load_by_us;
1094
1095 /* get overall load */
1096 chan_load = iwl_mld_get_chan_load_from_element(mld, link_conf);
1097 if (chan_load < 0)
1098 return chan_load;
1099
1100 chan_load_by_us = iwl_mld_get_chan_load_by_us(mld, link_conf,
1101 expect_active_link);
1102
1103 /* channel load by us is given in percentage */
1104 chan_load_by_us =
1105 NORMALIZE_PERCENT_TO_255(chan_load_by_us);
1106
1107 /* Use only values that firmware sends that can possibly be valid */
1108 if (chan_load_by_us <= chan_load)
1109 chan_load -= chan_load_by_us;
1110
1111 return chan_load;
1112 }
1113
1114 static unsigned int
iwl_mld_get_default_chan_load(struct ieee80211_bss_conf * link_conf)1115 iwl_mld_get_default_chan_load(struct ieee80211_bss_conf *link_conf)
1116 {
1117 enum nl80211_band band = link_conf->chanreq.oper.chan->band;
1118
1119 switch (band) {
1120 case NL80211_BAND_2GHZ:
1121 return DEFAULT_CHAN_LOAD_2GHZ;
1122 case NL80211_BAND_5GHZ:
1123 return DEFAULT_CHAN_LOAD_5GHZ;
1124 case NL80211_BAND_6GHZ:
1125 return DEFAULT_CHAN_LOAD_6GHZ;
1126 default:
1127 WARN_ON(1);
1128 return 0;
1129 }
1130 }
1131
iwl_mld_get_chan_load(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf)1132 unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld,
1133 struct ieee80211_bss_conf *link_conf)
1134 {
1135 int chan_load;
1136
1137 chan_load = iwl_mld_get_chan_load_by_others(mld, link_conf, false);
1138 if (chan_load >= 0)
1139 return chan_load;
1140
1141 /* No information from the element, take the defaults */
1142 chan_load = iwl_mld_get_default_chan_load(link_conf);
1143
1144 /* The defaults are given in percentage */
1145 return NORMALIZE_PERCENT_TO_255(chan_load);
1146 }
1147
1148 static unsigned int
iwl_mld_get_avail_chan_load(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf)1149 iwl_mld_get_avail_chan_load(struct iwl_mld *mld,
1150 struct ieee80211_bss_conf *link_conf)
1151 {
1152 return MAX_CHAN_LOAD - iwl_mld_get_chan_load(mld, link_conf);
1153 }
1154
1155 /* 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)1156 unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
1157 struct ieee80211_bss_conf *link_conf)
1158 {
1159 enum nl80211_band band;
1160 int rssi_idx;
1161 s32 link_rssi;
1162 unsigned int grade = MAX_GRADE;
1163
1164 if (WARN_ON_ONCE(!link_conf))
1165 return 0;
1166
1167 band = link_conf->chanreq.oper.chan->band;
1168 if (WARN_ONCE(band != NL80211_BAND_2GHZ &&
1169 band != NL80211_BAND_5GHZ &&
1170 band != NL80211_BAND_6GHZ,
1171 "Invalid band (%u)\n", band))
1172 return 0;
1173
1174 link_rssi = MBM_TO_DBM(link_conf->bss->signal);
1175 /*
1176 * For 6 GHz the RSSI of the beacons is lower than
1177 * the RSSI of the data.
1178 */
1179 if (band == NL80211_BAND_6GHZ && link_rssi)
1180 link_rssi += 4;
1181
1182 rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1;
1183
1184 /* No valid RSSI - take the lowest grade */
1185 if (!link_rssi)
1186 link_rssi = rssi_to_grade_map[0].rssi[rssi_idx];
1187
1188 IWL_DEBUG_EHT(mld,
1189 "Calculating grade of link %d: band = %d, bandwidth = %d, punctured subchannels =0x%x RSSI = %d\n",
1190 link_conf->link_id, band,
1191 link_conf->chanreq.oper.width,
1192 link_conf->chanreq.oper.punctured, link_rssi);
1193
1194 /* Get grade based on RSSI */
1195 for (int i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) {
1196 const struct iwl_mld_rssi_to_grade *line =
1197 &rssi_to_grade_map[i];
1198
1199 if (link_rssi > line->rssi[rssi_idx])
1200 continue;
1201 grade = line->grade;
1202 break;
1203 }
1204
1205 /* Apply the channel load and puncturing factors */
1206 grade = grade * iwl_mld_get_avail_chan_load(mld, link_conf) / SCALE_FACTOR;
1207 grade = grade * iwl_mld_get_n_subchannels(link_conf);
1208
1209 IWL_DEBUG_EHT(mld, "Link %d's grade: %d\n", link_conf->link_id, grade);
1210
1211 return grade;
1212 }
1213 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_link_grade);
1214