| // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
| /* |
| * KUnit tests for channel helper functions |
| * |
| * Copyright (C) 2024-2025 Intel Corporation |
| */ |
| #include <kunit/test.h> |
| #include <kunit/test-bug.h> |
| |
| #include "utils.h" |
| |
| #include <linux/device.h> |
| |
| #include "fw/api/scan.h" |
| #include "fw/api/mac-cfg.h" |
| #include "iwl-trans.h" |
| #include "mld.h" |
| #include "iface.h" |
| #include "link.h" |
| #include "phy.h" |
| #include "sta.h" |
| |
| int iwlmld_kunit_test_init(struct kunit *test) |
| { |
| struct iwl_mld *mld; |
| struct iwl_trans *trans; |
| const struct iwl_rf_cfg *cfg; |
| struct iwl_fw *fw; |
| struct ieee80211_hw *hw; |
| |
| KUNIT_ALLOC_AND_ASSERT(test, trans); |
| KUNIT_ALLOC_AND_ASSERT(test, trans->dev); |
| KUNIT_ALLOC_AND_ASSERT(test, cfg); |
| KUNIT_ALLOC_AND_ASSERT(test, fw); |
| KUNIT_ALLOC_AND_ASSERT(test, hw); |
| KUNIT_ALLOC_AND_ASSERT(test, hw->wiphy); |
| |
| mutex_init(&hw->wiphy->mtx); |
| |
| /* Allocate and initialize the mld structure */ |
| KUNIT_ALLOC_AND_ASSERT(test, mld); |
| iwl_construct_mld(mld, trans, cfg, fw, hw, NULL); |
| |
| fw->ucode_capa.num_stations = IWL_STATION_COUNT_MAX; |
| fw->ucode_capa.num_links = IWL_FW_MAX_LINK_ID + 1; |
| |
| mld->fwrt.trans = trans; |
| mld->fwrt.fw = fw; |
| mld->fwrt.dev = trans->dev; |
| |
| /* TODO: add priv_size to hw allocation and setup hw->priv to enable |
| * testing mac80211 callbacks |
| */ |
| |
| KUNIT_ALLOC_AND_ASSERT(test, mld->nvm_data); |
| KUNIT_ALLOC_AND_ASSERT_SIZE(test, mld->scan.cmd, |
| sizeof(struct iwl_scan_req_umac_v17)); |
| mld->scan.cmd_size = sizeof(struct iwl_scan_req_umac_v17); |
| |
| /* This is not the state at the end of the regular opmode_start, |
| * but it is more common to need it. Explicitly undo this if needed. |
| */ |
| mld->trans->state = IWL_TRANS_FW_ALIVE; |
| mld->fw_status.running = true; |
| |
| /* Avoid passing mld struct around */ |
| test->priv = mld; |
| return 0; |
| } |
| |
| IWL_MLD_ALLOC_FN(link, bss_conf) |
| |
| static void iwlmld_kunit_init_link(struct ieee80211_vif *vif, |
| struct ieee80211_bss_conf *link, |
| struct iwl_mld_link *mld_link, int link_id) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct iwl_mld *mld = test->priv; |
| struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); |
| int ret; |
| |
| /* setup mac80211 link */ |
| rcu_assign_pointer(vif->link_conf[link_id], link); |
| link->link_id = link_id; |
| link->vif = vif; |
| link->beacon_int = 100; |
| link->dtim_period = 3; |
| link->qos = true; |
| |
| /* and mld_link */ |
| ret = iwl_mld_allocate_link_fw_id(mld, &mld_link->fw_id, link); |
| KUNIT_ASSERT_EQ(test, ret, 0); |
| rcu_assign_pointer(mld_vif->link[link_id], mld_link); |
| rcu_assign_pointer(vif->link_conf[link_id], link); |
| } |
| |
| IWL_MLD_ALLOC_FN(vif, vif) |
| |
| /* Helper function to add and initialize a VIF for KUnit tests */ |
| struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct iwl_mld *mld = test->priv; |
| struct ieee80211_vif *vif; |
| struct iwl_mld_vif *mld_vif; |
| int ret; |
| |
| /* TODO: support more types */ |
| KUNIT_ASSERT_EQ(test, type, NL80211_IFTYPE_STATION); |
| |
| KUNIT_ALLOC_AND_ASSERT_SIZE(test, vif, |
| sizeof(*vif) + sizeof(*mld_vif)); |
| |
| vif->type = type; |
| mld_vif = iwl_mld_vif_from_mac80211(vif); |
| mld_vif->mld = mld; |
| |
| ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif); |
| KUNIT_ASSERT_EQ(test, ret, 0); |
| |
| /* TODO: revisit (task=EHT) */ |
| if (mlo) |
| return vif; |
| |
| /* Initialize the default link */ |
| iwlmld_kunit_init_link(vif, &vif->bss_conf, &mld_vif->deflink, 0); |
| |
| return vif; |
| } |
| |
| /* Use only for MLO vif */ |
| struct ieee80211_bss_conf * |
| iwlmld_kunit_add_link(struct ieee80211_vif *vif, int link_id) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct ieee80211_bss_conf *link; |
| struct iwl_mld_link *mld_link; |
| |
| KUNIT_ALLOC_AND_ASSERT(test, link); |
| KUNIT_ALLOC_AND_ASSERT(test, mld_link); |
| |
| iwlmld_kunit_init_link(vif, link, mld_link, link_id); |
| vif->valid_links |= BIT(link_id); |
| |
| return link; |
| } |
| |
| struct ieee80211_chanctx_conf * |
| iwlmld_kunit_add_chanctx(const struct cfg80211_chan_def *def) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct iwl_mld *mld = test->priv; |
| struct ieee80211_chanctx_conf *ctx; |
| struct iwl_mld_phy *phy; |
| int fw_id; |
| |
| KUNIT_ALLOC_AND_ASSERT_SIZE(test, ctx, sizeof(*ctx) + sizeof(*phy)); |
| |
| /* Setup the chanctx conf */ |
| ctx->def = *def; |
| ctx->min_def = *def; |
| ctx->ap = *def; |
| |
| /* and the iwl_mld_phy */ |
| phy = iwl_mld_phy_from_mac80211(ctx); |
| |
| fw_id = iwl_mld_allocate_fw_phy_id(mld); |
| KUNIT_ASSERT_GE(test, fw_id, 0); |
| |
| phy->fw_id = fw_id; |
| phy->mld = mld; |
| phy->chandef = *def; |
| |
| return ctx; |
| } |
| |
| void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif, |
| struct ieee80211_bss_conf *link, |
| struct ieee80211_chanctx_conf *ctx) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct iwl_mld *mld = test->priv; |
| struct iwl_mld_link *mld_link; |
| |
| KUNIT_EXPECT_NULL(test, rcu_access_pointer(link->chanctx_conf)); |
| rcu_assign_pointer(link->chanctx_conf, ctx); |
| |
| lockdep_assert_wiphy(mld->wiphy); |
| |
| mld_link = iwl_mld_link_from_mac80211(link); |
| |
| KUNIT_EXPECT_NULL(test, rcu_access_pointer(mld_link->chan_ctx)); |
| KUNIT_EXPECT_FALSE(test, mld_link->active); |
| |
| rcu_assign_pointer(mld_link->chan_ctx, ctx); |
| mld_link->active = true; |
| |
| if (ieee80211_vif_is_mld(vif)) |
| vif->active_links |= BIT(link->link_id); |
| } |
| |
| IWL_MLD_ALLOC_FN(link_sta, link_sta) |
| |
| static void iwlmld_kunit_add_link_sta(struct ieee80211_sta *sta, |
| struct ieee80211_link_sta *link_sta, |
| struct iwl_mld_link_sta *mld_link_sta, |
| u8 link_id) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); |
| struct iwl_mld *mld = test->priv; |
| u8 fw_id; |
| int ret; |
| |
| /* initialize mac80211's link_sta */ |
| link_sta->link_id = link_id; |
| rcu_assign_pointer(sta->link[link_id], link_sta); |
| |
| link_sta->sta = sta; |
| |
| /* and the iwl_mld_link_sta */ |
| ret = iwl_mld_allocate_link_sta_fw_id(mld, &fw_id, link_sta); |
| KUNIT_ASSERT_EQ(test, ret, 0); |
| mld_link_sta->fw_id = fw_id; |
| |
| rcu_assign_pointer(mld_sta->link[link_id], mld_link_sta); |
| } |
| |
| static struct ieee80211_link_sta * |
| iwlmld_kunit_alloc_link_sta(struct ieee80211_sta *sta, int link_id) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct ieee80211_link_sta *link_sta; |
| struct iwl_mld_link_sta *mld_link_sta; |
| |
| /* Only valid for MLO */ |
| KUNIT_ASSERT_TRUE(test, sta->valid_links); |
| |
| KUNIT_ALLOC_AND_ASSERT(test, link_sta); |
| KUNIT_ALLOC_AND_ASSERT(test, mld_link_sta); |
| |
| iwlmld_kunit_add_link_sta(sta, link_sta, mld_link_sta, link_id); |
| |
| sta->valid_links |= BIT(link_id); |
| |
| return link_sta; |
| } |
| |
| /* Allocate and initialize a STA with the first link_sta */ |
| static struct ieee80211_sta * |
| iwlmld_kunit_add_sta(struct ieee80211_vif *vif, int link_id) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct ieee80211_sta *sta; |
| struct iwl_mld_sta *mld_sta; |
| |
| /* Allocate memory for ieee80211_sta with embedded iwl_mld_sta */ |
| KUNIT_ALLOC_AND_ASSERT_SIZE(test, sta, sizeof(*sta) + sizeof(*mld_sta)); |
| |
| /* TODO: allocate and initialize the TXQs ? */ |
| |
| mld_sta = iwl_mld_sta_from_mac80211(sta); |
| mld_sta->vif = vif; |
| mld_sta->mld = test->priv; |
| |
| /* TODO: adjust for internal stations */ |
| mld_sta->sta_type = STATION_TYPE_PEER; |
| |
| if (link_id >= 0) { |
| iwlmld_kunit_add_link_sta(sta, &sta->deflink, |
| &mld_sta->deflink, link_id); |
| sta->valid_links = BIT(link_id); |
| } else { |
| iwlmld_kunit_add_link_sta(sta, &sta->deflink, |
| &mld_sta->deflink, 0); |
| } |
| return sta; |
| } |
| |
| /* Move s STA to a state */ |
| static void iwlmld_kunit_move_sta_state(struct ieee80211_vif *vif, |
| struct ieee80211_sta *sta, |
| enum ieee80211_sta_state state) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct iwl_mld_sta *mld_sta; |
| struct iwl_mld_vif *mld_vif; |
| |
| /* The sta will be removed automatically at the end of the test */ |
| KUNIT_ASSERT_NE(test, state, IEEE80211_STA_NOTEXIST); |
| |
| mld_sta = iwl_mld_sta_from_mac80211(sta); |
| mld_sta->sta_state = state; |
| |
| mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif); |
| mld_vif->authorized = state == IEEE80211_STA_AUTHORIZED; |
| |
| if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) |
| mld_vif->ap_sta = sta; |
| } |
| |
| struct ieee80211_sta *iwlmld_kunit_setup_sta(struct ieee80211_vif *vif, |
| enum ieee80211_sta_state state, |
| int link_id) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct ieee80211_sta *sta; |
| |
| /* The sta will be removed automatically at the end of the test */ |
| KUNIT_ASSERT_NE(test, state, IEEE80211_STA_NOTEXIST); |
| |
| /* First - allocate and init the STA */ |
| sta = iwlmld_kunit_add_sta(vif, link_id); |
| |
| /* Now move it all the way to the wanted state */ |
| for (enum ieee80211_sta_state _state = IEEE80211_STA_NONE; |
| _state <= state; _state++) |
| iwlmld_kunit_move_sta_state(vif, sta, state); |
| |
| return sta; |
| } |
| |
| static void iwlmld_kunit_set_vif_associated(struct ieee80211_vif *vif) |
| { |
| /* TODO: setup chanreq */ |
| /* TODO setup capabilities */ |
| |
| vif->cfg.assoc = 1; |
| } |
| |
| static struct ieee80211_vif * |
| iwlmld_kunit_setup_assoc(bool mlo, struct iwl_mld_kunit_link *assoc_link) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct iwl_mld *mld = test->priv; |
| struct ieee80211_vif *vif; |
| struct ieee80211_bss_conf *link; |
| struct ieee80211_chanctx_conf *chan_ctx; |
| |
| KUNIT_ASSERT_TRUE(test, mlo || assoc_link->id == 0); |
| |
| vif = iwlmld_kunit_add_vif(mlo, NL80211_IFTYPE_STATION); |
| |
| if (mlo) |
| link = iwlmld_kunit_add_link(vif, assoc_link->id); |
| else |
| link = &vif->bss_conf; |
| |
| chan_ctx = iwlmld_kunit_add_chanctx(assoc_link->chandef); |
| |
| wiphy_lock(mld->wiphy); |
| iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); |
| wiphy_unlock(mld->wiphy); |
| |
| /* The AP sta will now be pointer to by mld_vif->ap_sta */ |
| iwlmld_kunit_setup_sta(vif, IEEE80211_STA_AUTHORIZED, assoc_link->id); |
| |
| iwlmld_kunit_set_vif_associated(vif); |
| |
| return vif; |
| } |
| |
| struct ieee80211_vif * |
| iwlmld_kunit_setup_mlo_assoc(u16 valid_links, |
| struct iwl_mld_kunit_link *assoc_link) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct ieee80211_vif *vif; |
| |
| KUNIT_ASSERT_TRUE(test, |
| hweight16(valid_links) == 1 || |
| hweight16(valid_links) == 2); |
| KUNIT_ASSERT_TRUE(test, valid_links & BIT(assoc_link->id)); |
| |
| vif = iwlmld_kunit_setup_assoc(true, assoc_link); |
| |
| /* Add the other link, if applicable */ |
| if (hweight16(valid_links) > 1) { |
| u8 other_link_id = ffs(valid_links & ~BIT(assoc_link->id)) - 1; |
| |
| iwlmld_kunit_add_link(vif, other_link_id); |
| } |
| |
| return vif; |
| } |
| |
| struct ieee80211_vif * |
| iwlmld_kunit_setup_non_mlo_assoc(struct iwl_mld_kunit_link *assoc_link) |
| { |
| return iwlmld_kunit_setup_assoc(false, assoc_link); |
| } |
| |
| struct iwl_rx_packet * |
| _iwl_mld_kunit_create_pkt(const void *notif, size_t notif_sz) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct iwl_rx_packet *pkt; |
| |
| KUNIT_ALLOC_AND_ASSERT_SIZE(test, pkt, sizeof(pkt) + notif_sz); |
| |
| memcpy(pkt->data, notif, notif_sz); |
| pkt->len_n_flags = cpu_to_le32(sizeof(pkt->hdr) + notif_sz); |
| |
| return pkt; |
| } |
| |
| struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1, |
| struct iwl_mld_kunit_link *link2) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct iwl_mld *mld = test->priv; |
| struct ieee80211_vif *vif; |
| struct ieee80211_bss_conf *link; |
| struct ieee80211_chanctx_conf *chan_ctx; |
| struct ieee80211_sta *sta; |
| struct iwl_mld_vif *mld_vif; |
| u16 valid_links = BIT(link1->id) | BIT(link2->id); |
| |
| KUNIT_ASSERT_TRUE(test, hweight16(valid_links) == 2); |
| |
| vif = iwlmld_kunit_setup_mlo_assoc(valid_links, link1); |
| mld_vif = iwl_mld_vif_from_mac80211(vif); |
| |
| /* Activate second link */ |
| wiphy_lock(mld->wiphy); |
| |
| link = wiphy_dereference(mld->wiphy, vif->link_conf[link2->id]); |
| KUNIT_EXPECT_NOT_NULL(test, link); |
| |
| chan_ctx = iwlmld_kunit_add_chanctx(link2->chandef); |
| iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); |
| |
| wiphy_unlock(mld->wiphy); |
| |
| /* And other link sta */ |
| sta = mld_vif->ap_sta; |
| KUNIT_EXPECT_NOT_NULL(test, sta); |
| |
| iwlmld_kunit_alloc_link_sta(sta, link2->id); |
| |
| return vif; |
| } |
| |
| struct element *iwlmld_kunit_gen_element(u8 id, const void *data, size_t len) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct element *elem; |
| |
| KUNIT_ALLOC_AND_ASSERT_SIZE(test, elem, sizeof(*elem) + len); |
| |
| elem->id = id; |
| elem->datalen = len; |
| memcpy(elem->data, data, len); |
| |
| return elem; |
| } |
| |
| struct iwl_mld_phy *iwlmld_kunit_get_phy_of_link(struct ieee80211_vif *vif, |
| u8 link_id) |
| { |
| struct kunit *test = kunit_get_current_test(); |
| struct iwl_mld *mld = test->priv; |
| struct ieee80211_chanctx_conf *chanctx; |
| struct ieee80211_bss_conf *link = |
| wiphy_dereference(mld->wiphy, vif->link_conf[link_id]); |
| |
| KUNIT_EXPECT_NOT_NULL(test, link); |
| |
| chanctx = wiphy_dereference(mld->wiphy, link->chanctx_conf); |
| KUNIT_EXPECT_NOT_NULL(test, chanctx); |
| |
| return iwl_mld_phy_from_mac80211(chanctx); |
| } |
| |
| static const struct chandef_case { |
| const char *desc; |
| const struct cfg80211_chan_def *chandef; |
| } chandef_cases[] = { |
| #define CHANDEF(c, ...) { .desc = "chandef " #c " valid", .chandef = &c, }, |
| CHANDEF_LIST |
| #undef CHANDEF |
| }; |
| |
| KUNIT_ARRAY_PARAM_DESC(chandef, chandef_cases, desc); |
| |
| static void test_iwl_mld_chandef_valid(struct kunit *test) |
| { |
| const struct chandef_case *params = test->param_value; |
| |
| KUNIT_EXPECT_EQ(test, true, cfg80211_chandef_valid(params->chandef)); |
| } |
| |
| static struct kunit_case chandef_test_cases[] = { |
| KUNIT_CASE_PARAM(test_iwl_mld_chandef_valid, chandef_gen_params), |
| {} |
| }; |
| |
| static struct kunit_suite chandef_tests = { |
| .name = "iwlmld_valid_test_chandefs", |
| .test_cases = chandef_test_cases, |
| }; |
| |
| kunit_test_suite(chandef_tests); |