1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 /* 3 * Copyright (C) 2024 Intel Corporation 4 */ 5 #include <linux/crc32.h> 6 7 #include <net/mac80211.h> 8 9 #include "ap.h" 10 #include "hcmd.h" 11 #include "tx.h" 12 #include "power.h" 13 #include "key.h" 14 #include "phy.h" 15 #include "iwl-utils.h" 16 17 #include "fw/api/sta.h" 18 19 void iwl_mld_set_tim_idx(struct iwl_mld *mld, __le32 *tim_index, 20 u8 *beacon, u32 frame_size) 21 { 22 u32 tim_idx; 23 struct ieee80211_mgmt *mgmt = (void *)beacon; 24 25 /* The index is relative to frame start but we start looking at the 26 * variable-length part of the beacon. 27 */ 28 tim_idx = mgmt->u.beacon.variable - beacon; 29 30 /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ 31 while ((tim_idx < (frame_size - 2)) && 32 (beacon[tim_idx] != WLAN_EID_TIM)) 33 tim_idx += beacon[tim_idx + 1] + 2; 34 35 /* If TIM field was found, set variables */ 36 if ((tim_idx < (frame_size - 1)) && beacon[tim_idx] == WLAN_EID_TIM) 37 *tim_index = cpu_to_le32(tim_idx); 38 else 39 IWL_WARN(mld, "Unable to find TIM Element in beacon\n"); 40 } 41 42 u8 iwl_mld_get_rate_flags(struct iwl_mld *mld, 43 struct ieee80211_tx_info *info, 44 struct ieee80211_vif *vif, 45 struct ieee80211_bss_conf *link, 46 enum nl80211_band band) 47 { 48 u32 legacy = link->beacon_tx_rate.control[band].legacy; 49 u32 rate_idx, rate_flags = 0, fw_rate; 50 51 /* if beacon rate was configured try using it */ 52 if (hweight32(legacy) == 1) { 53 u32 rate = ffs(legacy) - 1; 54 struct ieee80211_supported_band *sband = 55 mld->hw->wiphy->bands[band]; 56 57 rate_idx = sband->bitrates[rate].hw_value; 58 } else { 59 rate_idx = iwl_mld_get_lowest_rate(mld, info, vif); 60 } 61 62 if (rate_idx <= IWL_LAST_CCK_RATE) 63 rate_flags = IWL_MAC_BEACON_CCK; 64 65 /* Legacy rates are indexed as follows: 66 * 0 - 3 for CCK and 0 - 7 for OFDM. 67 */ 68 fw_rate = (rate_idx >= IWL_FIRST_OFDM_RATE ? 69 rate_idx - IWL_FIRST_OFDM_RATE : rate_idx); 70 71 return fw_rate | rate_flags; 72 } 73 74 int iwl_mld_send_beacon_template_cmd(struct iwl_mld *mld, 75 struct sk_buff *beacon, 76 struct iwl_mac_beacon_cmd *cmd) 77 { 78 struct iwl_host_cmd hcmd = { 79 .id = BEACON_TEMPLATE_CMD, 80 }; 81 82 hcmd.len[0] = sizeof(*cmd); 83 hcmd.data[0] = cmd; 84 85 hcmd.len[1] = beacon->len; 86 hcmd.data[1] = beacon->data; 87 hcmd.dataflags[1] = IWL_HCMD_DFL_DUP; 88 89 return iwl_mld_send_cmd(mld, &hcmd); 90 } 91 92 static int iwl_mld_fill_beacon_template_cmd(struct iwl_mld *mld, 93 struct ieee80211_vif *vif, 94 struct sk_buff *beacon, 95 struct iwl_mac_beacon_cmd *cmd, 96 struct ieee80211_bss_conf *link) 97 { 98 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); 99 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(beacon); 100 struct ieee80211_chanctx_conf *ctx; 101 bool enable_fils; 102 u16 flags = 0; 103 104 lockdep_assert_wiphy(mld->wiphy); 105 106 if (WARN_ON(!mld_link)) 107 return -EINVAL; 108 109 cmd->link_id = cpu_to_le32(mld_link->fw_id); 110 111 ctx = wiphy_dereference(mld->wiphy, link->chanctx_conf); 112 if (WARN_ON(!ctx || !ctx->def.chan)) 113 return -EINVAL; 114 115 enable_fils = cfg80211_channel_is_psc(ctx->def.chan) || 116 (ctx->def.chan->band == NL80211_BAND_6GHZ && 117 ctx->def.width >= NL80211_CHAN_WIDTH_80); 118 119 if (enable_fils) { 120 flags |= IWL_MAC_BEACON_FILS; 121 cmd->short_ssid = cpu_to_le32(~crc32_le(~0, vif->cfg.ssid, 122 vif->cfg.ssid_len)); 123 } 124 125 cmd->byte_cnt = cpu_to_le16((u16)beacon->len); 126 127 flags |= iwl_mld_get_rate_flags(mld, info, vif, link, 128 ctx->def.chan->band); 129 130 cmd->flags = cpu_to_le16(flags); 131 132 if (vif->type == NL80211_IFTYPE_AP) { 133 iwl_mld_set_tim_idx(mld, &cmd->tim_idx, 134 beacon->data, beacon->len); 135 136 cmd->btwt_offset = 137 cpu_to_le32(iwl_find_ie_offset(beacon->data, 138 WLAN_EID_S1G_TWT, 139 beacon->len)); 140 } 141 142 cmd->csa_offset = 143 cpu_to_le32(iwl_find_ie_offset(beacon->data, 144 WLAN_EID_CHANNEL_SWITCH, 145 beacon->len)); 146 cmd->ecsa_offset = 147 cpu_to_le32(iwl_find_ie_offset(beacon->data, 148 WLAN_EID_EXT_CHANSWITCH_ANN, 149 beacon->len)); 150 151 return 0; 152 } 153 154 /* The beacon template for the AP/GO/IBSS has changed and needs update */ 155 int iwl_mld_update_beacon_template(struct iwl_mld *mld, 156 struct ieee80211_vif *vif, 157 struct ieee80211_bss_conf *link_conf) 158 { 159 struct iwl_mac_beacon_cmd cmd = {}; 160 struct sk_buff *beacon; 161 int ret; 162 #ifdef CONFIG_IWLWIFI_DEBUGFS 163 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 164 #endif 165 166 WARN_ON(vif->type != NL80211_IFTYPE_AP && 167 vif->type != NL80211_IFTYPE_ADHOC); 168 169 if (IWL_MLD_NON_TRANSMITTING_AP) 170 return 0; 171 172 #ifdef CONFIG_IWLWIFI_DEBUGFS 173 if (mld_vif->beacon_inject_active) { 174 IWL_DEBUG_INFO(mld, 175 "Can't update template, beacon injection's active\n"); 176 return -EBUSY; 177 } 178 179 #endif 180 beacon = ieee80211_beacon_get_template(mld->hw, vif, NULL, 181 link_conf->link_id); 182 if (!beacon) 183 return -ENOMEM; 184 185 ret = iwl_mld_fill_beacon_template_cmd(mld, vif, beacon, &cmd, 186 link_conf); 187 188 if (!ret) 189 ret = iwl_mld_send_beacon_template_cmd(mld, beacon, &cmd); 190 191 dev_kfree_skb(beacon); 192 193 return ret; 194 } 195 196 void iwl_mld_free_ap_early_key(struct iwl_mld *mld, 197 struct ieee80211_key_conf *key, 198 struct iwl_mld_vif *mld_vif) 199 { 200 struct iwl_mld_link *link; 201 202 if (WARN_ON(key->link_id < 0)) 203 return; 204 205 link = iwl_mld_link_dereference_check(mld_vif, key->link_id); 206 if (WARN_ON(!link)) 207 return; 208 209 for (int i = 0; i < ARRAY_SIZE(link->ap_early_keys); i++) { 210 if (link->ap_early_keys[i] != key) 211 continue; 212 /* Those weren't sent to FW, so should be marked as INVALID */ 213 if (WARN_ON(key->hw_key_idx != STA_KEY_IDX_INVALID)) 214 key->hw_key_idx = STA_KEY_IDX_INVALID; 215 link->ap_early_keys[i] = NULL; 216 } 217 } 218 219 int iwl_mld_store_ap_early_key(struct iwl_mld *mld, 220 struct ieee80211_key_conf *key, 221 struct iwl_mld_vif *mld_vif) 222 { 223 struct iwl_mld_link *link; 224 225 if (WARN_ON(key->link_id < 0)) 226 return -EINVAL; 227 228 link = iwl_mld_link_dereference_check(mld_vif, key->link_id); 229 if (WARN_ON(!link)) 230 return -EINVAL; 231 232 for (int i = 0; i < ARRAY_SIZE(link->ap_early_keys); i++) { 233 if (!link->ap_early_keys[i]) { 234 link->ap_early_keys[i] = key; 235 return 0; 236 } 237 } 238 239 return -ENOSPC; 240 } 241 242 static int iwl_mld_send_ap_early_keys(struct iwl_mld *mld, 243 struct ieee80211_vif *vif, 244 struct ieee80211_bss_conf *link) 245 { 246 struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); 247 int ret = 0; 248 249 if (WARN_ON(!link)) 250 return -EINVAL; 251 252 for (int i = 0; i < ARRAY_SIZE(mld_link->ap_early_keys); i++) { 253 struct ieee80211_key_conf *key = mld_link->ap_early_keys[i]; 254 255 if (!key) 256 continue; 257 258 mld_link->ap_early_keys[i] = NULL; 259 260 ret = iwl_mld_add_key(mld, vif, NULL, key); 261 if (ret) 262 break; 263 } 264 return ret; 265 } 266 267 int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, 268 struct ieee80211_vif *vif, 269 struct ieee80211_bss_conf *link) 270 { 271 struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 272 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 273 struct ieee80211_chanctx_conf *ctx; 274 int ret; 275 276 if (vif->type == NL80211_IFTYPE_AP) 277 iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link); 278 279 ret = iwl_mld_update_beacon_template(mld, vif, link); 280 if (ret) 281 return ret; 282 283 /* the link should be already activated when assigning chan context, 284 * and LINK_CONTEXT_MODIFY_EHT_PARAMS is deprecated 285 */ 286 ret = iwl_mld_change_link_in_fw(mld, link, 287 LINK_CONTEXT_MODIFY_ALL & 288 ~(LINK_CONTEXT_MODIFY_ACTIVE | 289 LINK_CONTEXT_MODIFY_EHT_PARAMS)); 290 if (ret) 291 return ret; 292 293 ret = iwl_mld_add_mcast_sta(mld, vif, link); 294 if (ret) 295 return ret; 296 297 mld_vif->ap_ibss_active = true; 298 299 if (vif->p2p && mld->p2p_device_vif) { 300 ret = iwl_mld_mac_fw_action(mld, mld->p2p_device_vif, 301 FW_CTXT_ACTION_MODIFY); 302 if (ret) { 303 mld_vif->ap_ibss_active = false; 304 goto rm_mcast; 305 } 306 } 307 308 ret = iwl_mld_add_bcast_sta(mld, vif, link); 309 if (ret) 310 goto update_p2p_dev; 311 312 /* Those keys were configured by the upper layers before starting the 313 * AP. Now that it is started and the bcast and mcast sta were added to 314 * the FW, we can add the keys too. 315 */ 316 ret = iwl_mld_send_ap_early_keys(mld, vif, link); 317 if (ret) 318 goto rm_bcast; 319 320 if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP) 321 iwl_mld_vif_update_low_latency(mld, vif, true, 322 LOW_LATENCY_VIF_TYPE); 323 324 /* When the channel context was added, the link is not yet active, so 325 * min_def is always used. Update the PHY again here in case def should 326 * actually be used. 327 */ 328 ctx = wiphy_dereference(mld->wiphy, link->chanctx_conf); 329 iwl_mld_update_phy_chandef(mld, ctx); 330 331 return 0; 332 rm_bcast: 333 iwl_mld_remove_bcast_sta(mld, vif, link); 334 update_p2p_dev: 335 mld_vif->ap_ibss_active = false; 336 if (vif->p2p && mld->p2p_device_vif) 337 iwl_mld_mac_fw_action(mld, mld->p2p_device_vif, 338 FW_CTXT_ACTION_MODIFY); 339 rm_mcast: 340 iwl_mld_remove_mcast_sta(mld, vif, link); 341 return ret; 342 } 343 344 void iwl_mld_stop_ap_ibss(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 345 struct ieee80211_bss_conf *link) 346 { 347 struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 348 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 349 350 mld_vif->ap_ibss_active = false; 351 352 if (vif->p2p && mld->p2p_device_vif) 353 iwl_mld_mac_fw_action(mld, mld->p2p_device_vif, 354 FW_CTXT_ACTION_MODIFY); 355 356 if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP) 357 iwl_mld_vif_update_low_latency(mld, vif, false, 358 LOW_LATENCY_VIF_TYPE); 359 360 iwl_mld_remove_bcast_sta(mld, vif, link); 361 362 iwl_mld_remove_mcast_sta(mld, vif, link); 363 } 364