]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: iwlwifi: mld: add KUnit tests for PSD/EIRP RSSI adjustment
authorAvinash Bhatt <avinash.bhatt@intel.com>
Sun, 31 May 2026 10:53:07 +0000 (13:53 +0300)
committerMiri Korenblit <miriam.rachel.korenblit@intel.com>
Wed, 3 Jun 2026 14:02:55 +0000 (17:02 +0300)
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 <avinash.bhatt@intel.com>
Link: https://patch.msgid.link/20260531135036.a18b8d0acd62.I68dfcc17359ab8a5abdc84e1e21db4ad1671af41@changeid
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
drivers/net/wireless/intel/iwlwifi/mld/link.c
drivers/net/wireless/intel/iwlwifi/mld/link.h
drivers/net/wireless/intel/iwlwifi/mld/tests/link.c
drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h

index 98b9c4eef583edf20e2e0030e3a55c8d4d659658..2b8b0196692ec3d3bf1714a527b24536e8a702cc 100644 (file)
@@ -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,
index d0aa577de81d9c4d32aaf98b2f773c91cf9a45f9..7b56819d45fe9d9891beb9aea3ed830700ba6382 100644 (file)
@@ -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,
index 21bcc341cd7d9d2d67f082de34c344a20b802c54..a4e5f2be499fc788ae05a32002d671bd51b7deb5 100644 (file)
@@ -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),
        {},
 };
 
index cfed5acaac3a80339b3647e144e5b5a1b2b50b25..7cc8cb6eedd631c8a12fbfb2ee12b178b80ff02d 100644 (file)
@@ -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 */