Commit 40840afa authored by Benjamin Berg's avatar Benjamin Berg Committed by Miri Korenblit
Browse files

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: default avatarBenjamin Berg <benjamin.berg@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20250609211928.8cc965af6990.I09bb2137863e888efe756c92d8eb0271ec95456c@changeid
parent 8ecc3928
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+112 −1
Original line number Diff line number Diff line
/* 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);
+3 −1
Original line number Diff line number Diff line
/* 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__ */
+0 −1
Original line number Diff line number Diff line
@@ -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
+4 −113
Original line number Diff line number Diff line
// 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));
}
Loading