Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/mld/ap.c
48285 views
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
365