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 /* If we have MLO enabled, then the firmware needs to enable 60 * address translation for the station(s) we add. That depends 61 * on having EHT enabled in firmware, which in turn depends on 62 * mac80211 in the code below. 63 * However, mac80211 doesn't enable HE/EHT until it has parsed 64 * the association response successfully, so just skip all that 65 * and enable both when we have MLO. 66 */ 67 if (ieee80211_vif_is_mld(vif)) { 68 iwl_mvm_mld_set_he_support(mvm, vif, cmd, cmd_ver); 69 if (cmd_ver == 2) 70 cmd->wifi_gen_v2.eht_support = cpu_to_le32(1); 71 else 72 cmd->wifi_gen.eht_support = 1; 73 return; 74 } 75 76 rcu_read_lock(); 77 for (link_id = 0; link_id < ARRAY_SIZE((vif)->link_conf); link_id++) { 78 link_conf = rcu_dereference(vif->link_conf[link_id]); 79 if (!link_conf) 80 continue; 81 82 if (link_conf->he_support) 83 iwl_mvm_mld_set_he_support(mvm, vif, cmd, cmd_ver); 84 85 /* It's not reasonable to have EHT without HE and FW API doesn't 86 * support it. Ignore EHT in this case. 87 */ 88 if (!link_conf->he_support && link_conf->eht_support) 89 continue; 90 91 if (link_conf->eht_support) { 92 if (cmd_ver == 2) 93 cmd->wifi_gen_v2.eht_support = cpu_to_le32(1); 94 else 95 cmd->wifi_gen.eht_support = 1; 96 break; 97 } 98 } 99 rcu_read_unlock(); 100 } 101 102 static int iwl_mvm_mld_mac_ctxt_send_cmd(struct iwl_mvm *mvm, 103 struct iwl_mac_config_cmd *cmd) 104 { 105 int ret = iwl_mvm_send_cmd_pdu(mvm, 106 WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD), 107 0, sizeof(*cmd), cmd); 108 if (ret) 109 IWL_ERR(mvm, "Failed to send MAC_CONFIG_CMD (action:%d): %d\n", 110 le32_to_cpu(cmd->action), ret); 111 return ret; 112 } 113 114 static int iwl_mvm_mld_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, 115 struct ieee80211_vif *vif, 116 u32 action, bool force_assoc_off) 117 { 118 struct iwl_mac_config_cmd cmd = {}; 119 u16 esr_transition_timeout; 120 121 WARN_ON(vif->type != NL80211_IFTYPE_STATION); 122 123 /* Fill the common data for all mac context types */ 124 iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); 125 126 /* 127 * We always want to hear MCAST frames, if we're not authorized yet, 128 * we'll drop them. 129 */ 130 cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP); 131 132 if (vif->p2p) 133 cmd.client.ctwin = 134 iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(mvm, vif); 135 136 if (vif->cfg.assoc && !force_assoc_off) { 137 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 138 139 cmd.client.is_assoc = 1; 140 141 if (!mvmvif->authorized && 142 fw_has_capa(&mvm->fw->ucode_capa, 143 IWL_UCODE_TLV_CAPA_COEX_HIGH_PRIO)) 144 cmd.client.data_policy |= 145 cpu_to_le16(COEX_HIGH_PRIORITY_ENABLE); 146 147 } else { 148 cmd.client.is_assoc = 0; 149 150 /* Allow beacons to pass through as long as we are not 151 * associated, or we do not have dtim period information. 152 */ 153 cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON); 154 } 155 156 cmd.client.assoc_id = cpu_to_le16(vif->cfg.aid); 157 if (ieee80211_vif_is_mld(vif)) { 158 esr_transition_timeout = 159 u16_get_bits(vif->cfg.eml_cap, 160 IEEE80211_EML_CAP_TRANSITION_TIMEOUT); 161 162 cmd.client.esr_transition_timeout = 163 min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU, 164 esr_transition_timeout); 165 cmd.client.medium_sync_delay = 166 cpu_to_le16(vif->cfg.eml_med_sync_delay); 167 } 168 169 if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p) 170 cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); 171 172 if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax) 173 cmd.client.data_policy |= 174 cpu_to_le16(iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(mvm, vif)); 175 176 return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 177 } 178 179 static int iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, 180 struct ieee80211_vif *vif, 181 u32 action) 182 { 183 struct iwl_mac_config_cmd cmd = {}; 184 185 WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); 186 187 iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); 188 189 cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_PROMISC | 190 MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT | 191 MAC_CFG_FILTER_ACCEPT_BEACON | 192 MAC_CFG_FILTER_ACCEPT_PROBE_REQ | 193 MAC_CFG_FILTER_ACCEPT_GRP); 194 195 return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 196 } 197 198 static int iwl_mvm_mld_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, 199 struct ieee80211_vif *vif, 200 u32 action) 201 { 202 struct iwl_mac_config_cmd cmd = {}; 203 204 WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); 205 206 iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); 207 208 cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON | 209 MAC_CFG_FILTER_ACCEPT_PROBE_REQ | 210 MAC_CFG_FILTER_ACCEPT_GRP); 211 212 return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 213 } 214 215 static int iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, 216 struct ieee80211_vif *vif, 217 u32 action) 218 { 219 struct iwl_mac_config_cmd cmd = {}; 220 221 WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); 222 223 iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); 224 225 cmd.p2p_dev.is_disc_extended = 226 iwl_mac_ctxt_p2p_dev_has_extended_disc(mvm, vif); 227 228 /* Override the filter flags to accept all management frames. This is 229 * needed to support both P2P device discovery using probe requests and 230 * P2P service discovery using action frames 231 */ 232 cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT); 233 234 return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 235 } 236 237 static int iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm *mvm, 238 struct ieee80211_vif *vif, 239 u32 action) 240 { 241 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 242 struct iwl_mac_config_cmd cmd = {}; 243 244 WARN_ON(vif->type != NL80211_IFTYPE_AP); 245 246 /* Fill the common data for all mac context types */ 247 iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); 248 249 iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(mvm, mvmvif, 250 &cmd.filter_flags, 251 MAC_CFG_FILTER_ACCEPT_PROBE_REQ, 252 MAC_CFG_FILTER_ACCEPT_BEACON); 253 254 return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 255 } 256 257 static int iwl_mvm_mld_mac_ctx_send(struct iwl_mvm *mvm, 258 struct ieee80211_vif *vif, 259 u32 action, bool force_assoc_off) 260 { 261 switch (vif->type) { 262 case NL80211_IFTYPE_STATION: 263 return iwl_mvm_mld_mac_ctxt_cmd_sta(mvm, vif, action, 264 force_assoc_off); 265 case NL80211_IFTYPE_AP: 266 return iwl_mvm_mld_mac_ctxt_cmd_ap_go(mvm, vif, action); 267 case NL80211_IFTYPE_MONITOR: 268 return iwl_mvm_mld_mac_ctxt_cmd_listener(mvm, vif, action); 269 case NL80211_IFTYPE_P2P_DEVICE: 270 return iwl_mvm_mld_mac_ctxt_cmd_p2p_device(mvm, vif, action); 271 case NL80211_IFTYPE_ADHOC: 272 return iwl_mvm_mld_mac_ctxt_cmd_ibss(mvm, vif, action); 273 default: 274 break; 275 } 276 277 return -EOPNOTSUPP; 278 } 279 280 int iwl_mvm_mld_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 281 { 282 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 283 int ret; 284 285 if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) 286 return -EOPNOTSUPP; 287 288 if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n", 289 vif->addr, ieee80211_vif_type_p2p(vif))) 290 return -EIO; 291 292 ret = iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD, 293 true); 294 if (ret) 295 return ret; 296 297 /* will only do anything at resume from D3 time */ 298 iwl_mvm_set_last_nonqos_seq(mvm, vif); 299 300 mvmvif->uploaded = true; 301 return 0; 302 } 303 304 int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm, 305 struct ieee80211_vif *vif, 306 bool force_assoc_off) 307 { 308 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 309 310 if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) 311 return -EOPNOTSUPP; 312 313 if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n", 314 vif->addr, ieee80211_vif_type_p2p(vif))) 315 return -EIO; 316 317 return iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY, 318 force_assoc_off); 319 } 320 321 int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 322 { 323 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 324 struct iwl_mac_config_cmd cmd = { 325 .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), 326 .id_and_color = cpu_to_le32(mvmvif->id), 327 }; 328 int ret; 329 330 if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) 331 return -EOPNOTSUPP; 332 333 if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n", 334 vif->addr, ieee80211_vif_type_p2p(vif))) 335 return -EIO; 336 337 ret = iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 338 if (ret) 339 return ret; 340 341 mvmvif->uploaded = false; 342 343 return 0; 344 } 345