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