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