1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3 * KUnit tests for channel helper functions
4 *
5 * Copyright (C) 2024-2025 Intel Corporation
6 */
7 #include <kunit/test.h>
8 #include <kunit/test-bug.h>
9
10 #include "utils.h"
11
12 #include <linux/device.h>
13
14 #include "fw/api/scan.h"
15 #include "fw/api/mac-cfg.h"
16 #include "iwl-trans.h"
17 #include "mld.h"
18 #include "iface.h"
19 #include "link.h"
20 #include "phy.h"
21 #include "sta.h"
22
iwlmld_kunit_test_init(struct kunit * test)23 int iwlmld_kunit_test_init(struct kunit *test)
24 {
25 struct iwl_mld *mld;
26 struct iwl_trans *trans;
27 const struct iwl_cfg *cfg;
28 struct iwl_fw *fw;
29 struct ieee80211_hw *hw;
30
31 KUNIT_ALLOC_AND_ASSERT(test, trans);
32 KUNIT_ALLOC_AND_ASSERT(test, trans->dev);
33 KUNIT_ALLOC_AND_ASSERT(test, cfg);
34 KUNIT_ALLOC_AND_ASSERT(test, fw);
35 KUNIT_ALLOC_AND_ASSERT(test, hw);
36 KUNIT_ALLOC_AND_ASSERT(test, hw->wiphy);
37
38 mutex_init(&hw->wiphy->mtx);
39
40 /* Allocate and initialize the mld structure */
41 KUNIT_ALLOC_AND_ASSERT(test, mld);
42 iwl_construct_mld(mld, trans, cfg, fw, hw, NULL);
43
44 fw->ucode_capa.num_stations = IWL_STATION_COUNT_MAX;
45 fw->ucode_capa.num_links = IWL_FW_MAX_LINK_ID + 1;
46
47 mld->fwrt.trans = trans;
48 mld->fwrt.fw = fw;
49 mld->fwrt.dev = trans->dev;
50
51 /* TODO: add priv_size to hw allocation and setup hw->priv to enable
52 * testing mac80211 callbacks
53 */
54
55 KUNIT_ALLOC_AND_ASSERT(test, mld->nvm_data);
56 KUNIT_ALLOC_AND_ASSERT_SIZE(test, mld->scan.cmd,
57 sizeof(struct iwl_scan_req_umac_v17));
58 mld->scan.cmd_size = sizeof(struct iwl_scan_req_umac_v17);
59
60 /* This is not the state at the end of the regular opmode_start,
61 * but it is more common to need it. Explicitly undo this if needed.
62 */
63 mld->trans->state = IWL_TRANS_FW_ALIVE;
64 mld->fw_status.running = true;
65
66 /* Avoid passing mld struct around */
67 test->priv = mld;
68 return 0;
69 }
70
IWL_MLD_ALLOC_FN(link,bss_conf)71 IWL_MLD_ALLOC_FN(link, bss_conf)
72
73 static void iwlmld_kunit_init_link(struct ieee80211_vif *vif,
74 struct ieee80211_bss_conf *link,
75 struct iwl_mld_link *mld_link, int link_id)
76 {
77 struct kunit *test = kunit_get_current_test();
78 struct iwl_mld *mld = test->priv;
79 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
80 int ret;
81
82 /* setup mac80211 link */
83 rcu_assign_pointer(vif->link_conf[link_id], link);
84 link->link_id = link_id;
85 link->vif = vif;
86 link->beacon_int = 100;
87 link->dtim_period = 3;
88 link->qos = true;
89
90 /* and mld_link */
91 ret = iwl_mld_allocate_link_fw_id(mld, &mld_link->fw_id, link);
92 KUNIT_ASSERT_EQ(test, ret, 0);
93 rcu_assign_pointer(mld_vif->link[link_id], mld_link);
94 rcu_assign_pointer(vif->link_conf[link_id], link);
95 }
96
IWL_MLD_ALLOC_FN(vif,vif)97 IWL_MLD_ALLOC_FN(vif, vif)
98
99 /* Helper function to add and initialize a VIF for KUnit tests */
100 struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type)
101 {
102 struct kunit *test = kunit_get_current_test();
103 struct iwl_mld *mld = test->priv;
104 struct ieee80211_vif *vif;
105 struct iwl_mld_vif *mld_vif;
106 int ret;
107
108 /* TODO: support more types */
109 KUNIT_ASSERT_EQ(test, type, NL80211_IFTYPE_STATION);
110
111 KUNIT_ALLOC_AND_ASSERT_SIZE(test, vif,
112 sizeof(*vif) + sizeof(*mld_vif));
113
114 vif->type = type;
115 mld_vif = iwl_mld_vif_from_mac80211(vif);
116 mld_vif->mld = mld;
117
118 ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif);
119 KUNIT_ASSERT_EQ(test, ret, 0);
120
121 /* TODO: revisit (task=EHT) */
122 if (mlo)
123 return vif;
124
125 /* Initialize the default link */
126 iwlmld_kunit_init_link(vif, &vif->bss_conf, &mld_vif->deflink, 0);
127
128 return vif;
129 }
130
131 /* Use only for MLO vif */
132 struct ieee80211_bss_conf *
iwlmld_kunit_add_link(struct ieee80211_vif * vif,int link_id)133 iwlmld_kunit_add_link(struct ieee80211_vif *vif, int link_id)
134 {
135 struct kunit *test = kunit_get_current_test();
136 struct ieee80211_bss_conf *link;
137 struct iwl_mld_link *mld_link;
138
139 KUNIT_ALLOC_AND_ASSERT(test, link);
140 KUNIT_ALLOC_AND_ASSERT(test, mld_link);
141
142 iwlmld_kunit_init_link(vif, link, mld_link, link_id);
143 vif->valid_links |= BIT(link_id);
144
145 return link;
146 }
147
148 struct ieee80211_chanctx_conf *
iwlmld_kunit_add_chanctx_from_def(struct cfg80211_chan_def * def)149 iwlmld_kunit_add_chanctx_from_def(struct cfg80211_chan_def *def)
150 {
151 struct kunit *test = kunit_get_current_test();
152 struct iwl_mld *mld = test->priv;
153 struct ieee80211_chanctx_conf *ctx;
154 struct iwl_mld_phy *phy;
155 int fw_id;
156
157 KUNIT_ALLOC_AND_ASSERT_SIZE(test, ctx, sizeof(*ctx) + sizeof(*phy));
158
159 /* Setup the chanctx conf */
160 ctx->def = *def;
161 ctx->min_def = *def;
162 ctx->ap = *def;
163
164 /* and the iwl_mld_phy */
165 phy = iwl_mld_phy_from_mac80211(ctx);
166
167 fw_id = iwl_mld_allocate_fw_phy_id(mld);
168 KUNIT_ASSERT_GE(test, fw_id, 0);
169
170 phy->fw_id = fw_id;
171 phy->mld = mld;
172 phy->chandef = *def;
173
174 return ctx;
175 }
176
iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif * vif,struct ieee80211_bss_conf * link,struct ieee80211_chanctx_conf * ctx)177 void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif,
178 struct ieee80211_bss_conf *link,
179 struct ieee80211_chanctx_conf *ctx)
180 {
181 struct kunit *test = kunit_get_current_test();
182 struct iwl_mld *mld = test->priv;
183 struct iwl_mld_link *mld_link;
184
185 KUNIT_EXPECT_NULL(test, rcu_access_pointer(link->chanctx_conf));
186 rcu_assign_pointer(link->chanctx_conf, ctx);
187
188 lockdep_assert_wiphy(mld->wiphy);
189
190 mld_link = iwl_mld_link_from_mac80211(link);
191
192 KUNIT_EXPECT_NULL(test, rcu_access_pointer(mld_link->chan_ctx));
193 KUNIT_EXPECT_FALSE(test, mld_link->active);
194
195 rcu_assign_pointer(mld_link->chan_ctx, ctx);
196 mld_link->active = true;
197
198 if (ieee80211_vif_is_mld(vif))
199 vif->active_links |= BIT(link->link_id);
200 }
201
IWL_MLD_ALLOC_FN(link_sta,link_sta)202 IWL_MLD_ALLOC_FN(link_sta, link_sta)
203
204 static void iwlmld_kunit_add_link_sta(struct ieee80211_sta *sta,
205 struct ieee80211_link_sta *link_sta,
206 struct iwl_mld_link_sta *mld_link_sta,
207 u8 link_id)
208 {
209 struct kunit *test = kunit_get_current_test();
210 struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
211 struct iwl_mld *mld = test->priv;
212 u8 fw_id;
213 int ret;
214
215 /* initialize mac80211's link_sta */
216 link_sta->link_id = link_id;
217 rcu_assign_pointer(sta->link[link_id], link_sta);
218
219 link_sta->sta = sta;
220
221 /* and the iwl_mld_link_sta */
222 ret = iwl_mld_allocate_link_sta_fw_id(mld, &fw_id, link_sta);
223 KUNIT_ASSERT_EQ(test, ret, 0);
224 mld_link_sta->fw_id = fw_id;
225
226 rcu_assign_pointer(mld_sta->link[link_id], mld_link_sta);
227 }
228
229 static struct ieee80211_link_sta *
iwlmld_kunit_alloc_link_sta(struct ieee80211_sta * sta,int link_id)230 iwlmld_kunit_alloc_link_sta(struct ieee80211_sta *sta, int link_id)
231 {
232 struct kunit *test = kunit_get_current_test();
233 struct ieee80211_link_sta *link_sta;
234 struct iwl_mld_link_sta *mld_link_sta;
235
236 /* Only valid for MLO */
237 KUNIT_ASSERT_TRUE(test, sta->valid_links);
238
239 KUNIT_ALLOC_AND_ASSERT(test, link_sta);
240 KUNIT_ALLOC_AND_ASSERT(test, mld_link_sta);
241
242 iwlmld_kunit_add_link_sta(sta, link_sta, mld_link_sta, link_id);
243
244 sta->valid_links |= BIT(link_id);
245
246 return link_sta;
247 }
248
249 /* Allocate and initialize a STA with the first link_sta */
250 static struct ieee80211_sta *
iwlmld_kunit_add_sta(struct ieee80211_vif * vif,int link_id)251 iwlmld_kunit_add_sta(struct ieee80211_vif *vif, int link_id)
252 {
253 struct kunit *test = kunit_get_current_test();
254 struct ieee80211_sta *sta;
255 struct iwl_mld_sta *mld_sta;
256
257 /* Allocate memory for ieee80211_sta with embedded iwl_mld_sta */
258 KUNIT_ALLOC_AND_ASSERT_SIZE(test, sta, sizeof(*sta) + sizeof(*mld_sta));
259
260 /* TODO: allocate and initialize the TXQs ? */
261
262 mld_sta = iwl_mld_sta_from_mac80211(sta);
263 mld_sta->vif = vif;
264 mld_sta->mld = test->priv;
265
266 /* TODO: adjust for internal stations */
267 mld_sta->sta_type = STATION_TYPE_PEER;
268
269 if (link_id >= 0) {
270 iwlmld_kunit_add_link_sta(sta, &sta->deflink,
271 &mld_sta->deflink, link_id);
272 sta->valid_links = BIT(link_id);
273 } else {
274 iwlmld_kunit_add_link_sta(sta, &sta->deflink,
275 &mld_sta->deflink, 0);
276 }
277 return sta;
278 }
279
280 /* Move s STA to a state */
iwlmld_kunit_move_sta_state(struct ieee80211_vif * vif,struct ieee80211_sta * sta,enum ieee80211_sta_state state)281 static void iwlmld_kunit_move_sta_state(struct ieee80211_vif *vif,
282 struct ieee80211_sta *sta,
283 enum ieee80211_sta_state state)
284 {
285 struct kunit *test = kunit_get_current_test();
286 struct iwl_mld_sta *mld_sta;
287 struct iwl_mld_vif *mld_vif;
288
289 /* The sta will be removed automatically at the end of the test */
290 KUNIT_ASSERT_NE(test, state, IEEE80211_STA_NOTEXIST);
291
292 mld_sta = iwl_mld_sta_from_mac80211(sta);
293 mld_sta->sta_state = state;
294
295 mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif);
296 mld_vif->authorized = state == IEEE80211_STA_AUTHORIZED;
297
298 if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
299 mld_vif->ap_sta = sta;
300 }
301
iwlmld_kunit_setup_sta(struct ieee80211_vif * vif,enum ieee80211_sta_state state,int link_id)302 struct ieee80211_sta *iwlmld_kunit_setup_sta(struct ieee80211_vif *vif,
303 enum ieee80211_sta_state state,
304 int link_id)
305 {
306 struct kunit *test = kunit_get_current_test();
307 struct ieee80211_sta *sta;
308
309 /* The sta will be removed automatically at the end of the test */
310 KUNIT_ASSERT_NE(test, state, IEEE80211_STA_NOTEXIST);
311
312 /* First - allocate and init the STA */
313 sta = iwlmld_kunit_add_sta(vif, link_id);
314
315 /* Now move it all the way to the wanted state */
316 for (enum ieee80211_sta_state _state = IEEE80211_STA_NONE;
317 _state <= state; _state++)
318 iwlmld_kunit_move_sta_state(vif, sta, state);
319
320 return sta;
321 }
322
iwlmld_kunit_set_vif_associated(struct ieee80211_vif * vif)323 static void iwlmld_kunit_set_vif_associated(struct ieee80211_vif *vif)
324 {
325 /* TODO: setup chanreq */
326 /* TODO setup capabilities */
327
328 vif->cfg.assoc = 1;
329 }
330
331 static struct ieee80211_vif *
iwlmld_kunit_setup_assoc(bool mlo,struct iwl_mld_kunit_link * assoc_link)332 iwlmld_kunit_setup_assoc(bool mlo, struct iwl_mld_kunit_link *assoc_link)
333 {
334 struct kunit *test = kunit_get_current_test();
335 struct iwl_mld *mld = test->priv;
336 struct ieee80211_vif *vif;
337 struct ieee80211_bss_conf *link;
338 struct ieee80211_chanctx_conf *chan_ctx;
339
340 KUNIT_ASSERT_TRUE(test, mlo || assoc_link->id == 0);
341
342 vif = iwlmld_kunit_add_vif(mlo, NL80211_IFTYPE_STATION);
343
344 if (mlo)
345 link = iwlmld_kunit_add_link(vif, assoc_link->id);
346 else
347 link = &vif->bss_conf;
348
349 chan_ctx = iwlmld_kunit_add_chanctx(assoc_link->band,
350 assoc_link->bandwidth);
351
352 wiphy_lock(mld->wiphy);
353 iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx);
354 wiphy_unlock(mld->wiphy);
355
356 /* The AP sta will now be pointer to by mld_vif->ap_sta */
357 iwlmld_kunit_setup_sta(vif, IEEE80211_STA_AUTHORIZED, assoc_link->id);
358
359 iwlmld_kunit_set_vif_associated(vif);
360
361 return vif;
362 }
363
364 struct ieee80211_vif *
iwlmld_kunit_setup_mlo_assoc(u16 valid_links,struct iwl_mld_kunit_link * assoc_link)365 iwlmld_kunit_setup_mlo_assoc(u16 valid_links,
366 struct iwl_mld_kunit_link *assoc_link)
367 {
368 struct kunit *test = kunit_get_current_test();
369 struct ieee80211_vif *vif;
370
371 KUNIT_ASSERT_TRUE(test,
372 hweight16(valid_links) == 1 ||
373 hweight16(valid_links) == 2);
374 KUNIT_ASSERT_TRUE(test, valid_links & BIT(assoc_link->id));
375
376 vif = iwlmld_kunit_setup_assoc(true, assoc_link);
377
378 /* Add the other link, if applicable */
379 if (hweight16(valid_links) > 1) {
380 u8 other_link_id = ffs(valid_links & ~BIT(assoc_link->id)) - 1;
381
382 iwlmld_kunit_add_link(vif, other_link_id);
383 }
384
385 return vif;
386 }
387
388 struct ieee80211_vif *
iwlmld_kunit_setup_non_mlo_assoc(struct iwl_mld_kunit_link * assoc_link)389 iwlmld_kunit_setup_non_mlo_assoc(struct iwl_mld_kunit_link *assoc_link)
390 {
391 return iwlmld_kunit_setup_assoc(false, assoc_link);
392 }
393
394 struct iwl_rx_packet *
_iwl_mld_kunit_create_pkt(const void * notif,size_t notif_sz)395 _iwl_mld_kunit_create_pkt(const void *notif, size_t notif_sz)
396 {
397 struct kunit *test = kunit_get_current_test();
398 struct iwl_rx_packet *pkt;
399
400 KUNIT_ALLOC_AND_ASSERT_SIZE(test, pkt, sizeof(pkt) + notif_sz);
401
402 memcpy(pkt->data, notif, notif_sz);
403 pkt->len_n_flags = cpu_to_le32(sizeof(pkt->hdr) + notif_sz);
404
405 return pkt;
406 }
407
iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link * link1,struct iwl_mld_kunit_link * link2)408 struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1,
409 struct iwl_mld_kunit_link *link2)
410 {
411 struct kunit *test = kunit_get_current_test();
412 struct iwl_mld *mld = test->priv;
413 struct ieee80211_vif *vif;
414 struct ieee80211_bss_conf *link;
415 struct ieee80211_chanctx_conf *chan_ctx;
416 struct ieee80211_sta *sta;
417 struct iwl_mld_vif *mld_vif;
418 u16 valid_links = BIT(link1->id) | BIT(link2->id);
419
420 KUNIT_ASSERT_TRUE(test, hweight16(valid_links) == 2);
421
422 vif = iwlmld_kunit_setup_mlo_assoc(valid_links, link1);
423 mld_vif = iwl_mld_vif_from_mac80211(vif);
424
425 /* Activate second link */
426 wiphy_lock(mld->wiphy);
427
428 link = wiphy_dereference(mld->wiphy, vif->link_conf[link2->id]);
429 KUNIT_EXPECT_NOT_NULL(test, link);
430
431 chan_ctx = iwlmld_kunit_add_chanctx(link2->band, link2->bandwidth);
432 iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx);
433
434 wiphy_unlock(mld->wiphy);
435
436 /* And other link sta */
437 sta = mld_vif->ap_sta;
438 KUNIT_EXPECT_NOT_NULL(test, sta);
439
440 iwlmld_kunit_alloc_link_sta(sta, link2->id);
441
442 return vif;
443 }
444
iwlmld_kunit_gen_element(u8 id,const void * data,size_t len)445 struct element *iwlmld_kunit_gen_element(u8 id, const void *data, size_t len)
446 {
447 struct kunit *test = kunit_get_current_test();
448 struct element *elem;
449
450 KUNIT_ALLOC_AND_ASSERT_SIZE(test, elem, sizeof(*elem) + len);
451
452 elem->id = id;
453 elem->datalen = len;
454 memcpy(elem->data, data, len);
455
456 return elem;
457 }
458
iwlmld_kunit_get_phy_of_link(struct ieee80211_vif * vif,u8 link_id)459 struct iwl_mld_phy *iwlmld_kunit_get_phy_of_link(struct ieee80211_vif *vif,
460 u8 link_id)
461 {
462 struct kunit *test = kunit_get_current_test();
463 struct iwl_mld *mld = test->priv;
464 struct ieee80211_chanctx_conf *chanctx;
465 struct ieee80211_bss_conf *link =
466 wiphy_dereference(mld->wiphy, vif->link_conf[link_id]);
467
468 KUNIT_EXPECT_NOT_NULL(test, link);
469
470 chanctx = wiphy_dereference(mld->wiphy, link->chanctx_conf);
471 KUNIT_EXPECT_NOT_NULL(test, chanctx);
472
473 return iwl_mld_phy_from_mac80211(chanctx);
474 }
475