wifi: iwlwifi: move dBm averaging function into utils

The function really is just a simple math helper. Move it into
iwl-utils.c so that it can also be used by iwlmld.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20250609211928.8cc965af6990.I09bb2137863e888efe756c92d8eb0271ec95456c@changeid
This commit is contained in:
Benjamin Berg
2025-06-09 21:21:18 +03:00
committed by Miri Korenblit
parent 8ecc3928f2
commit 40840afa53
8 changed files with 143 additions and 140 deletions

View File

@@ -97,6 +97,7 @@ config IWLWIFI_OPMODE_MODULAR
default y if IWLDVM=m
default y if IWLMVM=m
default y if IWLMLD=m
default y if IWLWIFI_KUNIT_TESTS=m
comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM or IWLMLD"
depends on IWLDVM=n && IWLMVM=n && IWLMLD=n

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2024 Intel Corporation
* Copyright (C) 2024-2025 Intel Corporation
*/
#include <net/gso.h>
#include <linux/ieee80211.h>
@@ -82,3 +82,114 @@ int iwl_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes,
}
IWL_EXPORT_SYMBOL(iwl_tx_tso_segment);
#endif /* CONFIG_INET */
static u32 iwl_div_by_db(u32 value, u8 db)
{
/*
* 2^32 * 10**(i / 10) for i = [1, 10], skipping 0 and simply stopping
* at 10 dB and looping instead of using a much larger table.
*
* Using 64 bit math is overkill, but means the helper does not require
* a limit on the input range.
*/
static const u32 db_to_val[] = {
0xcb59185e, 0xa1866ba8, 0x804dce7a, 0x65ea59fe, 0x50f44d89,
0x404de61f, 0x331426af, 0x2892c18b, 0x203a7e5b, 0x1999999a,
};
while (value && db > 0) {
u8 change = min_t(u8, db, ARRAY_SIZE(db_to_val));
value = (((u64)value) * db_to_val[change - 1]) >> 32;
db -= change;
}
return value;
}
s8 iwl_average_neg_dbm(const u8 *neg_dbm_values, u8 len)
{
int average_magnitude;
u32 average_factor;
int sum_magnitude = -128;
u32 sum_factor = 0;
int i, count = 0;
/*
* To properly average the decibel values (signal values given in dBm)
* we need to do the math in linear space. Doing a linear average of
* dB (dBm) values is a bit annoying though due to the large range of
* at least -10 to -110 dBm that will not fit into a 32 bit integer.
*
* A 64 bit integer should be sufficient, but then we still have the
* problem that there are no directly usable utility functions
* available.
*
* So, lets not deal with that and instead do much of the calculation
* with a 16.16 fixed point integer along with a base in dBm. 16.16 bit
* gives us plenty of head-room for adding up a few values and even
* doing some math on it. And the tail should be accurate enough too
* (1/2^16 is somewhere around -48 dB, so effectively zero).
*
* i.e. the real value of sum is:
* sum = sum_factor / 2^16 * 10^(sum_magnitude / 10) mW
*
* However, that does mean we need to be able to bring two values to
* a common base, so we need a helper for that.
*
* Note that this function takes an input with unsigned negative dBm
* values but returns a signed dBm (i.e. a negative value).
*/
for (i = 0; i < len; i++) {
int val_magnitude;
u32 val_factor;
/* Assume invalid */
if (neg_dbm_values[i] == 0xff)
continue;
val_factor = 0x10000;
val_magnitude = -neg_dbm_values[i];
if (val_magnitude <= sum_magnitude) {
u8 div_db = sum_magnitude - val_magnitude;
val_factor = iwl_div_by_db(val_factor, div_db);
val_magnitude = sum_magnitude;
} else {
u8 div_db = val_magnitude - sum_magnitude;
sum_factor = iwl_div_by_db(sum_factor, div_db);
sum_magnitude = val_magnitude;
}
sum_factor += val_factor;
count++;
}
/* No valid noise measurement, return a very high noise level */
if (count == 0)
return 0;
average_magnitude = sum_magnitude;
average_factor = sum_factor / count;
/*
* average_factor will be a number smaller than 1.0 (0x10000) at this
* point. What we need to do now is to adjust average_magnitude so that
* average_factor is between -0.5 dB and 0.5 dB.
*
* Just do -1 dB steps and find the point where
* -0.5 dB * -i dB = 0x10000 * 10^(-0.5/10) / i dB
* = div_by_db(0xe429, i)
* is smaller than average_factor.
*/
for (i = 0; average_factor < iwl_div_by_db(0xe429, i); i++) {
/* nothing */
}
return clamp(average_magnitude - i, -128, 0);
}
IWL_EXPORT_SYMBOL(iwl_average_neg_dbm);

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2024 Intel Corporation
* Copyright (C) 2024-2025 Intel Corporation
*/
#ifndef __iwl_utils_h__
#define __iwl_utils_h__
@@ -53,4 +53,6 @@ u32 iwl_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size)
return ie - beacon;
}
s8 iwl_average_neg_dbm(const u8 *neg_dbm_values, u8 len);
#endif /* __iwl_utils_h__ */

View File

@@ -2133,7 +2133,6 @@ bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
s8 iwl_mvm_average_dbm_values(const struct iwl_umac_scan_channel_survey_notif *notif);
extern const struct iwl_hcmd_arr iwl_mvm_groups[];
extern const unsigned int iwl_mvm_groups_size;
#endif

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2012-2014, 2018-2024 Intel Corporation
* Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -11,6 +11,7 @@
#include "mvm.h"
#include "fw/api/scan.h"
#include "iwl-io.h"
#include "iwl-utils.h"
#define IWL_DENSE_EBS_SCAN_RATIO 5
#define IWL_SPARSE_EBS_SCAN_RATIO 1
@@ -3685,117 +3686,6 @@ static int iwl_mvm_chanidx_from_phy(struct iwl_mvm *mvm,
return -EINVAL;
}
static u32 iwl_mvm_div_by_db(u32 value, u8 db)
{
/*
* 2^32 * 10**(i / 10) for i = [1, 10], skipping 0 and simply stopping
* at 10 dB and looping instead of using a much larger table.
*
* Using 64 bit math is overkill, but means the helper does not require
* a limit on the input range.
*/
static const u32 db_to_val[] = {
0xcb59185e, 0xa1866ba8, 0x804dce7a, 0x65ea59fe, 0x50f44d89,
0x404de61f, 0x331426af, 0x2892c18b, 0x203a7e5b, 0x1999999a,
};
while (value && db > 0) {
u8 change = min_t(u8, db, ARRAY_SIZE(db_to_val));
value = (((u64)value) * db_to_val[change - 1]) >> 32;
db -= change;
}
return value;
}
VISIBLE_IF_IWLWIFI_KUNIT s8
iwl_mvm_average_dbm_values(const struct iwl_umac_scan_channel_survey_notif *notif)
{
s8 average_magnitude;
u32 average_factor;
s8 sum_magnitude = -128;
u32 sum_factor = 0;
int i, count = 0;
/*
* To properly average the decibel values (signal values given in dBm)
* we need to do the math in linear space. Doing a linear average of
* dB (dBm) values is a bit annoying though due to the large range of
* at least -10 to -110 dBm that will not fit into a 32 bit integer.
*
* A 64 bit integer should be sufficient, but then we still have the
* problem that there are no directly usable utility functions
* available.
*
* So, lets not deal with that and instead do much of the calculation
* with a 16.16 fixed point integer along with a base in dBm. 16.16 bit
* gives us plenty of head-room for adding up a few values and even
* doing some math on it. And the tail should be accurate enough too
* (1/2^16 is somewhere around -48 dB, so effectively zero).
*
* i.e. the real value of sum is:
* sum = sum_factor / 2^16 * 10^(sum_magnitude / 10) mW
*
* However, that does mean we need to be able to bring two values to
* a common base, so we need a helper for that.
*
* Note that this function takes an input with unsigned negative dBm
* values but returns a signed dBm (i.e. a negative value).
*/
for (i = 0; i < ARRAY_SIZE(notif->noise); i++) {
s8 val_magnitude;
u32 val_factor;
if (notif->noise[i] == 0xff)
continue;
val_factor = 0x10000;
val_magnitude = -notif->noise[i];
if (val_magnitude <= sum_magnitude) {
u8 div_db = sum_magnitude - val_magnitude;
val_factor = iwl_mvm_div_by_db(val_factor, div_db);
val_magnitude = sum_magnitude;
} else {
u8 div_db = val_magnitude - sum_magnitude;
sum_factor = iwl_mvm_div_by_db(sum_factor, div_db);
sum_magnitude = val_magnitude;
}
sum_factor += val_factor;
count++;
}
/* No valid noise measurement, return a very high noise level */
if (count == 0)
return 0;
average_magnitude = sum_magnitude;
average_factor = sum_factor / count;
/*
* average_factor will be a number smaller than 1.0 (0x10000) at this
* point. What we need to do now is to adjust average_magnitude so that
* average_factor is between -0.5 dB and 0.5 dB.
*
* Just do -1 dB steps and find the point where
* -0.5 dB * -i dB = 0x10000 * 10^(-0.5/10) / i dB
* = div_by_db(0xe429, i)
* is smaller than average_factor.
*/
for (i = 0; average_factor < iwl_mvm_div_by_db(0xe429, i); i++) {
/* nothing */
}
return average_magnitude - i;
}
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_average_dbm_values);
void iwl_mvm_rx_channel_survey_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb)
{
@@ -3853,5 +3743,6 @@ void iwl_mvm_rx_channel_survey_notif(struct iwl_mvm *mvm,
info->time_busy = le32_to_cpu(notif->busy_time);
info->time_rx = le32_to_cpu(notif->rx_time);
info->time_tx = le32_to_cpu(notif->tx_time);
info->noise = iwl_mvm_average_dbm_values(notif);
info->noise =
iwl_average_neg_dbm(notif->noise, ARRAY_SIZE(notif->noise));
}

View File

@@ -1,3 +1,3 @@
iwlmvm-tests-y += module.o links.o scan.o hcmd.o
iwlmvm-tests-y += module.o links.o hcmd.o
obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlmvm-tests.o

View File

@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
iwlwifi-tests-y += module.o devinfo.o
iwlwifi-tests-y += module.o devinfo.o utils.o
ccflags-y += -I$(src)/../

View File

@@ -1,20 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit tests for channel helper functions
* KUnit tests for utilities
*
* Copyright (C) 2024 Intel Corporation
* Copyright (C) 2024-2025 Intel Corporation
*/
#include <net/mac80211.h>
#include "../mvm.h"
#include "../iwl-utils.h"
#include <kunit/test.h>
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
MODULE_IMPORT_NS("IWLWIFI");
static const struct acs_average_db_case {
static const struct average_neg_db_case {
const char *desc;
u8 neg_dbm[22];
s8 result;
} acs_average_db_cases[] = {
} average_neg_db_cases[] = {
{
.desc = "Smallest possible value, all filled",
.neg_dbm = {
@@ -73,38 +72,38 @@ static const struct acs_average_db_case {
},
};
KUNIT_ARRAY_PARAM_DESC(acs_average_db, acs_average_db_cases, desc)
KUNIT_ARRAY_PARAM_DESC(average_neg_db, average_neg_db_cases, desc)
static void test_acs_average_db(struct kunit *test)
static void test_average_neg_db(struct kunit *test)
{
const struct acs_average_db_case *params = test->param_value;
struct iwl_umac_scan_channel_survey_notif notif;
const struct average_neg_db_case *params = test->param_value;
u8 reversed[ARRAY_SIZE(params->neg_dbm)];
int i;
/* Test the values in the given order */
for (i = 0; i < ARRAY_SIZE(params->neg_dbm); i++)
notif.noise[i] = params->neg_dbm[i];
KUNIT_ASSERT_EQ(test,
iwl_mvm_average_dbm_values(&notif),
iwl_average_neg_dbm(params->neg_dbm,
ARRAY_SIZE(params->neg_dbm)),
params->result);
/* Test in reverse order */
for (i = 0; i < ARRAY_SIZE(params->neg_dbm); i++)
notif.noise[ARRAY_SIZE(params->neg_dbm) - i - 1] =
reversed[ARRAY_SIZE(params->neg_dbm) - i - 1] =
params->neg_dbm[i];
KUNIT_ASSERT_EQ(test,
iwl_mvm_average_dbm_values(&notif),
iwl_average_neg_dbm(reversed,
ARRAY_SIZE(params->neg_dbm)),
params->result);
}
static struct kunit_case acs_average_db_case[] = {
KUNIT_CASE_PARAM(test_acs_average_db, acs_average_db_gen_params),
static struct kunit_case average_db_case[] = {
KUNIT_CASE_PARAM(test_average_neg_db, average_neg_db_gen_params),
{}
};
static struct kunit_suite acs_average_db = {
.name = "iwlmvm-acs-average-db",
.test_cases = acs_average_db_case,
static struct kunit_suite average_db = {
.name = "iwl-average-db",
.test_cases = average_db_case,
};
kunit_test_suite(acs_average_db);
kunit_test_suite(average_db);