// SPDX-License-Identifier: GPL-2.0-only /* * KUnit tests for channel helper functions * * Copyright (C) 2024 Intel Corporation */ #include #include "../mvm.h" #include MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); static struct wiphy wiphy = { .mtx = __MUTEX_INITIALIZER(wiphy.mtx), }; static struct ieee80211_hw hw = { .wiphy = &wiphy, }; static struct ieee80211_channel chan_5ghz = { .band = NL80211_BAND_5GHZ, }; static struct ieee80211_channel chan_6ghz = { .band = NL80211_BAND_6GHZ, }; static struct ieee80211_channel chan_2ghz = { .band = NL80211_BAND_2GHZ, }; static struct cfg80211_chan_def chandef_a = {}; static struct cfg80211_chan_def chandef_b = {}; static struct iwl_mvm_phy_ctxt ctx = {}; static struct iwl_mvm_vif_link_info mvm_link = { .phy_ctxt = &ctx, .active = true }; static struct cfg80211_bss bss = {}; static struct ieee80211_bss_conf link_conf = {.bss = &bss}; static const struct iwl_fw_cmd_version entry = { .group = LEGACY_GROUP, .cmd = BT_PROFILE_NOTIFICATION, .notif_ver = 4 }; static struct iwl_fw fw = { .ucode_capa = { .n_cmd_versions = 1, .cmd_versions = &entry, }, }; static struct iwl_mvm mvm = { .hw = &hw, .fw = &fw, }; static const struct link_grading_case { const char *desc; const struct cfg80211_chan_def chandef; s32 signal; s16 channel_util; int chan_load_by_us; unsigned int grade; } link_grading_cases[] = { { .desc = "UHB, RSSI below range, no factors", .chandef = { .chan = &chan_6ghz, .width = NL80211_CHAN_WIDTH_20, }, .signal = -100, .grade = 177, }, { .desc = "LB, RSSI in range, no factors", .chandef = { .chan = &chan_2ghz, .width = NL80211_CHAN_WIDTH_20, }, .signal = -84, .grade = 344, }, { .desc = "HB, RSSI above range, no factors", .chandef = { .chan = &chan_5ghz, .width = NL80211_CHAN_WIDTH_20, }, .signal = -50, .grade = 3442, }, { .desc = "HB, BSS Load IE (20 percent), inactive link, no puncturing factor", .chandef = { .chan = &chan_5ghz, .width = NL80211_CHAN_WIDTH_20, }, .signal = -66, .channel_util = 51, .grade = 1836, }, { .desc = "LB, BSS Load IE (20 percent), active link, chan_load_by_us=10 percent. No puncturing factor", .chandef = { .chan = &chan_2ghz, .width = NL80211_CHAN_WIDTH_20, }, .signal = -61, .channel_util = 51, .chan_load_by_us = 10, .grade = 2061, }, { .desc = "UHB, BSS Load IE (40 percent), active link, chan_load_by_us=50 (invalid) percent. No puncturing factor", .chandef = { .chan = &chan_6ghz, .width = NL80211_CHAN_WIDTH_20, }, .signal = -66, .channel_util = 102, .chan_load_by_us = 50, .grade = 1552, }, { .desc = "HB, 80 MHz, no channel load factor, punctured percentage 0", .chandef = { .chan = &chan_5ghz, .width = NL80211_CHAN_WIDTH_80, .punctured = 0x0000 }, .signal = -72, .grade = 1750, }, { .desc = "HB, 160 MHz, no channel load factor, punctured percentage 25", .chandef = { .chan = &chan_5ghz, .width = NL80211_CHAN_WIDTH_160, .punctured = 0x3 }, .signal = -72, .grade = 1312, }, { .desc = "UHB, 320 MHz, no channel load factor, punctured percentage 12.5 (2/16)", .chandef = { .chan = &chan_6ghz, .width = NL80211_CHAN_WIDTH_320, .punctured = 0x3 }, .signal = -72, .grade = 1806, }, { .desc = "HB, 160 MHz, channel load 20, channel load by us 10, punctured percentage 25", .chandef = { .chan = &chan_5ghz, .width = NL80211_CHAN_WIDTH_160, .punctured = 0x3 }, .channel_util = 51, .chan_load_by_us = 10, .signal = -72, .grade = 1179, }, }; KUNIT_ARRAY_PARAM_DESC(link_grading, link_grading_cases, desc) static void setup_link_conf(struct kunit *test) { const struct link_grading_case *params = test->param_value; size_t vif_size = sizeof(struct ieee80211_vif) + sizeof(struct iwl_mvm_vif); struct ieee80211_vif *vif = kunit_kzalloc(test, vif_size, GFP_KERNEL); struct ieee80211_bss_load_elem *bss_load; struct element *element; size_t ies_size = sizeof(struct cfg80211_bss_ies) + sizeof(*bss_load) + sizeof(element); struct cfg80211_bss_ies *ies; struct iwl_mvm_vif *mvmvif; KUNIT_ASSERT_NOT_NULL(test, vif); mvmvif = iwl_mvm_vif_from_mac80211(vif); if (params->chan_load_by_us > 0) { ctx.channel_load_by_us = params->chan_load_by_us; mvmvif->link[0] = &mvm_link; } link_conf.vif = vif; link_conf.chanreq.oper = params->chandef; bss.signal = DBM_TO_MBM(params->signal); ies = kunit_kzalloc(test, ies_size, GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ies); ies->len = sizeof(*bss_load) + sizeof(struct element); element = (void *)ies->data; element->datalen = sizeof(*bss_load); element->id = 11; bss_load = (void *)element->data; bss_load->channel_util = params->channel_util; rcu_assign_pointer(bss.ies, ies); } static void test_link_grading(struct kunit *test) { const struct link_grading_case *params = test->param_value; unsigned int ret; setup_link_conf(test); rcu_read_lock(); ret = iwl_mvm_get_link_grade(&link_conf); rcu_read_unlock(); KUNIT_EXPECT_EQ(test, ret, params->grade); kunit_kfree(test, link_conf.vif); RCU_INIT_POINTER(bss.ies, NULL); } static struct kunit_case link_grading_test_cases[] = { KUNIT_CASE_PARAM(test_link_grading, link_grading_gen_params), {} }; static struct kunit_suite link_grading = { .name = "iwlmvm-link-grading", .test_cases = link_grading_test_cases, }; kunit_test_suite(link_grading); static const struct valid_link_pair_case { const char *desc; bool bt; struct ieee80211_channel *chan_a; struct ieee80211_channel *chan_b; enum nl80211_chan_width cw_a; enum nl80211_chan_width cw_b; s32 sig_a; s32 sig_b; bool csa_a; bool valid; } valid_link_pair_cases[] = { { .desc = "HB + UHB, valid.", .chan_a = &chan_6ghz, .chan_b = &chan_5ghz, .valid = true, }, { .desc = "LB + HB, no BT.", .chan_a = &chan_2ghz, .chan_b = &chan_5ghz, .valid = false, }, { .desc = "LB + HB, with BT.", .bt = true, .chan_a = &chan_2ghz, .chan_b = &chan_5ghz, .valid = false, }, { .desc = "Same band", .chan_a = &chan_2ghz, .chan_b = &chan_2ghz, .valid = false, }, { .desc = "RSSI: LB, 20 MHz, low", .chan_a = &chan_2ghz, .cw_a = NL80211_CHAN_WIDTH_20, .sig_a = -68, .chan_b = &chan_5ghz, .valid = false, }, { .desc = "RSSI: UHB, 20 MHz, high", .chan_a = &chan_6ghz, .cw_a = NL80211_CHAN_WIDTH_20, .sig_a = -66, .chan_b = &chan_5ghz, .cw_b = NL80211_CHAN_WIDTH_20, .valid = true, }, { .desc = "RSSI: UHB, 40 MHz, low", .chan_a = &chan_6ghz, .cw_a = NL80211_CHAN_WIDTH_40, .sig_a = -65, .chan_b = &chan_5ghz, .cw_b = NL80211_CHAN_WIDTH_40, .valid = false, }, { .desc = "RSSI: UHB, 40 MHz, high", .chan_a = &chan_6ghz, .cw_a = NL80211_CHAN_WIDTH_40, .sig_a = -63, .chan_b = &chan_5ghz, .cw_b = NL80211_CHAN_WIDTH_40, .valid = true, }, { .desc = "RSSI: UHB, 80 MHz, low", .chan_a = &chan_6ghz, .cw_a = NL80211_CHAN_WIDTH_80, .sig_a = -62, .chan_b = &chan_5ghz, .cw_b = NL80211_CHAN_WIDTH_80, .valid = false, }, { .desc = "RSSI: UHB, 80 MHz, high", .chan_a = &chan_6ghz, .cw_a = NL80211_CHAN_WIDTH_80, .sig_a = -60, .chan_b = &chan_5ghz, .cw_b = NL80211_CHAN_WIDTH_80, .valid = true, }, { .desc = "RSSI: UHB, 160 MHz, low", .chan_a = &chan_6ghz, .cw_a = NL80211_CHAN_WIDTH_160, .sig_a = -59, .chan_b = &chan_5ghz, .cw_b = NL80211_CHAN_WIDTH_160, .valid = false, }, { .desc = "RSSI: HB, 160 MHz, high", .chan_a = &chan_6ghz, .cw_a = NL80211_CHAN_WIDTH_160, .sig_a = -5, .chan_b = &chan_5ghz, .cw_b = NL80211_CHAN_WIDTH_160, .valid = true, }, { .desc = "CSA active", .chan_a = &chan_6ghz, .cw_a = NL80211_CHAN_WIDTH_160, .sig_a = -5, .chan_b = &chan_5ghz, .cw_b = NL80211_CHAN_WIDTH_160, .valid = false, /* same as previous entry with valid=true except for CSA */ .csa_a = true, }, }; KUNIT_ARRAY_PARAM_DESC(valid_link_pair, valid_link_pair_cases, desc) static void test_valid_link_pair(struct kunit *test) { const struct valid_link_pair_case *params = test->param_value; size_t vif_size = sizeof(struct ieee80211_vif) + sizeof(struct iwl_mvm_vif); struct ieee80211_vif *vif = kunit_kzalloc(test, vif_size, GFP_KERNEL); struct iwl_trans *trans = kunit_kzalloc(test, sizeof(struct iwl_trans), GFP_KERNEL); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_link_sel_data link_a = { .chandef = &chandef_a, .link_id = 1, .signal = params->sig_a, }; struct iwl_mvm_link_sel_data link_b = { .chandef = &chandef_b, .link_id = 5, .signal = params->sig_b, }; struct ieee80211_bss_conf *conf; bool result; KUNIT_ASSERT_NOT_NULL(test, vif); KUNIT_ASSERT_NOT_NULL(test, trans); chandef_a.chan = params->chan_a; chandef_b.chan = params->chan_b; chandef_a.width = params->cw_a ?: NL80211_CHAN_WIDTH_20; chandef_b.width = params->cw_b ?: NL80211_CHAN_WIDTH_20; #ifdef CONFIG_IWLWIFI_SUPPORT_DEBUG_OVERRIDES trans->dbg_cfg = default_dbg_config; #endif mvm.trans = trans; mvm.last_bt_notif.wifi_loss_low_rssi = params->bt; mvmvif->mvm = &mvm; conf = kunit_kzalloc(test, sizeof(*vif->link_conf[0]), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, conf); conf->chanreq.oper = chandef_a; conf->csa_active = params->csa_a; vif->link_conf[link_a.link_id] = (void __rcu *)conf; conf = kunit_kzalloc(test, sizeof(*vif->link_conf[0]), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, conf); conf->chanreq.oper = chandef_b; vif->link_conf[link_b.link_id] = (void __rcu *)conf; wiphy_lock(&wiphy); result = iwl_mvm_mld_valid_link_pair(vif, &link_a, &link_b); wiphy_unlock(&wiphy); KUNIT_EXPECT_EQ(test, result, params->valid); kunit_kfree(test, vif); kunit_kfree(test, trans); } static struct kunit_case valid_link_pair_test_cases[] = { KUNIT_CASE_PARAM(test_valid_link_pair, valid_link_pair_gen_params), {}, }; static struct kunit_suite valid_link_pair = { .name = "iwlmvm-valid-link-pair", .test_cases = valid_link_pair_test_cases, }; kunit_test_suite(valid_link_pair);