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 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 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 */ 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 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 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 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 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 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 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 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 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 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 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(mld_link->vif); 408 int ret; 409 410 lockdep_assert_wiphy(mld->wiphy); 411 412 if (WARN_ON(!mld_link || mld_link->active)) 413 return -EINVAL; 414 415 mld_link->rx_omi.exit_ts = jiffies; 416 mld_link->active = true; 417 418 ret = iwl_mld_change_link_in_fw(mld, link, 419 LINK_CONTEXT_MODIFY_ACTIVE); 420 if (ret) 421 mld_link->active = false; 422 else 423 mld_vif->last_link_activation_time = 424 ktime_get_boottime_seconds(); 425 426 return ret; 427 } 428 429 void iwl_mld_deactivate_link(struct iwl_mld *mld, 430 struct ieee80211_bss_conf *link) 431 { 432 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); 433 struct iwl_probe_resp_data *probe_data; 434 435 lockdep_assert_wiphy(mld->wiphy); 436 437 if (WARN_ON(!mld_link || !mld_link->active)) 438 return; 439 440 iwl_mld_cancel_session_protection(mld, link->vif, link->link_id); 441 442 /* If we deactivate the link, we will probably remove it, or switch 443 * channel. In both cases, the CSA or Notice of Absence information is 444 * now irrelevant. Remove the data here. 445 */ 446 probe_data = wiphy_dereference(mld->wiphy, mld_link->probe_resp_data); 447 RCU_INIT_POINTER(mld_link->probe_resp_data, NULL); 448 if (probe_data) 449 kfree_rcu(probe_data, rcu_head); 450 451 mld_link->active = false; 452 453 iwl_mld_change_link_in_fw(mld, link, LINK_CONTEXT_MODIFY_ACTIVE); 454 455 /* Now that the link is not active in FW, we don't expect any new 456 * notifications for it. Cancel the ones that are already pending 457 */ 458 iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_LINK, 459 mld_link->fw_id); 460 } 461 462 static void 463 iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link) 464 { 465 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); 466 struct iwl_link_config_cmd cmd = {}; 467 468 lockdep_assert_wiphy(mld->wiphy); 469 470 if (WARN_ON(!mld_link)) 471 return; 472 473 cmd.link_id = cpu_to_le32(mld_link->fw_id); 474 cmd.spec_link_id = link->link_id; 475 cmd.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID); 476 477 iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE); 478 } 479 480 static void iwl_mld_omi_bw_update(struct iwl_mld *mld, 481 struct ieee80211_bss_conf *link_conf, 482 struct iwl_mld_link *mld_link, 483 struct ieee80211_link_sta *link_sta, 484 enum ieee80211_sta_rx_bandwidth bw, 485 bool ap_update) 486 { 487 enum ieee80211_sta_rx_bandwidth apply_bw; 488 489 mld_link->rx_omi.desired_bw = bw; 490 491 /* Can't update OMI while already in progress, desired_bw was 492 * set so on FW notification the worker will see the change 493 * and apply new the new desired bw. 494 */ 495 if (mld_link->rx_omi.bw_in_progress) 496 return; 497 498 if (bw == IEEE80211_STA_RX_BW_MAX) 499 apply_bw = ieee80211_chan_width_to_rx_bw(link_conf->chanreq.oper.width); 500 else 501 apply_bw = bw; 502 503 if (!ap_update) { 504 /* The update isn't due to AP tracking after leaving OMI, 505 * where the AP could increase BW and then we must tell 506 * it that we can do the increased BW as well, if we did 507 * update the chandef. 508 * In this case, if we want MAX, then we will need to send 509 * a new OMI to the AP if it increases its own bandwidth as 510 * we can (due to internal and FW limitations, and being 511 * worried the AP might break) only send to what we're doing 512 * at the moment. In this case, set last_max_bw; otherwise 513 * if we really want to decrease our bandwidth set it to 0 514 * to indicate no updates are needed if the AP changes. 515 */ 516 if (bw != IEEE80211_STA_RX_BW_MAX) 517 mld_link->rx_omi.last_max_bw = apply_bw; 518 else 519 mld_link->rx_omi.last_max_bw = 0; 520 } else { 521 /* Otherwise, if we're already trying to do maximum and 522 * the AP is changing, set last_max_bw to the new max the 523 * AP is using, we'll only get to this code path if the 524 * new bandwidth of the AP is bigger than what we sent it 525 * previously. This avoids repeatedly sending updates if 526 * it changes bandwidth, only doing it once on an increase. 527 */ 528 mld_link->rx_omi.last_max_bw = apply_bw; 529 } 530 531 if (ieee80211_prepare_rx_omi_bw(link_sta, bw)) { 532 mld_link->rx_omi.bw_in_progress = apply_bw; 533 iwl_mld_change_link_omi_bw(mld, link_conf, apply_bw); 534 } 535 } 536 537 static void iwl_mld_omi_bw_finished_work(struct wiphy *wiphy, 538 struct wiphy_work *work) 539 { 540 struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); 541 struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 542 struct iwl_mld_link *mld_link = 543 container_of(work, typeof(*mld_link), rx_omi.finished_work.work); 544 enum ieee80211_sta_rx_bandwidth desired_bw, switched_to_bw; 545 struct ieee80211_vif *vif = mld_link->vif; 546 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 547 struct ieee80211_bss_conf *link_conf; 548 struct ieee80211_link_sta *link_sta; 549 550 if (!mld_vif->ap_sta) 551 return; 552 553 link_sta = wiphy_dereference(mld->wiphy, 554 mld_vif->ap_sta->link[mld_link->link_id]); 555 if (WARN_ON_ONCE(!link_sta)) 556 return; 557 558 link_conf = link_conf_dereference_protected(vif, link_sta->link_id); 559 if (WARN_ON_ONCE(!link_conf)) 560 return; 561 562 if (WARN_ON(!mld_link->rx_omi.bw_in_progress)) 563 return; 564 565 desired_bw = mld_link->rx_omi.desired_bw; 566 switched_to_bw = mld_link->rx_omi.bw_in_progress; 567 568 ieee80211_finalize_rx_omi_bw(link_sta); 569 mld_link->rx_omi.bw_in_progress = 0; 570 571 if (desired_bw != switched_to_bw) 572 iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, 573 desired_bw, false); 574 } 575 576 static struct ieee80211_vif * 577 iwl_mld_get_omi_bw_reduction_pointers(struct iwl_mld *mld, 578 struct ieee80211_link_sta **link_sta, 579 struct iwl_mld_link **mld_link) 580 { 581 struct iwl_mld_vif *mld_vif; 582 struct ieee80211_vif *vif; 583 int n_link_stas = 0; 584 585 *link_sta = NULL; 586 587 if (mld->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC) 588 return NULL; 589 590 vif = iwl_mld_get_bss_vif(mld); 591 if (!vif) 592 return NULL; 593 594 for (int i = 0; i < ARRAY_SIZE(mld->fw_id_to_link_sta); i++) { 595 struct ieee80211_link_sta *tmp; 596 597 tmp = wiphy_dereference(mld->wiphy, mld->fw_id_to_link_sta[i]); 598 if (IS_ERR_OR_NULL(tmp)) 599 continue; 600 601 n_link_stas++; 602 *link_sta = tmp; 603 } 604 605 /* can't do anything if we have TDLS peers or EMLSR */ 606 if (n_link_stas != 1) 607 return NULL; 608 609 mld_vif = iwl_mld_vif_from_mac80211(vif); 610 *mld_link = iwl_mld_link_dereference_check(mld_vif, 611 (*link_sta)->link_id); 612 if (WARN_ON(!*mld_link)) 613 return NULL; 614 615 return vif; 616 } 617 618 void iwl_mld_omi_ap_changed_bw(struct iwl_mld *mld, 619 struct ieee80211_bss_conf *link_conf, 620 enum ieee80211_sta_rx_bandwidth bw) 621 { 622 struct ieee80211_link_sta *link_sta; 623 struct iwl_mld_link *mld_link; 624 struct ieee80211_vif *vif; 625 626 vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); 627 if (!vif) 628 return; 629 630 if (WARN_ON(link_conf->vif != vif)) 631 return; 632 633 /* This is 0 if we requested an OMI BW reduction and don't want to 634 * be sending an OMI when the AP's bandwidth changes. 635 */ 636 if (!mld_link->rx_omi.last_max_bw) 637 return; 638 639 /* We only need to tell the AP if it increases BW over what we last 640 * told it we were using, if it reduces then our last OMI to it will 641 * not get used anyway (e.g. we said we want 160 but it's doing 80.) 642 */ 643 if (bw < mld_link->rx_omi.last_max_bw) 644 return; 645 646 iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, bw, true); 647 } 648 649 void iwl_mld_handle_omi_status_notif(struct iwl_mld *mld, 650 struct iwl_rx_packet *pkt) 651 { 652 int ver = iwl_fw_lookup_notif_ver(mld->fw, DATA_PATH_GROUP, 653 OMI_SEND_STATUS_NOTIF, 1); 654 struct ieee80211_link_sta *link_sta; 655 struct iwl_mld_link *mld_link; 656 struct ieee80211_vif *vif; 657 658 if (ver == 2) { 659 const struct iwl_omi_send_status_notif *notif = 660 (const void *)pkt->data; 661 u32 sta_id = le32_to_cpu(notif->sta_id); 662 struct iwl_mld_vif *mld_vif; 663 664 if (IWL_FW_CHECK(mld, sta_id >= mld->fw->ucode_capa.num_stations, 665 "Invalid station %d\n", sta_id)) 666 return; 667 668 link_sta = wiphy_dereference(mld->wiphy, 669 mld->fw_id_to_link_sta[sta_id]); 670 if (IWL_FW_CHECK(mld, !link_sta, "Station does not exist\n")) 671 return; 672 673 vif = iwl_mld_sta_from_mac80211(link_sta->sta)->vif; 674 mld_vif = iwl_mld_vif_from_mac80211(vif); 675 676 mld_link = iwl_mld_link_dereference_check(mld_vif, 677 link_sta->link_id); 678 if (WARN(!mld_link, "Link %d does not exist\n", 679 link_sta->link_id)) 680 return; 681 } else { 682 vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, 683 &mld_link); 684 } 685 if (IWL_FW_CHECK(mld, !vif, "unexpected OMI notification\n")) 686 return; 687 688 if (IWL_FW_CHECK(mld, !mld_link->rx_omi.bw_in_progress, 689 "OMI notification when not requested\n")) 690 return; 691 692 wiphy_delayed_work_queue(mld->hw->wiphy, 693 &mld_link->rx_omi.finished_work, 694 msecs_to_jiffies(IWL_MLD_OMI_AP_SETTLE_DELAY)); 695 } 696 697 void iwl_mld_leave_omi_bw_reduction(struct iwl_mld *mld) 698 { 699 struct ieee80211_bss_conf *link_conf; 700 struct ieee80211_link_sta *link_sta; 701 struct iwl_mld_link *mld_link; 702 struct ieee80211_vif *vif; 703 704 vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); 705 if (!vif) 706 return; 707 708 link_conf = link_conf_dereference_protected(vif, link_sta->link_id); 709 if (WARN_ON_ONCE(!link_conf)) 710 return; 711 712 if (!link_conf->he_support) 713 return; 714 715 mld_link->rx_omi.exit_ts = jiffies; 716 717 iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, 718 IEEE80211_STA_RX_BW_MAX, false); 719 } 720 721 void iwl_mld_check_omi_bw_reduction(struct iwl_mld *mld) 722 { 723 enum ieee80211_sta_rx_bandwidth bw = IEEE80211_STA_RX_BW_MAX; 724 struct ieee80211_chanctx_conf *chanctx; 725 struct ieee80211_bss_conf *link_conf; 726 struct ieee80211_link_sta *link_sta; 727 struct cfg80211_chan_def chandef; 728 struct iwl_mld_link *mld_link; 729 struct iwl_mld_vif *mld_vif; 730 struct ieee80211_vif *vif; 731 struct iwl_mld_phy *phy; 732 u16 punctured; 733 int exit_thr; 734 735 /* not allowed in CAM mode */ 736 if (iwlmld_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) 737 return; 738 739 /* must have one BSS connection (no P2P), no TDLS, nor EMLSR */ 740 vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); 741 if (!vif) 742 return; 743 744 link_conf = link_conf_dereference_protected(vif, link_sta->link_id); 745 if (WARN_ON_ONCE(!link_conf)) 746 return; 747 748 if (!link_conf->he_support) 749 return; 750 751 chanctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx); 752 if (WARN_ON(!chanctx)) 753 return; 754 755 mld_vif = iwl_mld_vif_from_mac80211(vif); 756 if (!mld_vif->authorized) 757 goto apply; 758 759 /* must not be in low-latency mode */ 760 if (iwl_mld_vif_low_latency(mld_vif)) 761 goto apply; 762 763 chandef = link_conf->chanreq.oper; 764 765 switch (chandef.width) { 766 case NL80211_CHAN_WIDTH_320: 767 exit_thr = IWL_MLD_OMI_EXIT_CHAN_LOAD_320; 768 break; 769 case NL80211_CHAN_WIDTH_160: 770 exit_thr = IWL_MLD_OMI_EXIT_CHAN_LOAD_160; 771 break; 772 default: 773 /* since we reduce to 80 MHz, must have more to start with */ 774 goto apply; 775 } 776 777 /* not to be done if primary 80 MHz is punctured */ 778 if (cfg80211_chandef_primary(&chandef, NL80211_CHAN_WIDTH_80, 779 &punctured) < 0 || 780 punctured != 0) 781 goto apply; 782 783 phy = iwl_mld_phy_from_mac80211(chanctx); 784 785 if (phy->channel_load_by_us > exit_thr) { 786 /* send OMI for max bandwidth */ 787 goto apply; 788 } 789 790 if (phy->channel_load_by_us > IWL_MLD_OMI_ENTER_CHAN_LOAD) { 791 /* no changes between enter/exit thresholds */ 792 return; 793 } 794 795 if (time_is_after_jiffies(mld_link->rx_omi.exit_ts + 796 msecs_to_jiffies(IWL_MLD_OMI_EXIT_PROTECTION))) 797 return; 798 799 /* reduce bandwidth to 80 MHz to save power */ 800 bw = IEEE80211_STA_RX_BW_80; 801 apply: 802 iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, bw, false); 803 } 804 805 IWL_MLD_ALLOC_FN(link, bss_conf) 806 807 /* Constructor function for struct iwl_mld_link */ 808 static int 809 iwl_mld_init_link(struct iwl_mld *mld, struct ieee80211_bss_conf *link, 810 struct iwl_mld_link *mld_link) 811 { 812 mld_link->vif = link->vif; 813 mld_link->link_id = link->link_id; 814 mld_link->average_beacon_energy = 0; 815 816 iwl_mld_init_internal_sta(&mld_link->bcast_sta); 817 iwl_mld_init_internal_sta(&mld_link->mcast_sta); 818 iwl_mld_init_internal_sta(&mld_link->mon_sta); 819 820 if (!mld->fw_status.in_hw_restart) 821 wiphy_delayed_work_init(&mld_link->rx_omi.finished_work, 822 iwl_mld_omi_bw_finished_work); 823 824 return iwl_mld_allocate_link_fw_id(mld, &mld_link->fw_id, link); 825 } 826 827 /* Initializes the link structure, maps fw id to the ieee80211_bss_conf, and 828 * adds a link to the fw 829 */ 830 int iwl_mld_add_link(struct iwl_mld *mld, 831 struct ieee80211_bss_conf *bss_conf) 832 { 833 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif); 834 struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf); 835 bool is_deflink = bss_conf == &bss_conf->vif->bss_conf; 836 int ret; 837 838 if (!link) { 839 if (is_deflink) 840 link = &mld_vif->deflink; 841 else 842 link = kzalloc(sizeof(*link), GFP_KERNEL); 843 } else { 844 WARN_ON(!mld->fw_status.in_hw_restart); 845 } 846 847 ret = iwl_mld_init_link(mld, bss_conf, link); 848 if (ret) 849 goto free; 850 851 rcu_assign_pointer(mld_vif->link[bss_conf->link_id], link); 852 853 ret = iwl_mld_add_link_to_fw(mld, bss_conf); 854 if (ret) { 855 RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL); 856 RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL); 857 goto free; 858 } 859 860 return ret; 861 862 free: 863 if (!is_deflink) 864 kfree(link); 865 return ret; 866 } 867 868 /* Remove link from fw, unmap the bss_conf, and destroy the link structure */ 869 void iwl_mld_remove_link(struct iwl_mld *mld, 870 struct ieee80211_bss_conf *bss_conf) 871 { 872 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif); 873 struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf); 874 bool is_deflink = link == &mld_vif->deflink; 875 876 if (WARN_ON(!link || link->active)) 877 return; 878 879 iwl_mld_rm_link_from_fw(mld, bss_conf); 880 /* Continue cleanup on failure */ 881 882 if (!is_deflink) 883 kfree_rcu(link, rcu_head); 884 885 RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL); 886 887 wiphy_delayed_work_cancel(mld->wiphy, &link->rx_omi.finished_work); 888 889 if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links)) 890 return; 891 892 RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL); 893 } 894 895 void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, 896 struct iwl_rx_packet *pkt) 897 { 898 const struct iwl_missed_beacons_notif *notif = (const void *)pkt->data; 899 union iwl_dbg_tlv_tp_data tp_data = { .fw_pkt = pkt }; 900 u32 link_id = le32_to_cpu(notif->link_id); 901 u32 missed_bcon = le32_to_cpu(notif->consec_missed_beacons); 902 u32 missed_bcon_since_rx = 903 le32_to_cpu(notif->consec_missed_beacons_since_last_rx); 904 u32 scnd_lnk_bcn_lost = 905 le32_to_cpu(notif->consec_missed_beacons_other_link); 906 struct ieee80211_bss_conf *link_conf = 907 iwl_mld_fw_id_to_link_conf(mld, link_id); 908 u32 bss_param_ch_cnt_link_id; 909 struct ieee80211_vif *vif; 910 911 if (WARN_ON(!link_conf)) 912 return; 913 914 vif = link_conf->vif; 915 bss_param_ch_cnt_link_id = link_conf->bss_param_ch_cnt_link_id; 916 917 IWL_DEBUG_INFO(mld, 918 "missed bcn link_id=%u, %u consecutive=%u\n", 919 link_id, missed_bcon, missed_bcon_since_rx); 920 921 if (WARN_ON(!vif)) 922 return; 923 924 mld->trans->dbg.dump_file_name_ext_valid = true; 925 snprintf(mld->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, 926 "LinkId_%d_MacType_%d", link_id, 927 iwl_mld_mac80211_iftype_to_fw(vif)); 928 929 iwl_dbg_tlv_time_point(&mld->fwrt, 930 IWL_FW_INI_TIME_POINT_MISSED_BEACONS, &tp_data); 931 932 if (missed_bcon >= IWL_MLD_MISSED_BEACONS_THRESHOLD_LONG) { 933 if (missed_bcon_since_rx >= 934 IWL_MLD_MISSED_BEACONS_SINCE_RX_THOLD) { 935 ieee80211_connection_loss(vif); 936 return; 937 } 938 IWL_WARN(mld, 939 "missed beacons exceeds threshold, but receiving data. Stay connected, Expect bugs.\n"); 940 return; 941 } 942 943 if (missed_bcon_since_rx > IWL_MLD_MISSED_BEACONS_THRESHOLD) { 944 ieee80211_cqm_beacon_loss_notify(vif, GFP_ATOMIC); 945 946 /* try to switch links, no-op if we don't have MLO */ 947 iwl_mld_int_mlo_scan(mld, vif); 948 } 949 950 /* no more logic if we're not in EMLSR */ 951 if (hweight16(vif->active_links) <= 1) 952 return; 953 954 /* We are processing a notification before link activation */ 955 if (le32_to_cpu(notif->other_link_id) == FW_CTXT_ID_INVALID) 956 return; 957 958 /* Exit EMLSR if we lost more than 959 * IWL_MLD_MISSED_BEACONS_EXIT_ESR_THRESH beacons on boths links 960 * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH on current link. 961 * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED 962 * on current link and the link's bss_param_ch_count has changed on 963 * the other link's beacon. 964 */ 965 if ((missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS && 966 scnd_lnk_bcn_lost >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) || 967 missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH || 968 (bss_param_ch_cnt_link_id != link_id && 969 missed_bcon >= 970 IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED)) { 971 iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_MISSED_BEACON, 972 iwl_mld_get_primary_link(vif)); 973 } 974 } 975 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_handle_missed_beacon_notif); 976 977 bool iwl_mld_cancel_missed_beacon_notif(struct iwl_mld *mld, 978 struct iwl_rx_packet *pkt, 979 u32 removed_link_id) 980 { 981 struct iwl_missed_beacons_notif *notif = (void *)pkt->data; 982 983 if (le32_to_cpu(notif->other_link_id) == removed_link_id) { 984 /* Second link is being removed. Don't cancel the notification, 985 * but mark second link as invalid. 986 */ 987 notif->other_link_id = cpu_to_le32(FW_CTXT_ID_INVALID); 988 } 989 990 /* If the primary link is removed, cancel the notification */ 991 return le32_to_cpu(notif->link_id) == removed_link_id; 992 } 993 994 int iwl_mld_link_set_associated(struct iwl_mld *mld, struct ieee80211_vif *vif, 995 struct ieee80211_bss_conf *link) 996 { 997 return iwl_mld_change_link_in_fw(mld, link, LINK_CONTEXT_MODIFY_ALL & 998 ~(LINK_CONTEXT_MODIFY_ACTIVE | 999 LINK_CONTEXT_MODIFY_EHT_PARAMS)); 1000 } 1001 1002 struct iwl_mld_rssi_to_grade { 1003 s8 rssi[2]; 1004 u16 grade; 1005 }; 1006 1007 #define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \ 1008 { \ 1009 .rssi = {_lb, _hb_uhb}, \ 1010 .grade = _grade \ 1011 } 1012 1013 /* 1014 * This array must be sorted by increasing RSSI for proper functionality. 1015 * The grades are actually estimated throughput, represented as fixed-point 1016 * with a scale factor of 1/10. 1017 */ 1018 static const struct iwl_mld_rssi_to_grade rssi_to_grade_map[] = { 1019 RSSI_TO_GRADE_LINE(-85, -89, 172), 1020 RSSI_TO_GRADE_LINE(-83, -86, 344), 1021 RSSI_TO_GRADE_LINE(-82, -85, 516), 1022 RSSI_TO_GRADE_LINE(-80, -83, 688), 1023 RSSI_TO_GRADE_LINE(-77, -79, 1032), 1024 RSSI_TO_GRADE_LINE(-73, -76, 1376), 1025 RSSI_TO_GRADE_LINE(-70, -74, 1548), 1026 RSSI_TO_GRADE_LINE(-69, -72, 1720), 1027 RSSI_TO_GRADE_LINE(-65, -68, 2064), 1028 RSSI_TO_GRADE_LINE(-61, -66, 2294), 1029 RSSI_TO_GRADE_LINE(-58, -61, 2580), 1030 RSSI_TO_GRADE_LINE(-55, -58, 2868), 1031 RSSI_TO_GRADE_LINE(-46, -55, 3098), 1032 RSSI_TO_GRADE_LINE(-43, -54, 3442) 1033 }; 1034 1035 #define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade) 1036 1037 #define DEFAULT_CHAN_LOAD_2GHZ 30 1038 #define DEFAULT_CHAN_LOAD_5GHZ 15 1039 #define DEFAULT_CHAN_LOAD_6GHZ 0 1040 1041 /* Factors calculation is done with fixed-point with a scaling factor of 1/256 */ 1042 #define SCALE_FACTOR 256 1043 #define MAX_CHAN_LOAD 256 1044 1045 static unsigned int 1046 iwl_mld_get_n_subchannels(const struct ieee80211_bss_conf *link_conf) 1047 { 1048 enum nl80211_chan_width chan_width = 1049 link_conf->chanreq.oper.width; 1050 int mhz = nl80211_chan_width_to_mhz(chan_width); 1051 unsigned int n_subchannels; 1052 1053 if (WARN_ONCE(mhz < 20 || mhz > 320, 1054 "Invalid channel width : (%d)\n", mhz)) 1055 return 1; 1056 1057 /* total number of subchannels */ 1058 n_subchannels = mhz / 20; 1059 1060 /* No puncturing if less than 80 MHz */ 1061 if (mhz >= 80) 1062 n_subchannels -= hweight16(link_conf->chanreq.oper.punctured); 1063 1064 return n_subchannels; 1065 } 1066 1067 static int 1068 iwl_mld_get_chan_load_from_element(struct iwl_mld *mld, 1069 struct ieee80211_bss_conf *link_conf) 1070 { 1071 struct ieee80211_vif *vif = link_conf->vif; 1072 const struct cfg80211_bss_ies *ies; 1073 const struct element *bss_load_elem = NULL; 1074 const struct ieee80211_bss_load_elem *bss_load; 1075 1076 guard(rcu)(); 1077 1078 if (ieee80211_vif_link_active(vif, link_conf->link_id)) 1079 ies = rcu_dereference(link_conf->bss->beacon_ies); 1080 else 1081 ies = rcu_dereference(link_conf->bss->ies); 1082 1083 if (ies) 1084 bss_load_elem = cfg80211_find_elem(WLAN_EID_QBSS_LOAD, 1085 ies->data, ies->len); 1086 1087 if (!bss_load_elem || 1088 bss_load_elem->datalen != sizeof(*bss_load)) 1089 return -EINVAL; 1090 1091 bss_load = (const void *)bss_load_elem->data; 1092 1093 return bss_load->channel_util; 1094 } 1095 1096 static unsigned int 1097 iwl_mld_get_chan_load_by_us(struct iwl_mld *mld, 1098 struct ieee80211_bss_conf *link_conf, 1099 bool expect_active_link) 1100 { 1101 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf); 1102 struct ieee80211_chanctx_conf *chan_ctx; 1103 struct iwl_mld_phy *phy; 1104 1105 if (!mld_link || !mld_link->active) { 1106 WARN_ON(expect_active_link); 1107 return 0; 1108 } 1109 1110 if (WARN_ONCE(!rcu_access_pointer(mld_link->chan_ctx), 1111 "Active link (%u) without channel ctxt assigned!\n", 1112 link_conf->link_id)) 1113 return 0; 1114 1115 chan_ctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx); 1116 phy = iwl_mld_phy_from_mac80211(chan_ctx); 1117 1118 return phy->channel_load_by_us; 1119 } 1120 1121 /* Returns error if the channel utilization element is invalid/unavailable */ 1122 int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld, 1123 struct ieee80211_bss_conf *link_conf, 1124 bool expect_active_link) 1125 { 1126 int chan_load; 1127 unsigned int chan_load_by_us; 1128 1129 /* get overall load */ 1130 chan_load = iwl_mld_get_chan_load_from_element(mld, link_conf); 1131 if (chan_load < 0) 1132 return chan_load; 1133 1134 chan_load_by_us = iwl_mld_get_chan_load_by_us(mld, link_conf, 1135 expect_active_link); 1136 1137 /* channel load by us is given in percentage */ 1138 chan_load_by_us = 1139 NORMALIZE_PERCENT_TO_255(chan_load_by_us); 1140 1141 /* Use only values that firmware sends that can possibly be valid */ 1142 if (chan_load_by_us <= chan_load) 1143 chan_load -= chan_load_by_us; 1144 1145 return chan_load; 1146 } 1147 1148 static unsigned int 1149 iwl_mld_get_default_chan_load(struct ieee80211_bss_conf *link_conf) 1150 { 1151 enum nl80211_band band = link_conf->chanreq.oper.chan->band; 1152 1153 switch (band) { 1154 case NL80211_BAND_2GHZ: 1155 return DEFAULT_CHAN_LOAD_2GHZ; 1156 case NL80211_BAND_5GHZ: 1157 return DEFAULT_CHAN_LOAD_5GHZ; 1158 case NL80211_BAND_6GHZ: 1159 return DEFAULT_CHAN_LOAD_6GHZ; 1160 default: 1161 WARN_ON(1); 1162 return 0; 1163 } 1164 } 1165 1166 unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld, 1167 struct ieee80211_bss_conf *link_conf) 1168 { 1169 int chan_load; 1170 1171 chan_load = iwl_mld_get_chan_load_by_others(mld, link_conf, false); 1172 if (chan_load >= 0) 1173 return chan_load; 1174 1175 /* No information from the element, take the defaults */ 1176 chan_load = iwl_mld_get_default_chan_load(link_conf); 1177 1178 /* The defaults are given in percentage */ 1179 return NORMALIZE_PERCENT_TO_255(chan_load); 1180 } 1181 1182 static unsigned int 1183 iwl_mld_get_avail_chan_load(struct iwl_mld *mld, 1184 struct ieee80211_bss_conf *link_conf) 1185 { 1186 return MAX_CHAN_LOAD - iwl_mld_get_chan_load(mld, link_conf); 1187 } 1188 1189 /* This function calculates the grade of a link. Returns 0 in error case */ 1190 unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, 1191 struct ieee80211_bss_conf *link_conf) 1192 { 1193 enum nl80211_band band; 1194 int rssi_idx; 1195 s32 link_rssi; 1196 unsigned int grade = MAX_GRADE; 1197 1198 if (WARN_ON_ONCE(!link_conf)) 1199 return 0; 1200 1201 band = link_conf->chanreq.oper.chan->band; 1202 if (WARN_ONCE(band != NL80211_BAND_2GHZ && 1203 band != NL80211_BAND_5GHZ && 1204 band != NL80211_BAND_6GHZ, 1205 "Invalid band (%u)\n", band)) 1206 return 0; 1207 1208 link_rssi = MBM_TO_DBM(link_conf->bss->signal); 1209 /* 1210 * For 6 GHz the RSSI of the beacons is lower than 1211 * the RSSI of the data. 1212 */ 1213 if (band == NL80211_BAND_6GHZ && link_rssi) 1214 link_rssi += 4; 1215 1216 rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1; 1217 1218 /* No valid RSSI - take the lowest grade */ 1219 if (!link_rssi) 1220 link_rssi = rssi_to_grade_map[0].rssi[rssi_idx]; 1221 1222 IWL_DEBUG_EHT(mld, 1223 "Calculating grade of link %d: band = %d, bandwidth = %d, punctured subchannels =0x%x RSSI = %d\n", 1224 link_conf->link_id, band, 1225 link_conf->chanreq.oper.width, 1226 link_conf->chanreq.oper.punctured, link_rssi); 1227 1228 /* Get grade based on RSSI */ 1229 for (int i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) { 1230 const struct iwl_mld_rssi_to_grade *line = 1231 &rssi_to_grade_map[i]; 1232 1233 if (link_rssi > line->rssi[rssi_idx]) 1234 continue; 1235 grade = line->grade; 1236 break; 1237 } 1238 1239 /* Apply the channel load and puncturing factors */ 1240 grade = grade * iwl_mld_get_avail_chan_load(mld, link_conf) / SCALE_FACTOR; 1241 grade = grade * iwl_mld_get_n_subchannels(link_conf); 1242 1243 IWL_DEBUG_EHT(mld, "Link %d's grade: %d\n", link_conf->link_id, grade); 1244 1245 return grade; 1246 } 1247 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_link_grade); 1248 1249 void iwl_mld_handle_beacon_filter_notif(struct iwl_mld *mld, 1250 struct iwl_rx_packet *pkt) 1251 { 1252 const struct iwl_beacon_filter_notif *notif = (const void *)pkt->data; 1253 u32 link_id = le32_to_cpu(notif->link_id); 1254 struct ieee80211_bss_conf *link_conf = 1255 iwl_mld_fw_id_to_link_conf(mld, link_id); 1256 struct iwl_mld_link *mld_link; 1257 1258 if (IWL_FW_CHECK(mld, !link_conf, "invalid link ID %d\n", link_id)) 1259 return; 1260 1261 mld_link = iwl_mld_link_from_mac80211(link_conf); 1262 if (WARN_ON_ONCE(!mld_link)) 1263 return; 1264 1265 mld_link->average_beacon_energy = le32_to_cpu(notif->average_energy); 1266 } 1267