1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 /* 3 * Copyright (C) 2022 - 2025 Intel Corporation 4 */ 5 #include "mvm.h" 6 7 static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm, 8 struct ieee80211_vif *vif, 9 struct iwl_mac_config_cmd *cmd, 10 int cmd_ver) 11 { 12 if (vif->type == NL80211_IFTYPE_AP) { 13 if (cmd_ver == 2) 14 cmd->wifi_gen_v2.he_ap_support = cpu_to_le16(1); 15 else 16 cmd->wifi_gen.he_ap_support = 1; 17 } else { 18 if (cmd_ver == 2) 19 cmd->wifi_gen_v2.he_support = cpu_to_le16(1); 20 else 21 cmd->wifi_gen.he_support = 1; 22 } 23 } 24 25 static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm, 26 struct ieee80211_vif *vif, 27 struct iwl_mac_config_cmd *cmd, 28 u32 action) 29 { 30 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 31 struct ieee80211_bss_conf *link_conf; 32 unsigned int link_id; 33 int cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, 34 WIDE_ID(MAC_CONF_GROUP, 35 MAC_CONFIG_CMD), 1); 36 37 if (WARN_ON(cmd_ver > 3)) 38 return; 39 40 cmd->id_and_color = cpu_to_le32(mvmvif->id); 41 cmd->action = cpu_to_le32(action); 42 43 cmd->mac_type = cpu_to_le32(iwl_mvm_get_mac_type(vif)); 44 45 memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN); 46 47 cmd->wifi_gen_v2.he_support = 0; 48 cmd->wifi_gen_v2.eht_support = 0; 49 50 /* should be set by specific context type handler */ 51 cmd->filter_flags = 0; 52 53 cmd->nic_not_ack_enabled = 54 cpu_to_le32(!iwl_mvm_is_nic_ack_enabled(mvm, vif)); 55 56 if (iwlwifi_mod_params.disable_11ax) 57 return; 58 59 rcu_read_lock(); 60 for (link_id = 0; link_id < ARRAY_SIZE((vif)->link_conf); link_id++) { 61 link_conf = rcu_dereference(vif->link_conf[link_id]); 62 if (!link_conf) 63 continue; 64 65 if (link_conf->he_support) 66 iwl_mvm_mld_set_he_support(mvm, vif, cmd, cmd_ver); 67 68 /* It's not reasonable to have EHT without HE and FW API doesn't 69 * support it. Ignore EHT in this case. 70 */ 71 if (!link_conf->he_support && link_conf->eht_support) 72 continue; 73 74 if (link_conf->eht_support) { 75 if (cmd_ver == 2) 76 cmd->wifi_gen_v2.eht_support = cpu_to_le32(1); 77 else 78 cmd->wifi_gen.eht_support = 1; 79 break; 80 } 81 } 82 rcu_read_unlock(); 83 } 84 85 static int iwl_mvm_mld_mac_ctxt_send_cmd(struct iwl_mvm *mvm, 86 struct iwl_mac_config_cmd *cmd) 87 { 88 int ret = iwl_mvm_send_cmd_pdu(mvm, 89 WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD), 90 0, sizeof(*cmd), cmd); 91 if (ret) 92 IWL_ERR(mvm, "Failed to send MAC_CONFIG_CMD (action:%d): %d\n", 93 le32_to_cpu(cmd->action), ret); 94 return ret; 95 } 96 97 static int iwl_mvm_mld_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, 98 struct ieee80211_vif *vif, 99 u32 action, bool force_assoc_off) 100 { 101 struct iwl_mac_config_cmd cmd = {}; 102 103 WARN_ON(vif->type != NL80211_IFTYPE_STATION); 104 105 /* Fill the common data for all mac context types */ 106 iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); 107 108 /* 109 * We always want to hear MCAST frames, if we're not authorized yet, 110 * we'll drop them. 111 */ 112 cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP); 113 114 if (vif->p2p) 115 cmd.client.ctwin = 116 iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(mvm, vif); 117 118 if (vif->cfg.assoc && !force_assoc_off) { 119 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 120 121 cmd.client.is_assoc = 1; 122 123 if (!mvmvif->authorized && 124 fw_has_capa(&mvm->fw->ucode_capa, 125 IWL_UCODE_TLV_CAPA_COEX_HIGH_PRIO)) 126 cmd.client.data_policy |= 127 cpu_to_le16(COEX_HIGH_PRIORITY_ENABLE); 128 129 } else { 130 cmd.client.is_assoc = 0; 131 132 /* Allow beacons to pass through as long as we are not 133 * associated, or we do not have dtim period information. 134 */ 135 cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON); 136 } 137 138 cmd.client.assoc_id = cpu_to_le16(vif->cfg.aid); 139 140 if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p) 141 cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); 142 143 if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax) 144 cmd.client.data_policy |= 145 cpu_to_le16(iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(mvm, vif)); 146 147 return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 148 } 149 150 static int iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, 151 struct ieee80211_vif *vif, 152 u32 action) 153 { 154 struct iwl_mac_config_cmd cmd = {}; 155 156 WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); 157 158 iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); 159 160 cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_PROMISC | 161 MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT | 162 MAC_CFG_FILTER_ACCEPT_BEACON | 163 MAC_CFG_FILTER_ACCEPT_PROBE_REQ | 164 MAC_CFG_FILTER_ACCEPT_GRP); 165 166 return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 167 } 168 169 static int iwl_mvm_mld_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, 170 struct ieee80211_vif *vif, 171 u32 action) 172 { 173 struct iwl_mac_config_cmd cmd = {}; 174 175 WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); 176 177 iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); 178 179 cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON | 180 MAC_CFG_FILTER_ACCEPT_PROBE_REQ | 181 MAC_CFG_FILTER_ACCEPT_GRP); 182 183 return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 184 } 185 186 static int iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, 187 struct ieee80211_vif *vif, 188 u32 action) 189 { 190 struct iwl_mac_config_cmd cmd = {}; 191 192 WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); 193 194 iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); 195 196 cmd.p2p_dev.is_disc_extended = 197 iwl_mac_ctxt_p2p_dev_has_extended_disc(mvm, vif); 198 199 /* Override the filter flags to accept all management frames. This is 200 * needed to support both P2P device discovery using probe requests and 201 * P2P service discovery using action frames 202 */ 203 cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT); 204 205 return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 206 } 207 208 static int iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm *mvm, 209 struct ieee80211_vif *vif, 210 u32 action) 211 { 212 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 213 struct iwl_mac_config_cmd cmd = {}; 214 215 WARN_ON(vif->type != NL80211_IFTYPE_AP); 216 217 /* Fill the common data for all mac context types */ 218 iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); 219 220 iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(mvm, mvmvif, 221 &cmd.filter_flags, 222 MAC_CFG_FILTER_ACCEPT_PROBE_REQ, 223 MAC_CFG_FILTER_ACCEPT_BEACON); 224 225 return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 226 } 227 228 static int iwl_mvm_mld_mac_ctx_send(struct iwl_mvm *mvm, 229 struct ieee80211_vif *vif, 230 u32 action, bool force_assoc_off) 231 { 232 switch (vif->type) { 233 case NL80211_IFTYPE_STATION: 234 return iwl_mvm_mld_mac_ctxt_cmd_sta(mvm, vif, action, 235 force_assoc_off); 236 case NL80211_IFTYPE_AP: 237 return iwl_mvm_mld_mac_ctxt_cmd_ap_go(mvm, vif, action); 238 case NL80211_IFTYPE_MONITOR: 239 return iwl_mvm_mld_mac_ctxt_cmd_listener(mvm, vif, action); 240 case NL80211_IFTYPE_P2P_DEVICE: 241 return iwl_mvm_mld_mac_ctxt_cmd_p2p_device(mvm, vif, action); 242 case NL80211_IFTYPE_ADHOC: 243 return iwl_mvm_mld_mac_ctxt_cmd_ibss(mvm, vif, action); 244 default: 245 break; 246 } 247 248 return -EOPNOTSUPP; 249 } 250 251 int iwl_mvm_mld_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 252 { 253 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 254 int ret; 255 256 if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n", 257 vif->addr, ieee80211_vif_type_p2p(vif))) 258 return -EIO; 259 260 ret = iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD, 261 true); 262 if (ret) 263 return ret; 264 265 /* will only do anything at resume from D3 time */ 266 iwl_mvm_set_last_nonqos_seq(mvm, vif); 267 268 mvmvif->uploaded = true; 269 return 0; 270 } 271 272 int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm, 273 struct ieee80211_vif *vif, 274 bool force_assoc_off) 275 { 276 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 277 278 if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n", 279 vif->addr, ieee80211_vif_type_p2p(vif))) 280 return -EIO; 281 282 return iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY, 283 force_assoc_off); 284 } 285 286 int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 287 { 288 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 289 struct iwl_mac_config_cmd cmd = { 290 .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), 291 .id_and_color = cpu_to_le32(mvmvif->id), 292 }; 293 int ret; 294 295 if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n", 296 vif->addr, ieee80211_vif_type_p2p(vif))) 297 return -EIO; 298 299 ret = iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 300 if (ret) 301 return ret; 302 303 mvmvif->uploaded = false; 304 305 return 0; 306 } 307