From: Avinash Bhatt Add tests for PSD/EIRP RSSI adjustment which compensates measurements when APs use PSD-based power scaling with bandwidth. Tests cover all power types, bandwidths, and limiting scenarios. Signed-off-by: Avinash Bhatt Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/link.c | 4 +- drivers/net/wireless/intel/iwlwifi/mld/link.h | 1 + .../wireless/intel/iwlwifi/mld/tests/link.c | 372 ++++++++++++++++++ .../wireless/intel/iwlwifi/mld/tests/utils.h | 2 + 4 files changed, 378 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index 98b9c4eef583..2b8b0196692e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -1098,7 +1098,8 @@ static s8 iwl_mld_get_primary_psd(const struct ieee80211_parsed_tpe_psd *psd, return psd->power[primary_idx] / 2; } -static s8 iwl_mld_get_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf) +VISIBLE_IF_IWLWIFI_KUNIT s8 +iwl_mld_get_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf) { const struct ieee80211_parsed_tpe *tpe = &link_conf->tpe; s8 psd_20mhz, psd_oper, psd_local, psd_reg, psd_boost; @@ -1217,6 +1218,7 @@ static s8 iwl_mld_get_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf) return adjustment; } +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_psd_eirp_rssi_adjust); /* This function calculates the grade of a link. Returns 0 in error case */ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h index d0aa577de81d..7b56819d45fe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h @@ -145,6 +145,7 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, #if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) s8 iwl_mld_get_dup_beacon_rssi_adjust(struct iwl_mld *mld, struct ieee80211_bss_conf *link_conf); +s8 iwl_mld_get_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf); #endif unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c index 21bcc341cd7d..a4e5f2be499f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c @@ -158,6 +158,354 @@ static const struct dup_beacon_test_case dup_beacon_cases[] = { KUNIT_ARRAY_PARAM_DESC(test_dup_beacon_rssi_adjust, dup_beacon_cases, desc); +struct psd_eirp_test_case { + const char *desc; + const struct cfg80211_chan_def *chandef; + enum ieee80211_ap_reg_power power_type; + struct { + s8 psd_20; + s8 psd_oper; + s8 eirp_20; + s8 eirp_oper; + } local, reg; + s8 expected_adj; + struct { + bool no_psd_data; + bool no_eirp_data; + bool no_reg_psd_data; + bool has_partial_psd; + u8 psd_partial_count; + bool non_uniform_psd; + bool has_unusable_channels; + } flags; +}; + +static const struct psd_eirp_test_case psd_eirp_cases[] = { + { + .desc = "20 MHz VLP baseline - no boost expected", + .chandef = &chandef_6ghz_20mhz, + .power_type = IEEE80211_REG_VLP_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 40, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 40, + }, + .expected_adj = 0, + }, + { + .desc = "40 MHz VLP - power limit prevents boost", + .chandef = &chandef_6ghz_40mhz, + .power_type = IEEE80211_REG_VLP_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 46, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 46, + }, + .expected_adj = 0, + }, + { + .desc = "80 MHz LPI - power limit caps the boost", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .expected_adj = 3, + }, + { + .desc = "160 MHz LPI - power limit caps the boost", + .chandef = &chandef_6ghz_160mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 58, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 58, + }, + .expected_adj = 3, + }, + { + .desc = "320 MHz SP - power limit caps the boost", + .chandef = &chandef_6ghz_320mhz_pri0, + .power_type = IEEE80211_REG_SP_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 63, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 63, + }, + .expected_adj = 3, + }, + { + .desc = "80 MHz - EIRP prevents boost", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 20, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 20, + }, + .expected_adj = 0, + }, + { + .desc = "40 MHz - regulatory TPE sets lower limits", + .chandef = &chandef_6ghz_40mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 30, .psd_oper = 30, + .eirp_20 = 50, .eirp_oper = 56, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 46, + }, + .expected_adj = 3, + }, + { + .desc = "80 MHz - PSD missing, use EIRP only", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = S8_MAX, .psd_oper = S8_MAX, + .eirp_20 = 40, .eirp_oper = 52, + }, + .reg = { + .psd_20 = S8_MAX, .psd_oper = S8_MAX, + .eirp_20 = 40, .eirp_oper = 52, + }, + .expected_adj = 0, + .flags.no_psd_data = true, + }, + { + .desc = "80 MHz - single PSD source available", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .expected_adj = 3, + .flags.no_reg_psd_data = true, + }, + { + .desc = "80 MHz - partial PSD data present", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 24, .psd_oper = 24, + .eirp_20 = 40, .eirp_oper = 56, + }, + .reg = { + .psd_20 = 24, .psd_oper = 24, + .eirp_20 = 40, .eirp_oper = 56, + }, + .expected_adj = 0, + .flags.has_partial_psd = true, + .flags.psd_partial_count = 2, + }, + { + .desc = "160 MHz - different PSD per sub-channel", + .chandef = &chandef_6ghz_160mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 8, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 58, + }, + .reg = { + .psd_20 = 8, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 58, + }, + .expected_adj = 11, + .flags.non_uniform_psd = true, + }, + { + .desc = "80 MHz - EIRP missing, use PSD only", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = S8_MAX, .eirp_oper = S8_MAX, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = S8_MAX, .eirp_oper = S8_MAX, + }, + .expected_adj = 3, + .flags.no_eirp_data = true, + }, + { + .desc = "80 MHz - skip unusable channels in average", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .expected_adj = 3, + .flags.has_unusable_channels = true, + }, + { + .desc = "40 MHz - no negative adjustment", + .chandef = &chandef_6ghz_40mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 18, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 18, + }, + .expected_adj = 0, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(test_psd_eirp_rssi_adjust, psd_eirp_cases, desc); + +static void setup_psd(struct ieee80211_bss_conf *link_conf, + const struct psd_eirp_test_case *params, + int num_subchannels) +{ + int i; + + if (params->flags.no_psd_data) { + link_conf->tpe.psd_local[0].valid = false; + link_conf->tpe.psd_reg_client[0].valid = false; + link_conf->tpe.psd_local[0].count = 0; + link_conf->tpe.psd_reg_client[0].count = 0; + } else if (params->flags.no_reg_psd_data) { + link_conf->tpe.psd_local[0].valid = true; + link_conf->tpe.psd_local[0].count = num_subchannels; + link_conf->tpe.psd_reg_client[0].valid = false; + link_conf->tpe.psd_reg_client[0].count = 0; + } else if (params->flags.has_partial_psd) { + link_conf->tpe.psd_local[0].valid = true; + link_conf->tpe.psd_local[0].count = + params->flags.psd_partial_count; + link_conf->tpe.psd_reg_client[0].valid = true; + link_conf->tpe.psd_reg_client[0].count = + params->flags.psd_partial_count; + } else { + link_conf->tpe.psd_local[0].valid = true; + link_conf->tpe.psd_local[0].count = num_subchannels; + link_conf->tpe.psd_reg_client[0].valid = true; + link_conf->tpe.psd_reg_client[0].count = num_subchannels; + } + + /* TPE element stores PSD limit as value * 2 */ + if (params->flags.non_uniform_psd) { + /* PSD varies per sub-channel: 10/12/10/8 dBm pattern */ + static const s8 psd_values[] = {20, 24, 20, 16, 20, 24, 20, 16, + 20, 24, 20, 16, 20, 24, 20}; + /* Set primary channel (index 0) explicitly */ + link_conf->tpe.psd_local[0].power[0] = + params->local.psd_20 * 2; + link_conf->tpe.psd_reg_client[0].power[0] = + params->reg.psd_20 * 2; + /* Set remaining subchannels with pattern */ + for (i = 1; i < num_subchannels; i++) { + link_conf->tpe.psd_local[0].power[i] = + psd_values[i - 1]; + link_conf->tpe.psd_reg_client[0].power[i] = + psd_values[i - 1]; + } + } else if (params->flags.no_psd_data) { + for (i = 0; i < num_subchannels; i++) { + link_conf->tpe.psd_local[0].power[i] = S8_MAX; + link_conf->tpe.psd_reg_client[0].power[i] = S8_MAX; + } + } else if (params->flags.has_unusable_channels) { + /* Alternate usable/unusable channels for S8_MIN test */ + /* Set primary channel (index 0) explicitly */ + link_conf->tpe.psd_local[0].power[0] = + params->local.psd_20 * 2; + link_conf->tpe.psd_reg_client[0].power[0] = + params->reg.psd_20 * 2; + /* Alternate usable/unusable for remaining subchannels */ + for (i = 1; i < num_subchannels; i++) { + if (i % 2 == 0) { + link_conf->tpe.psd_local[0].power[i] = + params->local.psd_oper * 2; + link_conf->tpe.psd_reg_client[0].power[i] = + params->reg.psd_oper * 2; + } else { + link_conf->tpe.psd_local[0].power[i] = S8_MIN; + link_conf->tpe.psd_reg_client[0].power[i] = + S8_MIN; + } + } + } else { + /* Set primary channel (index 0) separately */ + link_conf->tpe.psd_local[0].power[0] = + params->local.psd_20 * 2; + link_conf->tpe.psd_reg_client[0].power[0] = + params->reg.psd_20 * 2; + /* Set remaining subchannels */ + for (i = 1; i < num_subchannels; i++) { + link_conf->tpe.psd_local[0].power[i] = + params->local.psd_oper * 2; + link_conf->tpe.psd_reg_client[0].power[i] = + params->reg.psd_oper * 2; + } + } +} + +static void setup_eirp(struct ieee80211_bss_conf *link_conf, + const struct psd_eirp_test_case *params, + int num_subchannels) +{ + int i; + int count = ilog2(num_subchannels) + 1; + + link_conf->tpe.max_local[0].valid = !params->flags.no_eirp_data; + link_conf->tpe.max_reg_client[0].valid = !params->flags.no_eirp_data; + + if (params->flags.no_eirp_data) { + link_conf->tpe.max_local[0].count = 0; + link_conf->tpe.max_reg_client[0].count = 0; + return; + } + + link_conf->tpe.max_local[0].count = count; + link_conf->tpe.max_reg_client[0].count = count; + + /* TPE element stores EIRP limit as value * 2 */ + link_conf->tpe.max_local[0].power[0] = params->local.eirp_20 * 2; + link_conf->tpe.max_reg_client[0].power[0] = params->reg.eirp_20 * 2; + for (i = 1; i < count; i++) { + link_conf->tpe.max_local[0].power[i] = + params->local.eirp_oper * 2; + link_conf->tpe.max_reg_client[0].power[i] = + params->reg.eirp_oper * 2; + } +} + static void test_dup_beacon_rssi_adjust(struct kunit *test) { const struct dup_beacon_test_case *params = test->param_value; @@ -192,10 +540,34 @@ static void test_dup_beacon_rssi_adjust(struct kunit *test) KUNIT_EXPECT_EQ(test, result, params->expected_adj); } +static void test_psd_eirp_rssi_adjust(struct kunit *test) +{ + const struct psd_eirp_test_case *params = test->param_value; + struct ieee80211_bss_conf *link_conf; + int num_subchannels; + s8 result; + + KUNIT_ALLOC_AND_ASSERT(test, link_conf); + + link_conf->power_type = params->power_type; + link_conf->chanreq.oper = *params->chandef; + num_subchannels = + nl80211_chan_width_to_mhz(params->chandef->width) / 20; + + setup_psd(link_conf, params, num_subchannels); + setup_eirp(link_conf, params, num_subchannels); + + result = iwl_mld_get_psd_eirp_rssi_adjust(link_conf); + + KUNIT_EXPECT_EQ(test, result, params->expected_adj); +} + static struct kunit_case link_cases[] = { KUNIT_CASE_PARAM(test_missed_beacon, test_missed_beacon_gen_params), KUNIT_CASE_PARAM(test_dup_beacon_rssi_adjust, test_dup_beacon_rssi_adjust_gen_params), + KUNIT_CASE_PARAM(test_psd_eirp_rssi_adjust, + test_psd_eirp_rssi_adjust_gen_params), {}, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h index cfed5acaac3a..7cc8cb6eedd6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h @@ -77,6 +77,8 @@ CHANNEL(chan_6ghz_221, NL80211_BAND_6GHZ, 7055); NL80211_CHAN_WIDTH_160) \ CHANDEF(chandef_6ghz_320mhz, chan_6ghz, 6105, \ NL80211_CHAN_WIDTH_320) \ + CHANDEF(chandef_6ghz_320mhz_pri0, chan_6ghz, 6265, \ + NL80211_CHAN_WIDTH_320) \ CHANDEF(chandef_6ghz_221_160mhz, chan_6ghz_221, 6985, \ NL80211_CHAN_WIDTH_160) \ /* Feel free to add more */ -- 2.34.1