Commit 07bf5297 authored by Miri Korenblit's avatar Miri Korenblit Committed by Johannes Berg

wifi: iwlwifi: mvm: Implement new link selection algorithm

Replaces the current logic with a new algorithm based on the link
grading introduced in a previous patch.

The new selection algorithm will be invoked upon successful scan to ensure
it has the necessary updated data it needs.

This update delegates the selection logic as the primary link
determiner in EMLSR mode, storing it in mvmvif to avoid repeated
calculations, as the result may vary.

Additionally, includes tests for iwl_mvm_valid_link_pair to validate
link pairs for EMLSR.
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Reviewed-by: default avatarJohannes Berg <johannes.berg@intel.com>
Link: https://msgid.link/20240416134215.309fb1b3fe44.I5baf0c293c89a5a28bd1a6386bf9ca6d2bf61ab8@changeidSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 1b9b7d37
...@@ -282,7 +282,7 @@ static void iwl_mvm_bt_coex_enable_esr(struct iwl_mvm *mvm, ...@@ -282,7 +282,7 @@ static void iwl_mvm_bt_coex_enable_esr(struct iwl_mvm *mvm,
static bool static bool
iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm, iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
int link_id, int primary_link) int link_id)
{ {
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id]; struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
...@@ -298,7 +298,7 @@ iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm, ...@@ -298,7 +298,7 @@ iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm,
return true; return true;
/* If LB link is the primary one we should always disable eSR */ /* If LB link is the primary one we should always disable eSR */
if (link_id == primary_link) if (link_id == iwl_mvm_get_primary_link(vif))
return false; return false;
/* The feature is not supported */ /* The feature is not supported */
...@@ -340,17 +340,13 @@ void iwl_mvm_bt_coex_update_link_esr(struct iwl_mvm *mvm, ...@@ -340,17 +340,13 @@ void iwl_mvm_bt_coex_update_link_esr(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
int link_id) int link_id)
{ {
unsigned long usable_links = ieee80211_vif_usable_links(vif);
int primary_link = iwl_mvm_mld_get_primary_link(mvm, vif,
usable_links);
bool enable; bool enable;
/* Not assoc, not MLD vif or only one usable link */ if (!ieee80211_vif_is_mld(vif) ||
if (primary_link < 0) !iwl_mvm_vif_from_mac80211(vif)->authorized)
return; return;
enable = iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link_id, enable = iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link_id);
primary_link);
iwl_mvm_bt_coex_enable_esr(mvm, vif, enable); iwl_mvm_bt_coex_enable_esr(mvm, vif, enable);
} }
......
...@@ -1261,31 +1261,22 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, ...@@ -1261,31 +1261,22 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
if (IS_ERR_OR_NULL(vif)) if (IS_ERR_OR_NULL(vif))
return 1; return 1;
if (hweight16(vif->active_links) > 1) { mutex_lock(&mvm->mutex);
primary_link = iwl_mvm_get_primary_link(vif);
if (ieee80211_vif_is_mld(vif) && vif->cfg.assoc &&
mvmvif->esr_active) {
/* /*
* Select the 'best' link. * Select the 'best' link. May need to revisit, it seems
* May need to revisit, it seems better to not optimize * better to not optimize for throughput but rather
* for throughput but rather range, reliability and * range, reliability and power here - and select
* power here - and select 2.4 GHz ... * 2.4 GHz ...
*/ */
primary_link = iwl_mvm_mld_get_primary_link(mvm, vif,
vif->active_links);
if (WARN_ONCE(primary_link < 0, "no primary link in 0x%x\n",
vif->active_links))
primary_link = __ffs(vif->active_links);
ret = ieee80211_set_active_links(vif, BIT(primary_link)); ret = ieee80211_set_active_links(vif, BIT(primary_link));
if (ret) if (ret)
return ret; return ret;
} else if (vif->active_links) {
primary_link = __ffs(vif->active_links);
} else {
primary_link = 0;
} }
mutex_lock(&mvm->mutex);
set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status); set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
synchronize_net(); synchronize_net();
......
...@@ -1350,6 +1350,7 @@ void iwl_mvm_mac_stop(struct ieee80211_hw *hw) ...@@ -1350,6 +1350,7 @@ void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_INT_MLO, false); iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_INT_MLO, false);
mutex_unlock(&mvm->mutex); mutex_unlock(&mvm->mutex);
wiphy_work_flush(mvm->hw->wiphy, &mvm->async_handlers_wiphy_wk);
flush_work(&mvm->async_handlers_wk); flush_work(&mvm->async_handlers_wk);
flush_work(&mvm->add_stream_wk); flush_work(&mvm->add_stream_wk);
...@@ -3883,6 +3884,9 @@ iwl_mvm_sta_state_assoc_to_authorized(struct iwl_mvm *mvm, ...@@ -3883,6 +3884,9 @@ iwl_mvm_sta_state_assoc_to_authorized(struct iwl_mvm *mvm,
WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif)); WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif));
mvmvif->authorized = 1; mvmvif->authorized = 1;
mvmvif->link_selection_res = 0;
mvmvif->link_selection_primary =
vif->active_links ? __ffs(vif->active_links) : 0;
callbacks->mac_ctxt_changed(mvm, vif, false); callbacks->mac_ctxt_changed(mvm, vif, false);
iwl_mvm_mei_host_associated(mvm, vif, mvm_sta); iwl_mvm_mei_host_associated(mvm, vif, mvm_sta);
...@@ -3891,11 +3895,11 @@ iwl_mvm_sta_state_assoc_to_authorized(struct iwl_mvm *mvm, ...@@ -3891,11 +3895,11 @@ iwl_mvm_sta_state_assoc_to_authorized(struct iwl_mvm *mvm,
iwl_mvm_bt_coex_update_vif_esr(mvm, vif); iwl_mvm_bt_coex_update_vif_esr(mvm, vif);
/* when client is authorized (AP station marked as such), /* when client is authorized (AP station marked as such),
* try to enable more links * try to enable the best link(s).
*/ */
if (vif->type == NL80211_IFTYPE_STATION && if (vif->type == NL80211_IFTYPE_STATION &&
!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
iwl_mvm_mld_select_links(mvm, vif, false); iwl_mvm_select_links(mvm, vif);
} }
mvm_sta->authorized = true; mvm_sta->authorized = true;
...@@ -3939,6 +3943,7 @@ iwl_mvm_sta_state_authorized_to_assoc(struct iwl_mvm *mvm, ...@@ -3939,6 +3943,7 @@ iwl_mvm_sta_state_authorized_to_assoc(struct iwl_mvm *mvm,
* time. * time.
*/ */
mvmvif->authorized = 0; mvmvif->authorized = 0;
mvmvif->link_selection_res = 0;
/* disable beacon filtering */ /* disable beacon filtering */
iwl_mvm_disable_beacon_filter(mvm, vif); iwl_mvm_disable_beacon_filter(mvm, vif);
......
...@@ -232,6 +232,12 @@ static int iwl_mvm_esr_mode_active(struct iwl_mvm *mvm, ...@@ -232,6 +232,12 @@ static int iwl_mvm_esr_mode_active(struct iwl_mvm *mvm,
link->phy_ctxt->rlc_disabled = true; link->phy_ctxt->rlc_disabled = true;
} }
if (vif->active_links == mvmvif->link_selection_res &&
!WARN_ON(!(vif->active_links & BIT(mvmvif->link_selection_primary))))
mvmvif->primary_link = mvmvif->link_selection_primary;
else
mvmvif->primary_link = __ffs(vif->active_links);
return ret; return ret;
} }
...@@ -689,9 +695,6 @@ iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm, ...@@ -689,9 +695,6 @@ iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm,
if (ret) if (ret)
IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
if (changes & BSS_CHANGED_MLD_VALID_LINKS)
iwl_mvm_mld_select_links(mvm, vif, true);
memcpy(mvmvif->link[link_conf->link_id]->bssid, link_conf->bssid, memcpy(mvmvif->link[link_conf->link_id]->bssid, link_conf->bssid,
ETH_ALEN); ETH_ALEN);
...@@ -1112,6 +1115,14 @@ iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw, ...@@ -1112,6 +1115,14 @@ iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw,
if (new_links == 0) { if (new_links == 0) {
mvmvif->link[0] = &mvmvif->deflink; mvmvif->link[0] = &mvmvif->deflink;
err = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); err = iwl_mvm_add_link(mvm, vif, &vif->bss_conf);
if (err == 0)
mvmvif->primary_link = 0;
} else if (!(new_links & BIT(mvmvif->primary_link))) {
/*
* Ensure we always have a valid primary_link, the real
* decision happens later when PHY is activated.
*/
mvmvif->primary_link = BIT(__ffs(new_links));
} }
out_err: out_err:
...@@ -1144,27 +1155,22 @@ void iwl_mvm_recalc_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif) ...@@ -1144,27 +1155,22 @@ void iwl_mvm_recalc_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{ {
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
bool enable = !mvmvif->esr_disable_reason; bool enable = !mvmvif->esr_disable_reason;
int link_id; u16 new_active_links;
/* Nothing to do */ /* Nothing to do */
if (mvmvif->esr_active == enable) if (mvmvif->esr_active == enable)
return; return;
if (enable) { /* The next link selection will enter eSR if possible */
/* Try to re-enable eSR */ if (enable)
iwl_mvm_mld_select_links(mvm, vif, false);
return; return;
}
/* /*
* Find the primary link, as we want to switch to it and drop the * Find the primary link, as we want to switch to it and drop the
* secondary one. * secondary one.
*/ */
link_id = iwl_mvm_mld_get_primary_link(mvm, vif, vif->active_links); new_active_links = BIT(iwl_mvm_get_primary_link(vif));
WARN_ON(link_id < 0); ieee80211_set_active_links_async(vif, new_active_links);
ieee80211_set_active_links_async(vif,
vif->active_links & BIT(link_id));
} }
bool iwl_mvm_esr_allowed_on_vif(struct iwl_mvm *mvm, bool iwl_mvm_esr_allowed_on_vif(struct iwl_mvm *mvm,
...@@ -1200,12 +1206,13 @@ static bool iwl_mvm_can_enter_esr(struct iwl_mvm *mvm, ...@@ -1200,12 +1206,13 @@ static bool iwl_mvm_can_enter_esr(struct iwl_mvm *mvm,
unsigned long desired_links) unsigned long desired_links)
{ {
struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS]; struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS];
u8 n_data; u8 best_link, n_data;
if (!iwl_mvm_esr_allowed_on_vif(mvm, vif)) if (!iwl_mvm_esr_allowed_on_vif(mvm, vif))
return false; return false;
n_data = iwl_mvm_set_link_selection_data(vif, data, desired_links); n_data = iwl_mvm_set_link_selection_data(vif, data, desired_links,
&best_link);
if (n_data != 2) if (n_data != 2)
return false; return false;
......
...@@ -388,6 +388,12 @@ enum iwl_mvm_esr_disable_reason { ...@@ -388,6 +388,12 @@ enum iwl_mvm_esr_disable_reason {
* @esr_active: indicates eSR mode is active * @esr_active: indicates eSR mode is active
* @esr_disable_reason: a bitmap of enum iwl_mvm_esr_disable_reason * @esr_disable_reason: a bitmap of enum iwl_mvm_esr_disable_reason
* @pm_enabled: indicates powersave is enabled * @pm_enabled: indicates powersave is enabled
* @link_selection_res: bitmap of active links as it was decided in the last
* link selection. Valid only for a MLO vif after assoc. 0 if there wasn't
* any link selection yet.
* @link_selection_primary: primary link selected by link selection
* @primary_link: primary link in eSR. Valid only for an associated MLD vif,
* and in eSR mode. Valid only for a STA.
*/ */
struct iwl_mvm_vif { struct iwl_mvm_vif {
struct iwl_mvm *mvm; struct iwl_mvm *mvm;
...@@ -478,6 +484,9 @@ struct iwl_mvm_vif { ...@@ -478,6 +484,9 @@ struct iwl_mvm_vif {
struct ieee80211_key_conf __rcu *keys[2]; struct ieee80211_key_conf __rcu *keys[2];
} bcn_prot; } bcn_prot;
u16 link_selection_res;
u8 link_selection_primary;
u8 primary_link;
struct iwl_mvm_vif_link_info deflink; struct iwl_mvm_vif_link_info deflink;
struct iwl_mvm_vif_link_info *link[IEEE80211_MLD_MAX_NUM_LINKS]; struct iwl_mvm_vif_link_info *link[IEEE80211_MLD_MAX_NUM_LINKS];
}; };
...@@ -1944,24 +1953,27 @@ int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ...@@ -1944,24 +1953,27 @@ int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf); struct ieee80211_bss_conf *link_conf);
void iwl_mvm_mld_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif, void iwl_mvm_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
bool valid_links_changed); u8 iwl_mvm_get_primary_link(struct ieee80211_vif *vif);
int iwl_mvm_mld_get_primary_link(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, #if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
unsigned long usable_links); unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf);
#endif
struct iwl_mvm_link_sel_data { struct iwl_mvm_link_sel_data {
u8 link_id; u8 link_id;
enum nl80211_band band; enum nl80211_band band;
enum nl80211_chan_width width; u16 grade;
bool active;
}; };
u8 iwl_mvm_set_link_selection_data(struct ieee80211_vif *vif, u8 iwl_mvm_set_link_selection_data(struct ieee80211_vif *vif,
struct iwl_mvm_link_sel_data *data, struct iwl_mvm_link_sel_data *data,
unsigned long usable_links); unsigned long usable_links,
u8 *best_link_idx);
bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif, bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
struct iwl_mvm_link_sel_data *a, const struct iwl_mvm_link_sel_data *a,
struct iwl_mvm_link_sel_data *b); const struct iwl_mvm_link_sel_data *b);
/* AP and IBSS */ /* AP and IBSS */
bool iwl_mvm_start_ap_ibss_common(struct ieee80211_hw *hw, bool iwl_mvm_start_ap_ibss_common(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, int *ret); struct ieee80211_vif *vif, int *ret);
...@@ -2461,7 +2473,6 @@ u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm, ...@@ -2461,7 +2473,6 @@ u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
struct ieee80211_key_conf *keyconf); struct ieee80211_key_conf *keyconf);
unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf);
bool iwl_rfi_supported(struct iwl_mvm *mvm); bool iwl_rfi_supported(struct iwl_mvm *mvm);
int iwl_rfi_send_config_cmd(struct iwl_mvm *mvm, int iwl_rfi_send_config_cmd(struct iwl_mvm *mvm,
......
...@@ -365,7 +365,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { ...@@ -365,7 +365,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
iwl_mvm_rx_scan_match_found, iwl_mvm_rx_scan_match_found,
RX_HANDLER_SYNC), RX_HANDLER_SYNC),
RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif, RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif,
RX_HANDLER_ASYNC_LOCKED, struct iwl_umac_scan_complete), RX_HANDLER_ASYNC_LOCKED_WIPHY,
struct iwl_umac_scan_complete),
RX_HANDLER(SCAN_ITERATION_COMPLETE_UMAC, RX_HANDLER(SCAN_ITERATION_COMPLETE_UMAC,
iwl_mvm_rx_umac_scan_iter_complete_notif, RX_HANDLER_SYNC, iwl_mvm_rx_umac_scan_iter_complete_notif, RX_HANDLER_SYNC,
struct iwl_umac_scan_iter_complete_notif), struct iwl_umac_scan_iter_complete_notif),
......
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* /*
* Copyright (C) 2012-2014, 2018-2023 Intel Corporation * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH
*/ */
...@@ -3177,6 +3177,23 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, ...@@ -3177,6 +3177,23 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
return ret; return ret;
} }
static void iwl_mvm_find_link_selection_vif(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
if (ieee80211_vif_is_mld(vif) && mvmvif->authorized)
iwl_mvm_select_links(mvmvif->mvm, vif);
}
static void iwl_mvm_post_scan_link_selection(struct iwl_mvm *mvm)
{
ieee80211_iterate_active_interfaces(mvm->hw,
IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_find_link_selection_vif,
NULL);
}
void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb) struct iwl_rx_cmd_buffer *rxb)
{ {
...@@ -3236,6 +3253,9 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, ...@@ -3236,6 +3253,9 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
mvm->last_ebs_successful = false; mvm->last_ebs_successful = false;
mvm->scan_uid_status[uid] = 0; mvm->scan_uid_status[uid] = 0;
if (notif->status == IWL_SCAN_OFFLOAD_COMPLETED)
iwl_mvm_post_scan_link_selection(mvm);
} }
void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm,
......
...@@ -208,3 +208,77 @@ static struct kunit_suite link_grading = { ...@@ -208,3 +208,77 @@ static struct kunit_suite link_grading = {
}; };
kunit_test_suite(link_grading); kunit_test_suite(link_grading);
static const struct valid_link_pair_case {
const char *desc;
u32 esr_disable_reason;
enum nl80211_band band_a;
enum nl80211_band band_b;
bool valid;
} valid_link_pair_cases[] = {
{
.desc = "HB + UHB, valid.",
.band_a = NL80211_BAND_5GHZ,
.band_b = NL80211_BAND_6GHZ,
.valid = true,
},
{
.desc = "LB + HB, no BT.",
.band_a = NL80211_BAND_2GHZ,
.band_b = NL80211_BAND_5GHZ,
.valid = true,
},
{
.desc = "LB + HB, with BT.",
.esr_disable_reason = 0x1,
.band_a = NL80211_BAND_2GHZ,
.band_b = NL80211_BAND_5GHZ,
.valid = false,
},
{
.desc = "Same band",
.band_a = NL80211_BAND_2GHZ,
.band_b = NL80211_BAND_2GHZ,
.valid = false,
},
};
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_mvm_link_sel_data link_a = {
.band = params->band_a,
};
struct iwl_mvm_link_sel_data link_b = {
.band = params->band_b,
};
bool result;
KUNIT_ASSERT_NOT_NULL(test, vif);
iwl_mvm_vif_from_mac80211(vif)->esr_disable_reason =
params->esr_disable_reason;
result = iwl_mvm_mld_valid_link_pair(vif, &link_a, &link_b);
KUNIT_EXPECT_EQ(test, result, params->valid);
kunit_kfree(test, vif);
}
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);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment