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