Path: blob/main/sys/contrib/dev/iwlwifi/mld/iface.c
48285 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2024-2025 Intel Corporation3*/4#include <net/cfg80211.h>56#include "iface.h"7#include "hcmd.h"8#include "key.h"9#include "mlo.h"10#include "mac80211.h"1112#include "fw/api/context.h"13#include "fw/api/mac.h"14#include "fw/api/time-event.h"15#include "fw/api/datapath.h"1617/* Cleanup function for struct iwl_mld_vif, will be called in restart */18void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif)19{20struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);21struct iwl_mld *mld = mld_vif->mld;22struct iwl_mld_link *link;2324mld_vif->emlsr.blocked_reasons &= ~IWL_MLD_EMLSR_BLOCKED_ROC;2526if (mld_vif->aux_sta.sta_id != IWL_INVALID_STA)27iwl_mld_free_internal_sta(mld, &mld_vif->aux_sta);2829/* EMLSR is turned back on during recovery */30vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE;3132if (mld_vif->roc_activity != ROC_NUM_ACTIVITIES)33ieee80211_remain_on_channel_expired(mld->hw);3435mld_vif->roc_activity = ROC_NUM_ACTIVITIES;3637for_each_mld_vif_valid_link(mld_vif, link) {38iwl_mld_cleanup_link(mld_vif->mld, link);3940/* Correctly allocated primary link in non-MLO mode */41if (!ieee80211_vif_is_mld(vif) &&42link_id == 0 && link == &mld_vif->deflink)43continue;4445if (vif->active_links & BIT(link_id))46continue;4748/* Should not happen as link removal should always succeed */49WARN_ON(1);50if (link != &mld_vif->deflink)51kfree_rcu(link, rcu_head);52RCU_INIT_POINTER(mld_vif->link[link_id], NULL);53}5455ieee80211_iter_keys(mld->hw, vif, iwl_mld_cleanup_keys_iter, NULL);5657wiphy_delayed_work_cancel(mld->wiphy, &mld_vif->mlo_scan_start_wk);5859CLEANUP_STRUCT(mld_vif);60}6162static int iwl_mld_send_mac_cmd(struct iwl_mld *mld,63struct iwl_mac_config_cmd *cmd)64{65int ret;6667lockdep_assert_wiphy(mld->wiphy);6869ret = iwl_mld_send_cmd_pdu(mld,70WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD),71cmd);72if (ret)73IWL_ERR(mld, "Failed to send MAC_CONFIG_CMD ret = %d\n", ret);7475return ret;76}7778int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif)79{80switch (vif->type) {81case NL80211_IFTYPE_STATION:82return vif->p2p ? FW_MAC_TYPE_P2P_STA : FW_MAC_TYPE_BSS_STA;83case NL80211_IFTYPE_AP:84return FW_MAC_TYPE_GO;85case NL80211_IFTYPE_MONITOR:86return FW_MAC_TYPE_LISTENER;87case NL80211_IFTYPE_P2P_DEVICE:88return FW_MAC_TYPE_P2P_DEVICE;89case NL80211_IFTYPE_ADHOC:90return FW_MAC_TYPE_IBSS;91default:92WARN_ON_ONCE(1);93}94return FW_MAC_TYPE_BSS_STA;95}9697static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld,98struct ieee80211_vif *vif)99{100const struct ieee80211_supported_band *sband;101const struct ieee80211_sta_he_cap *own_he_cap;102103lockdep_assert_wiphy(mld->wiphy);104105/* This capability is the same for all bands,106* so take it from one of them.107*/108sband = mld->hw->wiphy->bands[NL80211_BAND_2GHZ];109own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);110111return own_he_cap && (own_he_cap->he_cap_elem.mac_cap_info[2] &112IEEE80211_HE_MAC_CAP2_ACK_EN);113}114115static void iwl_mld_set_he_support(struct iwl_mld *mld,116struct ieee80211_vif *vif,117struct iwl_mac_config_cmd *cmd,118int cmd_ver)119{120if (vif->type == NL80211_IFTYPE_AP) {121if (cmd_ver == 2)122cmd->wifi_gen_v2.he_ap_support = cpu_to_le16(1);123else124cmd->wifi_gen.he_ap_support = 1;125} else {126if (cmd_ver == 2)127cmd->wifi_gen_v2.he_support = cpu_to_le16(1);128else129cmd->wifi_gen.he_support = 1;130}131}132133/* fill the common part for all interface types */134static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,135struct ieee80211_vif *vif,136struct iwl_mac_config_cmd *cmd,137u32 action)138{139struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);140struct ieee80211_bss_conf *link_conf;141unsigned int link_id;142int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw,143WIDE_ID(MAC_CONF_GROUP,144MAC_CONFIG_CMD), 0);145146lockdep_assert_wiphy(mld->wiphy);147148cmd->id_and_color = cpu_to_le32(mld_vif->fw_id);149cmd->action = cpu_to_le32(action);150151cmd->mac_type =152cpu_to_le32(iwl_mld_mac80211_iftype_to_fw(vif));153154memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN);155156if (iwlwifi_mod_params.disable_11ax)157return;158159cmd->nic_not_ack_enabled =160cpu_to_le32(!iwl_mld_is_nic_ack_enabled(mld, vif));161162/* If we have MLO enabled, then the firmware needs to enable163* address translation for the station(s) we add. That depends164* on having EHT enabled in firmware, which in turn depends on165* mac80211 in the code below.166* However, mac80211 doesn't enable HE/EHT until it has parsed167* the association response successfully, so just skip all that168* and enable both when we have MLO.169*/170if (ieee80211_vif_is_mld(vif)) {171iwl_mld_set_he_support(mld, vif, cmd, cmd_ver);172if (cmd_ver == 2)173cmd->wifi_gen_v2.eht_support = cpu_to_le32(1);174else175cmd->wifi_gen.eht_support = 1;176return;177}178179for_each_vif_active_link(vif, link_conf, link_id) {180if (!link_conf->he_support)181continue;182183iwl_mld_set_he_support(mld, vif, cmd, cmd_ver);184185/* EHT, if supported, was already set above */186break;187}188}189190static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld,191struct ieee80211_vif *vif, u32 action,192struct iwl_mac_config_cmd *cmd)193{194struct ieee80211_bss_conf *link;195u32 twt_policy = 0;196int link_id;197198lockdep_assert_wiphy(mld->wiphy);199200WARN_ON(vif->type != NL80211_IFTYPE_STATION);201202/* We always want to hear MCAST frames, if we're not authorized yet,203* we'll drop them.204*/205cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP);206207/* Adding a MAC ctxt with is_assoc set is not allowed in fw208* (and shouldn't happen)209*/210if (vif->cfg.assoc && action != FW_CTXT_ACTION_ADD) {211cmd->client.is_assoc = 1;212213if (!iwl_mld_vif_from_mac80211(vif)->authorized)214cmd->client.data_policy |=215cpu_to_le16(COEX_HIGH_PRIORITY_ENABLE);216} else {217/* Allow beacons to pass through as long as we are not218* associated219*/220cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON);221}222223cmd->client.assoc_id = cpu_to_le16(vif->cfg.aid);224225if (ieee80211_vif_is_mld(vif)) {226u16 esr_transition_timeout =227u16_get_bits(vif->cfg.eml_cap,228IEEE80211_EML_CAP_TRANSITION_TIMEOUT);229230cmd->client.esr_transition_timeout =231min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU,232esr_transition_timeout);233cmd->client.medium_sync_delay =234cpu_to_le16(vif->cfg.eml_med_sync_delay);235}236237for_each_vif_active_link(vif, link, link_id) {238if (!link->he_support)239continue;240241if (link->twt_requester)242twt_policy |= TWT_SUPPORTED;243if (link->twt_protected)244twt_policy |= PROTECTED_TWT_SUPPORTED;245if (link->twt_broadcast)246twt_policy |= BROADCAST_TWT_SUPPORTED;247}248249if (!iwlwifi_mod_params.disable_11ax)250cmd->client.data_policy |= cpu_to_le16(twt_policy);251252if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p)253cmd->filter_flags |=254cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ);255}256257static void iwl_mld_fill_mac_cmd_ap(struct iwl_mld *mld,258struct ieee80211_vif *vif,259struct iwl_mac_config_cmd *cmd)260{261struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);262263lockdep_assert_wiphy(mld->wiphy);264265WARN_ON(vif->type != NL80211_IFTYPE_AP);266267cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ);268269/* in AP mode, pass beacons from other APs (needed for ht protection).270* When there're no any associated station, which means that we are not271* TXing anyway, don't ask FW to pass beacons to prevent unnecessary272* wake-ups.273*/274if (mld_vif->num_associated_stas)275cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON);276}277278static void iwl_mld_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif)279{280bool *go_active = _data;281282if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO &&283iwl_mld_vif_from_mac80211(vif)->ap_ibss_active)284*go_active = true;285}286287static bool iwl_mld_p2p_dev_has_extended_disc(struct iwl_mld *mld)288{289bool go_active = false;290291/* This flag should be set to true when the P2P Device is292* discoverable and there is at least a P2P GO. Setting293* this flag will allow the P2P Device to be discoverable on other294* channels in addition to its listen channel.295* Note that this flag should not be set in other cases as it opens the296* Rx filters on all MAC and increases the number of interrupts.297*/298ieee80211_iterate_active_interfaces(mld->hw,299IEEE80211_IFACE_ITER_RESUME_ALL,300iwl_mld_go_iterator, &go_active);301302return go_active;303}304305static void iwl_mld_fill_mac_cmd_p2p_dev(struct iwl_mld *mld,306struct ieee80211_vif *vif,307struct iwl_mac_config_cmd *cmd)308{309bool ext_disc = iwl_mld_p2p_dev_has_extended_disc(mld);310311lockdep_assert_wiphy(mld->wiphy);312313/* Override the filter flags to accept all management frames. This is314* needed to support both P2P device discovery using probe requests and315* P2P service discovery using action frames316*/317cmd->filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT);318319if (ext_disc)320cmd->p2p_dev.is_disc_extended = cpu_to_le32(1);321}322323static void iwl_mld_fill_mac_cmd_ibss(struct iwl_mld *mld,324struct ieee80211_vif *vif,325struct iwl_mac_config_cmd *cmd)326{327lockdep_assert_wiphy(mld->wiphy);328329WARN_ON(vif->type != NL80211_IFTYPE_ADHOC);330331cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON |332MAC_CFG_FILTER_ACCEPT_PROBE_REQ |333MAC_CFG_FILTER_ACCEPT_GRP);334}335336static int337iwl_mld_rm_mac_from_fw(struct iwl_mld *mld, struct ieee80211_vif *vif)338{339struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);340struct iwl_mac_config_cmd cmd = {341.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),342.id_and_color = cpu_to_le32(mld_vif->fw_id),343};344345return iwl_mld_send_mac_cmd(mld, &cmd);346}347348int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,349u32 action)350{351struct iwl_mac_config_cmd cmd = {};352353lockdep_assert_wiphy(mld->wiphy);354355if (action == FW_CTXT_ACTION_REMOVE)356return iwl_mld_rm_mac_from_fw(mld, vif);357358iwl_mld_mac_cmd_fill_common(mld, vif, &cmd, action);359360switch (vif->type) {361case NL80211_IFTYPE_STATION:362iwl_mld_fill_mac_cmd_sta(mld, vif, action, &cmd);363break;364case NL80211_IFTYPE_AP:365iwl_mld_fill_mac_cmd_ap(mld, vif, &cmd);366break;367case NL80211_IFTYPE_MONITOR:368cmd.filter_flags =369cpu_to_le32(MAC_CFG_FILTER_PROMISC |370MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT |371MAC_CFG_FILTER_ACCEPT_BEACON |372MAC_CFG_FILTER_ACCEPT_PROBE_REQ |373MAC_CFG_FILTER_ACCEPT_GRP);374break;375case NL80211_IFTYPE_P2P_DEVICE:376iwl_mld_fill_mac_cmd_p2p_dev(mld, vif, &cmd);377break;378case NL80211_IFTYPE_ADHOC:379iwl_mld_fill_mac_cmd_ibss(mld, vif, &cmd);380break;381default:382WARN(1, "not supported yet\n");383return -EOPNOTSUPP;384}385386return iwl_mld_send_mac_cmd(mld, &cmd);387}388389static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy,390struct wiphy_work *wk)391{392struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif,393mlo_scan_start_wk.work);394struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);395struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);396397iwl_mld_int_mlo_scan(mld, iwl_mld_vif_to_mac80211(mld_vif));398}399400IWL_MLD_ALLOC_FN(vif, vif)401402/* Constructor function for struct iwl_mld_vif */403static int404iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)405{406struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);407int ret;408409lockdep_assert_wiphy(mld->wiphy);410411mld_vif->mld = mld;412mld_vif->roc_activity = ROC_NUM_ACTIVITIES;413414ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif);415if (ret)416return ret;417418if (!mld->fw_status.in_hw_restart) {419wiphy_work_init(&mld_vif->emlsr.unblock_tpt_wk,420iwl_mld_emlsr_unblock_tpt_wk);421wiphy_delayed_work_init(&mld_vif->emlsr.check_tpt_wk,422iwl_mld_emlsr_check_tpt);423wiphy_delayed_work_init(&mld_vif->emlsr.prevent_done_wk,424iwl_mld_emlsr_prevent_done_wk);425wiphy_delayed_work_init(&mld_vif->emlsr.tmp_non_bss_done_wk,426iwl_mld_emlsr_tmp_non_bss_done_wk);427wiphy_delayed_work_init(&mld_vif->mlo_scan_start_wk,428iwl_mld_mlo_scan_start_wk);429}430iwl_mld_init_internal_sta(&mld_vif->aux_sta);431432return 0;433}434435int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)436{437struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);438int ret;439440lockdep_assert_wiphy(mld->wiphy);441442ret = iwl_mld_init_vif(mld, vif);443if (ret)444return ret;445446ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_ADD);447if (ret)448RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL);449450return ret;451}452453int iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)454{455struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);456int ret;457458lockdep_assert_wiphy(mld->wiphy);459460ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_REMOVE);461462if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->fw_id_to_vif)))463return -EINVAL;464465RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL);466467iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_VIF,468mld_vif->fw_id);469470return ret;471}472473void iwl_mld_set_vif_associated(struct iwl_mld *mld,474struct ieee80211_vif *vif)475{476struct ieee80211_bss_conf *link;477unsigned int link_id;478479for_each_vif_active_link(vif, link, link_id) {480if (iwl_mld_link_set_associated(mld, vif, link))481IWL_ERR(mld, "failed to update link %d\n", link_id);482}483484iwl_mld_recalc_multicast_filter(mld);485}486487static void iwl_mld_get_fw_id_bss_bitmap_iter(void *_data, u8 *mac,488struct ieee80211_vif *vif)489{490u8 *fw_id_bitmap = _data;491struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);492493if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)494return;495496*fw_id_bitmap |= BIT(mld_vif->fw_id);497}498499u8 iwl_mld_get_fw_bss_vifs_ids(struct iwl_mld *mld)500{501u8 fw_id_bitmap = 0;502503ieee80211_iterate_active_interfaces_mtx(mld->hw,504IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER,505iwl_mld_get_fw_id_bss_bitmap_iter,506&fw_id_bitmap);507508return fw_id_bitmap;509}510511void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld,512struct iwl_rx_packet *pkt)513{514const struct iwl_probe_resp_data_notif *notif = (void *)pkt->data;515struct iwl_probe_resp_data *old_data, *new_data;516struct ieee80211_vif *vif;517struct iwl_mld_link *mld_link;518519IWL_DEBUG_INFO(mld, "Probe response data notif: noa %d, csa %d\n",520notif->noa_active, notif->csa_counter);521522if (IWL_FW_CHECK(mld, le32_to_cpu(notif->mac_id) >=523ARRAY_SIZE(mld->fw_id_to_vif),524"mac id is invalid: %d\n",525le32_to_cpu(notif->mac_id)))526return;527528vif = wiphy_dereference(mld->wiphy,529mld->fw_id_to_vif[le32_to_cpu(notif->mac_id)]);530531/* the firmware gives us the mac_id (and not the link_id), mac80211532* gets a vif and not a link, bottom line, this flow is not MLD ready533* yet.534*/535if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif))536return;537538if (notif->csa_counter != IWL_PROBE_RESP_DATA_NO_CSA &&539notif->csa_counter >= 1)540ieee80211_beacon_set_cntdwn(vif, notif->csa_counter);541542if (!vif->p2p)543return;544545mld_link = &iwl_mld_vif_from_mac80211(vif)->deflink;546547new_data = kzalloc(sizeof(*new_data), GFP_KERNEL);548if (!new_data)549return;550551memcpy(&new_data->notif, notif, sizeof(new_data->notif));552553/* noa_attr contains 1 reserved byte, need to substruct it */554new_data->noa_len = sizeof(struct ieee80211_vendor_ie) +555sizeof(new_data->notif.noa_attr) - 1;556557/*558* If it's a one time NoA, only one descriptor is needed,559* adjust the length according to len_low.560*/561if (new_data->notif.noa_attr.len_low ==562sizeof(struct ieee80211_p2p_noa_desc) + 2)563new_data->noa_len -= sizeof(struct ieee80211_p2p_noa_desc);564565old_data = wiphy_dereference(mld->wiphy, mld_link->probe_resp_data);566rcu_assign_pointer(mld_link->probe_resp_data, new_data);567568if (old_data)569kfree_rcu(old_data, rcu_head);570}571572void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld,573struct iwl_rx_packet *pkt)574{575struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data;576struct ieee80211_vif *vif;577578if (IWL_FW_CHECK(mld, notif->mac_id >= ARRAY_SIZE(mld->fw_id_to_vif),579"mac id is invalid: %d\n", notif->mac_id))580return;581582vif = wiphy_dereference(mld->wiphy, mld->fw_id_to_vif[notif->mac_id]);583584if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif))585return;586587IWL_WARN(mld, "uapsd misbehaving AP: %pM\n", vif->bss_conf.bssid);588}589590void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld,591struct iwl_rx_packet *pkt)592{593struct iwl_datapath_monitor_notif *notif = (void *)pkt->data;594struct ieee80211_bss_conf *link;595struct ieee80211_supported_band *sband;596const struct ieee80211_sta_he_cap *he_cap;597struct ieee80211_vif *vif;598struct iwl_mld_vif *mld_vif;599600if (notif->type != cpu_to_le32(IWL_DP_MON_NOTIF_TYPE_EXT_CCA))601return;602603link = iwl_mld_fw_id_to_link_conf(mld, notif->link_id);604if (WARN_ON(!link))605return;606607vif = link->vif;608if (WARN_ON(!vif) || vif->type != NL80211_IFTYPE_STATION ||609!vif->cfg.assoc)610return;611612if (!link->chanreq.oper.chan ||613link->chanreq.oper.chan->band != NL80211_BAND_2GHZ ||614link->chanreq.oper.width < NL80211_CHAN_WIDTH_40)615return;616617mld_vif = iwl_mld_vif_from_mac80211(vif);618619/* this shouldn't happen *again*, ignore it */620if (mld_vif->cca_40mhz_workaround != CCA_40_MHZ_WA_NONE)621return;622623mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_RECONNECT;624625/*626* This capability manipulation isn't really ideal, but it's the627* easiest choice - otherwise we'd have to do some major changes628* in mac80211 to support this, which isn't worth it. This does629* mean that userspace may have outdated information, but that's630* actually not an issue at all.631*/632sband = mld->wiphy->bands[NL80211_BAND_2GHZ];633634WARN_ON(!sband->ht_cap.ht_supported);635WARN_ON(!(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40));636sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;637638he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);639640if (he_cap) {641/* we know that ours is writable */642struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap;643644WARN_ON(!he->has_he);645WARN_ON(!(he->he_cap_elem.phy_cap_info[0] &646IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G));647he->he_cap_elem.phy_cap_info[0] &=648~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;649}650651ieee80211_disconnect(vif, true);652}653654void iwl_mld_reset_cca_40mhz_workaround(struct iwl_mld *mld,655struct ieee80211_vif *vif)656{657struct ieee80211_supported_band *sband;658const struct ieee80211_sta_he_cap *he_cap;659struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);660661if (vif->type != NL80211_IFTYPE_STATION)662return;663664if (mld_vif->cca_40mhz_workaround == CCA_40_MHZ_WA_NONE)665return;666667/* Now we are just reconnecting with the new capabilities,668* but remember to reset the capabilities when we disconnect for real669*/670if (mld_vif->cca_40mhz_workaround == CCA_40_MHZ_WA_RECONNECT) {671mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_RESET;672return;673}674675/* Now cca_40mhz_workaround == CCA_40_MHZ_WA_RESET */676677sband = mld->wiphy->bands[NL80211_BAND_2GHZ];678679sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;680681he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);682683if (he_cap) {684/* we know that ours is writable */685struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap;686687he->he_cap_elem.phy_cap_info[0] |=688IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;689}690691mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_NONE;692}693694struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld)695{696unsigned long fw_id_bitmap = iwl_mld_get_fw_bss_vifs_ids(mld);697int fw_id;698699if (hweight8(fw_id_bitmap) != 1)700return NULL;701702fw_id = __ffs(fw_id_bitmap);703704return wiphy_dereference(mld->wiphy,705mld->fw_id_to_vif[fw_id]);706}707708709