1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 /* 3 * Copyright (C) 2024-2025 Intel Corporation 4 */ 5 #include <net/cfg80211.h> 6 7 #include "iface.h" 8 #include "hcmd.h" 9 #include "key.h" 10 #include "mlo.h" 11 #include "mac80211.h" 12 13 #include "fw/api/context.h" 14 #include "fw/api/mac.h" 15 #include "fw/api/time-event.h" 16 #include "fw/api/datapath.h" 17 18 /* Cleanup function for struct iwl_mld_vif, will be called in restart */ 19 void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif) 20 { 21 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 22 struct iwl_mld *mld = mld_vif->mld; 23 struct iwl_mld_link *link; 24 25 mld_vif->emlsr.blocked_reasons &= ~IWL_MLD_EMLSR_BLOCKED_ROC; 26 27 if (mld_vif->aux_sta.sta_id != IWL_INVALID_STA) 28 iwl_mld_free_internal_sta(mld, &mld_vif->aux_sta); 29 30 /* EMLSR is turned back on during recovery */ 31 vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE; 32 33 if (mld_vif->roc_activity != ROC_NUM_ACTIVITIES) 34 ieee80211_remain_on_channel_expired(mld->hw); 35 36 mld_vif->roc_activity = ROC_NUM_ACTIVITIES; 37 38 for_each_mld_vif_valid_link(mld_vif, link) { 39 iwl_mld_cleanup_link(mld_vif->mld, link); 40 41 /* Correctly allocated primary link in non-MLO mode */ 42 if (!ieee80211_vif_is_mld(vif) && 43 link_id == 0 && link == &mld_vif->deflink) 44 continue; 45 46 if (vif->active_links & BIT(link_id)) 47 continue; 48 49 /* Should not happen as link removal should always succeed */ 50 WARN_ON(1); 51 if (link != &mld_vif->deflink) 52 kfree_rcu(link, rcu_head); 53 RCU_INIT_POINTER(mld_vif->link[link_id], NULL); 54 } 55 56 ieee80211_iter_keys(mld->hw, vif, iwl_mld_cleanup_keys_iter, NULL); 57 58 CLEANUP_STRUCT(mld_vif); 59 } 60 61 static int iwl_mld_send_mac_cmd(struct iwl_mld *mld, 62 struct iwl_mac_config_cmd *cmd) 63 { 64 int ret; 65 66 lockdep_assert_wiphy(mld->wiphy); 67 68 ret = iwl_mld_send_cmd_pdu(mld, 69 WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD), 70 cmd); 71 if (ret) 72 IWL_ERR(mld, "Failed to send MAC_CONFIG_CMD ret = %d\n", ret); 73 74 return ret; 75 } 76 77 int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif) 78 { 79 switch (vif->type) { 80 case NL80211_IFTYPE_STATION: 81 return vif->p2p ? FW_MAC_TYPE_P2P_STA : FW_MAC_TYPE_BSS_STA; 82 case NL80211_IFTYPE_AP: 83 return FW_MAC_TYPE_GO; 84 case NL80211_IFTYPE_MONITOR: 85 return FW_MAC_TYPE_LISTENER; 86 case NL80211_IFTYPE_P2P_DEVICE: 87 return FW_MAC_TYPE_P2P_DEVICE; 88 case NL80211_IFTYPE_ADHOC: 89 return FW_MAC_TYPE_IBSS; 90 default: 91 WARN_ON_ONCE(1); 92 } 93 return FW_MAC_TYPE_BSS_STA; 94 } 95 96 static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld, 97 struct ieee80211_vif *vif) 98 { 99 const struct ieee80211_supported_band *sband; 100 const struct ieee80211_sta_he_cap *own_he_cap; 101 102 lockdep_assert_wiphy(mld->wiphy); 103 104 /* This capability is the same for all bands, 105 * so take it from one of them. 106 */ 107 sband = mld->hw->wiphy->bands[NL80211_BAND_2GHZ]; 108 own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif); 109 110 return own_he_cap && (own_he_cap->he_cap_elem.mac_cap_info[2] & 111 IEEE80211_HE_MAC_CAP2_ACK_EN); 112 } 113 114 static void iwl_mld_set_he_support(struct iwl_mld *mld, 115 struct ieee80211_vif *vif, 116 struct iwl_mac_config_cmd *cmd) 117 { 118 if (vif->type == NL80211_IFTYPE_AP) 119 cmd->wifi_gen.he_ap_support = 1; 120 else 121 cmd->wifi_gen.he_support = 1; 122 } 123 124 /* fill the common part for all interface types */ 125 static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, 126 struct ieee80211_vif *vif, 127 struct iwl_mac_config_cmd *cmd, 128 u32 action) 129 { 130 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 131 struct ieee80211_bss_conf *link_conf; 132 unsigned int link_id; 133 134 lockdep_assert_wiphy(mld->wiphy); 135 136 cmd->id_and_color = cpu_to_le32(mld_vif->fw_id); 137 cmd->action = cpu_to_le32(action); 138 139 cmd->mac_type = 140 cpu_to_le32(iwl_mld_mac80211_iftype_to_fw(vif)); 141 142 memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN); 143 144 if (iwlwifi_mod_params.disable_11ax) 145 return; 146 147 cmd->nic_not_ack_enabled = 148 cpu_to_le32(!iwl_mld_is_nic_ack_enabled(mld, vif)); 149 150 /* If we have MLO enabled, then the firmware needs to enable 151 * address translation for the station(s) we add. That depends 152 * on having EHT enabled in firmware, which in turn depends on 153 * mac80211 in the code below. 154 * However, mac80211 doesn't enable HE/EHT until it has parsed 155 * the association response successfully, so just skip all that 156 * and enable both when we have MLO. 157 */ 158 if (ieee80211_vif_is_mld(vif)) { 159 iwl_mld_set_he_support(mld, vif, cmd); 160 cmd->wifi_gen.eht_support = 1; 161 return; 162 } 163 164 for_each_vif_active_link(vif, link_conf, link_id) { 165 if (!link_conf->he_support) 166 continue; 167 168 iwl_mld_set_he_support(mld, vif, cmd); 169 170 /* EHT, if supported, was already set above */ 171 break; 172 } 173 } 174 175 static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld, 176 struct ieee80211_vif *vif, u32 action, 177 struct iwl_mac_config_cmd *cmd) 178 { 179 struct ieee80211_bss_conf *link; 180 u32 twt_policy = 0; 181 int link_id; 182 183 lockdep_assert_wiphy(mld->wiphy); 184 185 WARN_ON(vif->type != NL80211_IFTYPE_STATION); 186 187 /* We always want to hear MCAST frames, if we're not authorized yet, 188 * we'll drop them. 189 */ 190 cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP); 191 192 /* Adding a MAC ctxt with is_assoc set is not allowed in fw 193 * (and shouldn't happen) 194 */ 195 if (vif->cfg.assoc && action != FW_CTXT_ACTION_ADD) { 196 cmd->client.is_assoc = 1; 197 198 if (!iwl_mld_vif_from_mac80211(vif)->authorized) 199 cmd->client.data_policy |= 200 cpu_to_le16(COEX_HIGH_PRIORITY_ENABLE); 201 } else { 202 /* Allow beacons to pass through as long as we are not 203 * associated 204 */ 205 cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON); 206 } 207 208 cmd->client.assoc_id = cpu_to_le16(vif->cfg.aid); 209 210 if (ieee80211_vif_is_mld(vif)) { 211 u16 esr_transition_timeout = 212 u16_get_bits(vif->cfg.eml_cap, 213 IEEE80211_EML_CAP_TRANSITION_TIMEOUT); 214 215 cmd->client.esr_transition_timeout = 216 min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU, 217 esr_transition_timeout); 218 cmd->client.medium_sync_delay = 219 cpu_to_le16(vif->cfg.eml_med_sync_delay); 220 } 221 222 for_each_vif_active_link(vif, link, link_id) { 223 if (!link->he_support) 224 continue; 225 226 if (link->twt_requester) 227 twt_policy |= TWT_SUPPORTED; 228 if (link->twt_protected) 229 twt_policy |= PROTECTED_TWT_SUPPORTED; 230 if (link->twt_broadcast) 231 twt_policy |= BROADCAST_TWT_SUPPORTED; 232 } 233 234 if (!iwlwifi_mod_params.disable_11ax) 235 cmd->client.data_policy |= cpu_to_le16(twt_policy); 236 237 if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p) 238 cmd->filter_flags |= 239 cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); 240 } 241 242 static void iwl_mld_fill_mac_cmd_ap(struct iwl_mld *mld, 243 struct ieee80211_vif *vif, 244 struct iwl_mac_config_cmd *cmd) 245 { 246 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 247 248 lockdep_assert_wiphy(mld->wiphy); 249 250 WARN_ON(vif->type != NL80211_IFTYPE_AP); 251 252 cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); 253 254 /* in AP mode, pass beacons from other APs (needed for ht protection). 255 * When there're no any associated station, which means that we are not 256 * TXing anyway, don't ask FW to pass beacons to prevent unnecessary 257 * wake-ups. 258 */ 259 if (mld_vif->num_associated_stas) 260 cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON); 261 } 262 263 static void iwl_mld_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) 264 { 265 bool *go_active = _data; 266 267 if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO && 268 iwl_mld_vif_from_mac80211(vif)->ap_ibss_active) 269 *go_active = true; 270 } 271 272 static bool iwl_mld_p2p_dev_has_extended_disc(struct iwl_mld *mld) 273 { 274 bool go_active = false; 275 276 /* This flag should be set to true when the P2P Device is 277 * discoverable and there is at least a P2P GO. Setting 278 * this flag will allow the P2P Device to be discoverable on other 279 * channels in addition to its listen channel. 280 * Note that this flag should not be set in other cases as it opens the 281 * Rx filters on all MAC and increases the number of interrupts. 282 */ 283 ieee80211_iterate_active_interfaces(mld->hw, 284 IEEE80211_IFACE_ITER_RESUME_ALL, 285 iwl_mld_go_iterator, &go_active); 286 287 return go_active; 288 } 289 290 static void iwl_mld_fill_mac_cmd_p2p_dev(struct iwl_mld *mld, 291 struct ieee80211_vif *vif, 292 struct iwl_mac_config_cmd *cmd) 293 { 294 bool ext_disc = iwl_mld_p2p_dev_has_extended_disc(mld); 295 296 lockdep_assert_wiphy(mld->wiphy); 297 298 /* Override the filter flags to accept all management frames. This is 299 * needed to support both P2P device discovery using probe requests and 300 * P2P service discovery using action frames 301 */ 302 cmd->filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT); 303 304 if (ext_disc) 305 cmd->p2p_dev.is_disc_extended = cpu_to_le32(1); 306 } 307 308 static void iwl_mld_fill_mac_cmd_ibss(struct iwl_mld *mld, 309 struct ieee80211_vif *vif, 310 struct iwl_mac_config_cmd *cmd) 311 { 312 lockdep_assert_wiphy(mld->wiphy); 313 314 WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); 315 316 cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON | 317 MAC_CFG_FILTER_ACCEPT_PROBE_REQ | 318 MAC_CFG_FILTER_ACCEPT_GRP); 319 } 320 321 static int 322 iwl_mld_rm_mac_from_fw(struct iwl_mld *mld, struct ieee80211_vif *vif) 323 { 324 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 325 struct iwl_mac_config_cmd cmd = { 326 .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), 327 .id_and_color = cpu_to_le32(mld_vif->fw_id), 328 }; 329 330 return iwl_mld_send_mac_cmd(mld, &cmd); 331 } 332 333 int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif, 334 u32 action) 335 { 336 struct iwl_mac_config_cmd cmd = {}; 337 338 lockdep_assert_wiphy(mld->wiphy); 339 340 if (action == FW_CTXT_ACTION_REMOVE) 341 return iwl_mld_rm_mac_from_fw(mld, vif); 342 343 iwl_mld_mac_cmd_fill_common(mld, vif, &cmd, action); 344 345 switch (vif->type) { 346 case NL80211_IFTYPE_STATION: 347 iwl_mld_fill_mac_cmd_sta(mld, vif, action, &cmd); 348 break; 349 case NL80211_IFTYPE_AP: 350 iwl_mld_fill_mac_cmd_ap(mld, vif, &cmd); 351 break; 352 case NL80211_IFTYPE_MONITOR: 353 cmd.filter_flags = 354 cpu_to_le32(MAC_CFG_FILTER_PROMISC | 355 MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT | 356 MAC_CFG_FILTER_ACCEPT_BEACON | 357 MAC_CFG_FILTER_ACCEPT_PROBE_REQ | 358 MAC_CFG_FILTER_ACCEPT_GRP); 359 break; 360 case NL80211_IFTYPE_P2P_DEVICE: 361 iwl_mld_fill_mac_cmd_p2p_dev(mld, vif, &cmd); 362 break; 363 case NL80211_IFTYPE_ADHOC: 364 iwl_mld_fill_mac_cmd_ibss(mld, vif, &cmd); 365 break; 366 default: 367 WARN(1, "not supported yet\n"); 368 return -EOPNOTSUPP; 369 } 370 371 return iwl_mld_send_mac_cmd(mld, &cmd); 372 } 373 374 static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy, 375 struct wiphy_work *wk) 376 { 377 struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif, 378 mlo_scan_start_wk.work); 379 struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); 380 struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 381 382 iwl_mld_int_mlo_scan(mld, iwl_mld_vif_to_mac80211(mld_vif)); 383 } 384 385 IWL_MLD_ALLOC_FN(vif, vif) 386 387 /* Constructor function for struct iwl_mld_vif */ 388 static int 389 iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) 390 { 391 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 392 int ret; 393 394 lockdep_assert_wiphy(mld->wiphy); 395 396 mld_vif->mld = mld; 397 mld_vif->roc_activity = ROC_NUM_ACTIVITIES; 398 399 ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif); 400 if (ret) 401 return ret; 402 403 if (!mld->fw_status.in_hw_restart) { 404 wiphy_work_init(&mld_vif->emlsr.unblock_tpt_wk, 405 iwl_mld_emlsr_unblock_tpt_wk); 406 wiphy_delayed_work_init(&mld_vif->emlsr.check_tpt_wk, 407 iwl_mld_emlsr_check_tpt); 408 wiphy_delayed_work_init(&mld_vif->emlsr.prevent_done_wk, 409 iwl_mld_emlsr_prevent_done_wk); 410 wiphy_delayed_work_init(&mld_vif->emlsr.tmp_non_bss_done_wk, 411 iwl_mld_emlsr_tmp_non_bss_done_wk); 412 wiphy_delayed_work_init(&mld_vif->mlo_scan_start_wk, 413 iwl_mld_mlo_scan_start_wk); 414 } 415 iwl_mld_init_internal_sta(&mld_vif->aux_sta); 416 417 return 0; 418 } 419 420 int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) 421 { 422 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 423 int ret; 424 425 lockdep_assert_wiphy(mld->wiphy); 426 427 ret = iwl_mld_init_vif(mld, vif); 428 if (ret) 429 return ret; 430 431 ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_ADD); 432 if (ret) 433 RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL); 434 435 return ret; 436 } 437 438 void iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) 439 { 440 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 441 442 lockdep_assert_wiphy(mld->wiphy); 443 444 iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_REMOVE); 445 446 if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->fw_id_to_vif))) 447 return; 448 449 RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL); 450 451 iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_VIF, 452 mld_vif->fw_id); 453 } 454 455 void iwl_mld_set_vif_associated(struct iwl_mld *mld, 456 struct ieee80211_vif *vif) 457 { 458 struct ieee80211_bss_conf *link; 459 unsigned int link_id; 460 461 for_each_vif_active_link(vif, link, link_id) { 462 if (iwl_mld_link_set_associated(mld, vif, link)) 463 IWL_ERR(mld, "failed to update link %d\n", link_id); 464 } 465 466 iwl_mld_recalc_multicast_filter(mld); 467 } 468 469 static void iwl_mld_get_fw_id_bss_bitmap_iter(void *_data, u8 *mac, 470 struct ieee80211_vif *vif) 471 { 472 u8 *fw_id_bitmap = _data; 473 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 474 475 if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) 476 return; 477 478 *fw_id_bitmap |= BIT(mld_vif->fw_id); 479 } 480 481 u8 iwl_mld_get_fw_bss_vifs_ids(struct iwl_mld *mld) 482 { 483 u8 fw_id_bitmap = 0; 484 485 ieee80211_iterate_active_interfaces_mtx(mld->hw, 486 IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER, 487 iwl_mld_get_fw_id_bss_bitmap_iter, 488 &fw_id_bitmap); 489 490 return fw_id_bitmap; 491 } 492 493 void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld, 494 struct iwl_rx_packet *pkt) 495 { 496 const struct iwl_probe_resp_data_notif *notif = (void *)pkt->data; 497 struct iwl_probe_resp_data *old_data, *new_data; 498 struct ieee80211_vif *vif; 499 struct iwl_mld_link *mld_link; 500 501 IWL_DEBUG_INFO(mld, "Probe response data notif: noa %d, csa %d\n", 502 notif->noa_active, notif->csa_counter); 503 504 if (IWL_FW_CHECK(mld, le32_to_cpu(notif->mac_id) >= 505 ARRAY_SIZE(mld->fw_id_to_vif), 506 "mac id is invalid: %d\n", 507 le32_to_cpu(notif->mac_id))) 508 return; 509 510 vif = wiphy_dereference(mld->wiphy, 511 mld->fw_id_to_vif[le32_to_cpu(notif->mac_id)]); 512 513 /* the firmware gives us the mac_id (and not the link_id), mac80211 514 * gets a vif and not a link, bottom line, this flow is not MLD ready 515 * yet. 516 */ 517 if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif)) 518 return; 519 520 if (notif->csa_counter != IWL_PROBE_RESP_DATA_NO_CSA && 521 notif->csa_counter >= 1) 522 ieee80211_beacon_set_cntdwn(vif, notif->csa_counter); 523 524 if (!vif->p2p) 525 return; 526 527 mld_link = &iwl_mld_vif_from_mac80211(vif)->deflink; 528 529 /* len_low should be 2 + n*13 (where n is the number of descriptors. 530 * 13 is the size of a NoA descriptor). We can have either one or two 531 * descriptors. 532 */ 533 if (IWL_FW_CHECK(mld, notif->noa_active && 534 notif->noa_attr.len_low != 2 + 535 sizeof(struct ieee80211_p2p_noa_desc) && 536 notif->noa_attr.len_low != 2 + 537 sizeof(struct ieee80211_p2p_noa_desc) * 2, 538 "Invalid noa_attr.len_low (%d)\n", 539 notif->noa_attr.len_low)) 540 return; 541 542 new_data = kzalloc(sizeof(*new_data), GFP_KERNEL); 543 if (!new_data) 544 return; 545 546 memcpy(&new_data->notif, notif, sizeof(new_data->notif)); 547 548 /* noa_attr contains 1 reserved byte, need to substruct it */ 549 new_data->noa_len = sizeof(struct ieee80211_vendor_ie) + 550 sizeof(new_data->notif.noa_attr) - 1; 551 552 /* 553 * If it's a one time NoA, only one descriptor is needed, 554 * adjust the length according to len_low. 555 */ 556 if (new_data->notif.noa_attr.len_low == 557 sizeof(struct ieee80211_p2p_noa_desc) + 2) 558 new_data->noa_len -= sizeof(struct ieee80211_p2p_noa_desc); 559 560 old_data = wiphy_dereference(mld->wiphy, mld_link->probe_resp_data); 561 rcu_assign_pointer(mld_link->probe_resp_data, new_data); 562 563 if (old_data) 564 kfree_rcu(old_data, rcu_head); 565 } 566 567 void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld, 568 struct iwl_rx_packet *pkt) 569 { 570 struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data; 571 struct ieee80211_vif *vif; 572 573 if (IWL_FW_CHECK(mld, notif->mac_id >= ARRAY_SIZE(mld->fw_id_to_vif), 574 "mac id is invalid: %d\n", notif->mac_id)) 575 return; 576 577 vif = wiphy_dereference(mld->wiphy, mld->fw_id_to_vif[notif->mac_id]); 578 579 if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif)) 580 return; 581 582 IWL_WARN(mld, "uapsd misbehaving AP: %pM\n", vif->bss_conf.bssid); 583 } 584 585 void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld, 586 struct iwl_rx_packet *pkt) 587 { 588 struct iwl_datapath_monitor_notif *notif = (void *)pkt->data; 589 struct ieee80211_bss_conf *link; 590 struct ieee80211_supported_band *sband; 591 const struct ieee80211_sta_he_cap *he_cap; 592 struct ieee80211_vif *vif; 593 struct iwl_mld_vif *mld_vif; 594 595 if (notif->type != cpu_to_le32(IWL_DP_MON_NOTIF_TYPE_EXT_CCA)) 596 return; 597 598 link = iwl_mld_fw_id_to_link_conf(mld, notif->link_id); 599 if (WARN_ON(!link)) 600 return; 601 602 vif = link->vif; 603 if (WARN_ON(!vif) || vif->type != NL80211_IFTYPE_STATION || 604 !vif->cfg.assoc) 605 return; 606 607 if (!link->chanreq.oper.chan || 608 link->chanreq.oper.chan->band != NL80211_BAND_2GHZ || 609 link->chanreq.oper.width < NL80211_CHAN_WIDTH_40) 610 return; 611 612 mld_vif = iwl_mld_vif_from_mac80211(vif); 613 614 /* this shouldn't happen *again*, ignore it */ 615 if (mld_vif->cca_40mhz_workaround != CCA_40_MHZ_WA_NONE) 616 return; 617 618 mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_RECONNECT; 619 620 /* 621 * This capability manipulation isn't really ideal, but it's the 622 * easiest choice - otherwise we'd have to do some major changes 623 * in mac80211 to support this, which isn't worth it. This does 624 * mean that userspace may have outdated information, but that's 625 * actually not an issue at all. 626 */ 627 sband = mld->wiphy->bands[NL80211_BAND_2GHZ]; 628 629 WARN_ON(!sband->ht_cap.ht_supported); 630 WARN_ON(!(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)); 631 sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; 632 633 he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif); 634 635 if (he_cap) { 636 /* we know that ours is writable */ 637 struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap; 638 639 WARN_ON(!he->has_he); 640 WARN_ON(!(he->he_cap_elem.phy_cap_info[0] & 641 IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G)); 642 he->he_cap_elem.phy_cap_info[0] &= 643 ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; 644 } 645 646 ieee80211_disconnect(vif, true); 647 } 648 649 void iwl_mld_reset_cca_40mhz_workaround(struct iwl_mld *mld, 650 struct ieee80211_vif *vif) 651 { 652 struct ieee80211_supported_band *sband; 653 const struct ieee80211_sta_he_cap *he_cap; 654 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 655 656 if (vif->type != NL80211_IFTYPE_STATION) 657 return; 658 659 if (mld_vif->cca_40mhz_workaround == CCA_40_MHZ_WA_NONE) 660 return; 661 662 /* Now we are just reconnecting with the new capabilities, 663 * but remember to reset the capabilities when we disconnect for real 664 */ 665 if (mld_vif->cca_40mhz_workaround == CCA_40_MHZ_WA_RECONNECT) { 666 mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_RESET; 667 return; 668 } 669 670 /* Now cca_40mhz_workaround == CCA_40_MHZ_WA_RESET */ 671 672 sband = mld->wiphy->bands[NL80211_BAND_2GHZ]; 673 674 sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; 675 676 he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif); 677 678 if (he_cap) { 679 /* we know that ours is writable */ 680 struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap; 681 682 he->he_cap_elem.phy_cap_info[0] |= 683 IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; 684 } 685 686 mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_NONE; 687 } 688 689 struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld) 690 { 691 unsigned long fw_id_bitmap = iwl_mld_get_fw_bss_vifs_ids(mld); 692 int fw_id; 693 694 if (hweight8(fw_id_bitmap) != 1) 695 return NULL; 696 697 fw_id = __ffs(fw_id_bitmap); 698 699 return wiphy_dereference(mld->wiphy, 700 mld->fw_id_to_vif[fw_id]); 701 } 702