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