| // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
| /* |
| * Copyright (C) 2022 - 2024 Intel Corporation |
| */ |
| #include "mvm.h" |
| |
| static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm, |
| struct ieee80211_vif *vif, |
| struct iwl_mac_config_cmd *cmd) |
| { |
| if (vif->type == NL80211_IFTYPE_AP) |
| cmd->he_ap_support = cpu_to_le16(1); |
| else |
| cmd->he_support = cpu_to_le16(1); |
| } |
| |
| static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm, |
| struct ieee80211_vif *vif, |
| struct iwl_mac_config_cmd *cmd, |
| u32 action) |
| { |
| struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
| struct ieee80211_bss_conf *link_conf; |
| unsigned int link_id; |
| |
| cmd->id_and_color = cpu_to_le32(mvmvif->id); |
| cmd->action = cpu_to_le32(action); |
| |
| cmd->mac_type = cpu_to_le32(iwl_mvm_get_mac_type(vif)); |
| |
| memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN); |
| |
| cmd->he_support = 0; |
| cmd->eht_support = 0; |
| |
| /* should be set by specific context type handler */ |
| cmd->filter_flags = 0; |
| |
| cmd->nic_not_ack_enabled = |
| cpu_to_le32(!iwl_mvm_is_nic_ack_enabled(mvm, vif)); |
| |
| if (iwlwifi_mod_params.disable_11ax) |
| return; |
| |
| /* If we have MLO enabled, then the firmware needs to enable |
| * address translation for the station(s) we add. That depends |
| * on having EHT enabled in firmware, which in turn depends on |
| * mac80211 in the code below. |
| * However, mac80211 doesn't enable HE/EHT until it has parsed |
| * the association response successfully, so just skip all that |
| * and enable both when we have MLO. |
| */ |
| if (ieee80211_vif_is_mld(vif)) { |
| iwl_mvm_mld_set_he_support(mvm, vif, cmd); |
| cmd->eht_support = cpu_to_le32(1); |
| return; |
| } |
| |
| rcu_read_lock(); |
| for (link_id = 0; link_id < ARRAY_SIZE((vif)->link_conf); link_id++) { |
| link_conf = rcu_dereference(vif->link_conf[link_id]); |
| if (!link_conf) |
| continue; |
| |
| if (link_conf->he_support) |
| iwl_mvm_mld_set_he_support(mvm, vif, cmd); |
| |
| /* it's not reasonable to have EHT without HE and FW API doesn't |
| * support it. Ignore EHT in this case. |
| */ |
| if (!link_conf->he_support && link_conf->eht_support) |
| continue; |
| |
| if (link_conf->eht_support) { |
| cmd->eht_support = cpu_to_le32(1); |
| break; |
| } |
| } |
| rcu_read_unlock(); |
| } |
| |
| static int iwl_mvm_mld_mac_ctxt_send_cmd(struct iwl_mvm *mvm, |
| struct iwl_mac_config_cmd *cmd) |
| { |
| int ret = iwl_mvm_send_cmd_pdu(mvm, |
| WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD), |
| 0, sizeof(*cmd), cmd); |
| if (ret) |
| IWL_ERR(mvm, "Failed to send MAC_CONFIG_CMD (action:%d): %d\n", |
| le32_to_cpu(cmd->action), ret); |
| return ret; |
| } |
| |
| static int iwl_mvm_mld_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, |
| struct ieee80211_vif *vif, |
| u32 action, bool force_assoc_off) |
| { |
| struct iwl_mac_config_cmd cmd = {}; |
| u16 esr_transition_timeout; |
| |
| WARN_ON(vif->type != NL80211_IFTYPE_STATION); |
| |
| /* Fill the common data for all mac context types */ |
| iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); |
| |
| /* |
| * We always want to hear MCAST frames, if we're not authorized yet, |
| * we'll drop them. |
| */ |
| cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP); |
| |
| if (vif->p2p) |
| cmd.client.ctwin = |
| iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(mvm, vif); |
| |
| if (vif->cfg.assoc && !force_assoc_off) { |
| struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
| |
| cmd.client.is_assoc = 1; |
| |
| if (!mvmvif->authorized && |
| fw_has_capa(&mvm->fw->ucode_capa, |
| IWL_UCODE_TLV_CAPA_COEX_HIGH_PRIO)) |
| cmd.client.data_policy |= |
| cpu_to_le16(COEX_HIGH_PRIORITY_ENABLE); |
| |
| } else { |
| cmd.client.is_assoc = 0; |
| |
| /* Allow beacons to pass through as long as we are not |
| * associated, or we do not have dtim period information. |
| */ |
| cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON); |
| } |
| |
| cmd.client.assoc_id = cpu_to_le16(vif->cfg.aid); |
| if (ieee80211_vif_is_mld(vif)) { |
| esr_transition_timeout = |
| u16_get_bits(vif->cfg.eml_cap, |
| IEEE80211_EML_CAP_TRANSITION_TIMEOUT); |
| |
| cmd.client.esr_transition_timeout = |
| min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU, |
| esr_transition_timeout); |
| cmd.client.medium_sync_delay = |
| cpu_to_le16(vif->cfg.eml_med_sync_delay); |
| } |
| |
| if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p) |
| cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); |
| |
| if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax) |
| cmd.client.data_policy |= |
| cpu_to_le16(iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(mvm, vif)); |
| |
| return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); |
| } |
| |
| static int iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, |
| struct ieee80211_vif *vif, |
| u32 action) |
| { |
| struct iwl_mac_config_cmd cmd = {}; |
| |
| WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); |
| |
| iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); |
| |
| cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_PROMISC | |
| MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT | |
| MAC_CFG_FILTER_ACCEPT_BEACON | |
| MAC_CFG_FILTER_ACCEPT_PROBE_REQ | |
| MAC_CFG_FILTER_ACCEPT_GRP); |
| |
| return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); |
| } |
| |
| static int iwl_mvm_mld_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, |
| struct ieee80211_vif *vif, |
| u32 action) |
| { |
| struct iwl_mac_config_cmd cmd = {}; |
| |
| WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); |
| |
| iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); |
| |
| cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON | |
| MAC_CFG_FILTER_ACCEPT_PROBE_REQ | |
| MAC_CFG_FILTER_ACCEPT_GRP); |
| |
| return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); |
| } |
| |
| static int iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, |
| struct ieee80211_vif *vif, |
| u32 action) |
| { |
| struct iwl_mac_config_cmd cmd = {}; |
| |
| WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); |
| |
| iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); |
| |
| cmd.p2p_dev.is_disc_extended = |
| iwl_mac_ctxt_p2p_dev_has_extended_disc(mvm, vif); |
| |
| /* Override the filter flags to accept all management frames. This is |
| * needed to support both P2P device discovery using probe requests and |
| * P2P service discovery using action frames |
| */ |
| cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT); |
| |
| return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); |
| } |
| |
| static int iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm *mvm, |
| struct ieee80211_vif *vif, |
| u32 action) |
| { |
| struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
| struct iwl_mac_config_cmd cmd = {}; |
| |
| WARN_ON(vif->type != NL80211_IFTYPE_AP); |
| |
| /* Fill the common data for all mac context types */ |
| iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); |
| |
| iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(mvm, mvmvif, |
| &cmd.filter_flags, |
| MAC_CFG_FILTER_ACCEPT_PROBE_REQ, |
| MAC_CFG_FILTER_ACCEPT_BEACON); |
| |
| return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); |
| } |
| |
| static int iwl_mvm_mld_mac_ctx_send(struct iwl_mvm *mvm, |
| struct ieee80211_vif *vif, |
| u32 action, bool force_assoc_off) |
| { |
| switch (vif->type) { |
| case NL80211_IFTYPE_STATION: |
| return iwl_mvm_mld_mac_ctxt_cmd_sta(mvm, vif, action, |
| force_assoc_off); |
| case NL80211_IFTYPE_AP: |
| return iwl_mvm_mld_mac_ctxt_cmd_ap_go(mvm, vif, action); |
| case NL80211_IFTYPE_MONITOR: |
| return iwl_mvm_mld_mac_ctxt_cmd_listener(mvm, vif, action); |
| case NL80211_IFTYPE_P2P_DEVICE: |
| return iwl_mvm_mld_mac_ctxt_cmd_p2p_device(mvm, vif, action); |
| case NL80211_IFTYPE_ADHOC: |
| return iwl_mvm_mld_mac_ctxt_cmd_ibss(mvm, vif, action); |
| default: |
| break; |
| } |
| |
| return -EOPNOTSUPP; |
| } |
| |
| int iwl_mvm_mld_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) |
| { |
| struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
| int ret; |
| |
| if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) |
| return -EOPNOTSUPP; |
| |
| if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n", |
| vif->addr, ieee80211_vif_type_p2p(vif))) |
| return -EIO; |
| |
| ret = iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD, |
| true); |
| if (ret) |
| return ret; |
| |
| /* will only do anything at resume from D3 time */ |
| iwl_mvm_set_last_nonqos_seq(mvm, vif); |
| |
| mvmvif->uploaded = true; |
| return 0; |
| } |
| |
| int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm, |
| struct ieee80211_vif *vif, |
| bool force_assoc_off) |
| { |
| struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
| |
| if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) |
| return -EOPNOTSUPP; |
| |
| if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n", |
| vif->addr, ieee80211_vif_type_p2p(vif))) |
| return -EIO; |
| |
| return iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY, |
| force_assoc_off); |
| } |
| |
| int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) |
| { |
| struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
| struct iwl_mac_config_cmd cmd = { |
| .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), |
| .id_and_color = cpu_to_le32(mvmvif->id), |
| }; |
| int ret; |
| |
| if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) |
| return -EOPNOTSUPP; |
| |
| if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n", |
| vif->addr, ieee80211_vif_type_p2p(vif))) |
| return -EIO; |
| |
| ret = iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); |
| if (ret) |
| return ret; |
| |
| mvmvif->uploaded = false; |
| |
| return 0; |
| } |