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