1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 /* 3 * Copyright (C) 2022 - 2023 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_FILTER_IN_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 only probe requests */ 209 cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); 210 211 return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 212 } 213 214 static int iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm *mvm, 215 struct ieee80211_vif *vif, 216 u32 action) 217 { 218 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 219 struct iwl_mac_config_cmd cmd = {}; 220 221 WARN_ON(vif->type != NL80211_IFTYPE_AP); 222 223 /* Fill the common data for all mac context types */ 224 iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); 225 226 iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(mvm, mvmvif, 227 &cmd.filter_flags, 228 MAC_CFG_FILTER_ACCEPT_PROBE_REQ, 229 MAC_CFG_FILTER_ACCEPT_BEACON); 230 231 return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 232 } 233 234 static int iwl_mvm_mld_mac_ctx_send(struct iwl_mvm *mvm, 235 struct ieee80211_vif *vif, 236 u32 action, bool force_assoc_off) 237 { 238 switch (vif->type) { 239 case NL80211_IFTYPE_STATION: 240 return iwl_mvm_mld_mac_ctxt_cmd_sta(mvm, vif, action, 241 force_assoc_off); 242 case NL80211_IFTYPE_AP: 243 return iwl_mvm_mld_mac_ctxt_cmd_ap_go(mvm, vif, action); 244 case NL80211_IFTYPE_MONITOR: 245 return iwl_mvm_mld_mac_ctxt_cmd_listener(mvm, vif, action); 246 case NL80211_IFTYPE_P2P_DEVICE: 247 return iwl_mvm_mld_mac_ctxt_cmd_p2p_device(mvm, vif, action); 248 case NL80211_IFTYPE_ADHOC: 249 return iwl_mvm_mld_mac_ctxt_cmd_ibss(mvm, vif, action); 250 default: 251 break; 252 } 253 254 return -EOPNOTSUPP; 255 } 256 257 int iwl_mvm_mld_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 258 { 259 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 260 int ret; 261 262 if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) 263 return -EOPNOTSUPP; 264 265 if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n", 266 vif->addr, ieee80211_vif_type_p2p(vif))) 267 return -EIO; 268 269 ret = iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD, 270 true); 271 if (ret) 272 return ret; 273 274 /* will only do anything at resume from D3 time */ 275 iwl_mvm_set_last_nonqos_seq(mvm, vif); 276 277 mvmvif->uploaded = true; 278 return 0; 279 } 280 281 int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm, 282 struct ieee80211_vif *vif, 283 bool force_assoc_off) 284 { 285 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 286 287 if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) 288 return -EOPNOTSUPP; 289 290 if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n", 291 vif->addr, ieee80211_vif_type_p2p(vif))) 292 return -EIO; 293 294 return iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY, 295 force_assoc_off); 296 } 297 298 int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 299 { 300 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 301 struct iwl_mac_config_cmd cmd = { 302 .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), 303 .id_and_color = cpu_to_le32(mvmvif->id), 304 }; 305 int ret; 306 307 if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) 308 return -EOPNOTSUPP; 309 310 if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n", 311 vif->addr, ieee80211_vif_type_p2p(vif))) 312 return -EIO; 313 314 ret = iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); 315 if (ret) 316 return ret; 317 318 mvmvif->uploaded = false; 319 320 return 0; 321 } 322