Commit 10159a45 authored by Miri Korenblit's avatar Miri Korenblit Committed by Johannes Berg
Browse files

wifi: iwlwifi: disable eSR when BT is active



eSR should be disabled when BT Coex is active and:
- LB link is the primary link.
- LB link is the secondary link and the predicted BT penalty
  (the wifi loss rate caused by BT interference) is higher
  than a given threshold.
If one of the conditions above is no longer true then re-enable eSR.

In order to implement this, add support for version 5 of
BT_PROFILE_NOTIFICATION, in which the bt penalty is provided
by FW.

Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Reviewed-by: default avatarGregory Greenman <gregory.greenman@intel.com>
Link: https://msgid.link/20240131225342.b922b6485af8.I7d808ce535a7372aca9cb85c045755e6788a4904@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 17903a28
Loading
Loading
Loading
Loading
+11 −3
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
 * Copyright (C) 2023 Intel Corporation
 * Copyright (C) 2013-2014, 2018-2019 Intel Corporation
 * Copyright (C) 2013-2014 Intel Mobile Communications GmbH
 * Copyright (C) 2017 Intel Deutschland GmbH
@@ -170,7 +171,11 @@ enum iwl_bt_ci_compliance {
 * @bt_activity_grading: the activity of BT &enum iwl_bt_activity_grading
 * @ttc_status: is TTC enabled - one bit per PHY
 * @rrc_status: is RRC enabled - one bit per PHY
 * @reserved: reserved
 * The following fields are only for version 5, and are reserved in version 4:
 * @wifi_loss_low_rssi: The predicted lost WiFi rate (% of air time that BT is
 *	utilizing) when the RSSI is low (<= -65 dBm)
 * @wifi_loss_mid_high_rssi: The predicted lost WiFi rate (% of air time that
 *	BT is utilizing) when the RSSI is mid/high (>= -65 dBm)
 */
struct iwl_bt_coex_profile_notif {
	__le32 mbox_msg[4];
@@ -182,7 +187,10 @@ struct iwl_bt_coex_profile_notif {
	__le32 bt_activity_grading;
	u8 ttc_status;
	u8 rrc_status;
	__le16 reserved;
} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */
	u8 wifi_loss_low_rssi;
	u8 wifi_loss_mid_high_rssi;
} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4
	     * BT_COEX_PROFILE_NTFY_API_S_VER_5
	     */

#endif /* __iwl_fw_api_coex_h__ */
+124 −0
Original line number Diff line number Diff line
@@ -252,6 +252,124 @@ static void iwl_mvm_bt_coex_tcm_based_ci(struct iwl_mvm *mvm,
	swap(data->primary, data->secondary);
}

static void iwl_mvm_bt_coex_enable_esr(struct iwl_mvm *mvm,
				       struct ieee80211_vif *vif, bool enable)
{
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
	int link_id;

	lockdep_assert_held(&mvm->mutex);

	if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif))
		return;

	/* Done already */
	if (mvmvif->bt_coex_esr_disabled == !enable)
		return;

	mvmvif->bt_coex_esr_disabled = !enable;

	/* Nothing to do */
	if (mvmvif->esr_active == enable)
		return;

	if (enable) {
		/* Try to re-enable eSR*/
		iwl_mvm_mld_select_links(mvm, vif, false);
		return;
	}

	/*
	 * Find the primary link, as we want to switch to it and drop the
	 * secondary one.
	 */
	link_id = iwl_mvm_mld_get_primary_link(mvm, vif, vif->active_links);
	WARN_ON(link_id < 0);

	ieee80211_set_active_links_async(vif,
					 vif->active_links & BIT(link_id));
}

/*
 * This function receives the LB link id and checks if eSR should be
 * enabled or disabled (due to BT coex)
 */
bool
iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm,
				   struct ieee80211_vif *vif,
				   int link_id, int primary_link)
{
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
	struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
	bool have_wifi_loss_rate =
		iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
					BT_PROFILE_NOTIFICATION, 0) > 4;
	s8 link_rssi = 0;
	u8 wifi_loss_rate;

	lockdep_assert_held(&mvm->mutex);

	if (mvm->last_bt_notif.wifi_loss_low_rssi == BT_OFF)
		return true;

	 /* If LB link is the primary one we should always disable eSR */
	if (link_id == primary_link)
		return false;

	/* The feature is not supported */
	if (!have_wifi_loss_rate)
		return true;

	/*
	 * We might not have a link_info when checking whether we can
	 * (re)enable eSR - the LB link might not exist yet
	 */
	if (link_info)
		link_rssi = (s8)link_info->beacon_stats.avg_signal;

	/*
	 * In case we don't know the RSSI - take the lower wifi loss,
	 * so we will more likely enter eSR, and if RSSI is low -
	 * we will get an update on this and exit eSR.
	 */
	if (!link_rssi)
		wifi_loss_rate = mvm->last_bt_notif.wifi_loss_mid_high_rssi;

	else if (!mvmvif->bt_coex_esr_disabled)
		 /* RSSI needs to get really low to disable eSR... */
		wifi_loss_rate =
			link_rssi <= -IWL_MVM_BT_COEX_DISABLE_ESR_THRESH ?
				mvm->last_bt_notif.wifi_loss_low_rssi :
				mvm->last_bt_notif.wifi_loss_mid_high_rssi;
	else
		/* ...And really high before we enable it back */
		wifi_loss_rate =
			link_rssi <= -IWL_MVM_BT_COEX_ENABLE_ESR_THRESH ?
				mvm->last_bt_notif.wifi_loss_low_rssi :
				mvm->last_bt_notif.wifi_loss_mid_high_rssi;

	return wifi_loss_rate <= IWL_MVM_BT_COEX_WIFI_LOSS_THRESH;
}

void iwl_mvm_bt_coex_update_vif_esr(struct iwl_mvm *mvm,
				    struct ieee80211_vif *vif,
				    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;

	/* Not assoc, not MLD vif or only one usable link */
	if (primary_link < 0)
		return;

	enable = iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link_id,
						    primary_link);

	iwl_mvm_bt_coex_enable_esr(mvm, vif, enable);
}

static void iwl_mvm_bt_notif_per_link(struct iwl_mvm *mvm,
				      struct ieee80211_vif *vif,
				      struct iwl_bt_iterator_data *data,
@@ -297,6 +415,8 @@ static void iwl_mvm_bt_notif_per_link(struct iwl_mvm *mvm,
		return;
	}

	iwl_mvm_bt_coex_update_vif_esr(mvm, vif, link_id);

	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2))
		min_ag_for_static_smps = BT_VERY_HIGH_TRAFFIC;
	else
@@ -432,6 +552,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
		return;
	}

	/* When BT is off this will be 0 */
	if (data->notif->wifi_loss_low_rssi == BT_OFF)
		iwl_mvm_bt_coex_enable_esr(mvm, vif, true);

	for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++)
		iwl_mvm_bt_notif_per_link(mvm, vif, data, link_id);
}
+3 −0
Original line number Diff line number Diff line
@@ -11,6 +11,9 @@
#include "fw-api.h"

#define IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM		20
#define IWL_MVM_BT_COEX_DISABLE_ESR_THRESH	69
#define IWL_MVM_BT_COEX_ENABLE_ESR_THRESH	63
#define IWL_MVM_BT_COEX_WIFI_LOSS_THRESH	0

#define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT	(100 * USEC_PER_MSEC)
#define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT	(100 * USEC_PER_MSEC)
+106 −1
Original line number Diff line number Diff line
@@ -603,6 +603,7 @@ static int iwl_mvm_mld_mac_sta_state(struct ieee80211_hw *hw,
struct iwl_mvm_link_sel_data {
	u8 link_id;
	enum nl80211_band band;
	enum nl80211_chan_width width;
	bool active;
};

@@ -655,6 +656,7 @@ void iwl_mvm_mld_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif,

		data[n_data].link_id = link_id;
		data[n_data].band = link_conf->chandef.chan->band;
		data[n_data].width = link_conf->chandef.width;
		data[n_data].active = vif->active_links & BIT(link_id);
		n_data++;
	}
@@ -1215,13 +1217,116 @@ iwl_mvm_mld_change_sta_links(struct ieee80211_hw *hw,
	return ret;
}

/*
 * This function receives a subset of the usable links bitmap and
 * returns the primary link id, and -1 if such link doesn't exist
 * (e.g. non-MLO connection) or wasn't found.
 */
int iwl_mvm_mld_get_primary_link(struct iwl_mvm *mvm,
				 struct ieee80211_vif *vif,
				 unsigned long usable_links)
{
	struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS];
	u8 link_id, n_data = 0;

	if (!ieee80211_vif_is_mld(vif) || !vif->cfg.assoc)
		return -1;

	for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
		struct ieee80211_bss_conf *link_conf =
			link_conf_dereference_protected(vif, link_id);

		if (WARN_ON_ONCE(!link_conf))
			continue;

		data[n_data].link_id = link_id;
		data[n_data].band = link_conf->chandef.chan->band;
		data[n_data].width = link_conf->chandef.width;
		data[n_data].active = true;
		n_data++;
	}

	if (n_data <= 1)
		return -1;

	/* The logic should be modified to handle more than 2 links */
	WARN_ON_ONCE(n_data > 2);

	/* Primary link is the link with the wider bandwidth or higher band */
	if (data[0].width > data[1].width)
		return data[0].link_id;
	if (data[0].width < data[1].width)
		return data[1].link_id;
	if (data[0].band >= data[1].band)
		return data[0].link_id;

	return data[1].link_id;
}

/*
 * This function receives a bitmap of usable links and check if we can enter
 * eSR on those links.
 */
static bool iwl_mvm_can_enter_esr(struct iwl_mvm *mvm,
				  struct ieee80211_vif *vif,
				  unsigned long desired_links)
{
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
	int primary_link = iwl_mvm_mld_get_primary_link(mvm, vif,
							desired_links);
	bool ret = true;
	int link_id;

	if (primary_link < 0)
		return false;

	for_each_set_bit(link_id, &desired_links, IEEE80211_MLD_MAX_NUM_LINKS) {
		struct ieee80211_bss_conf *link_conf =
			link_conf_dereference_protected(vif, link_id);

		if (WARN_ON_ONCE(!link_conf))
			continue;

		/* BT Coex effects eSR mode only if one of the link is on LB */
		if (link_conf->chandef.chan->band != NL80211_BAND_2GHZ)
			continue;

		ret = iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link_id,
							 primary_link);
		// Mark eSR as disabled for the next time
		if (!ret)
			mvmvif->bt_coex_esr_disabled = true;
		break;
	}

	return ret;
}

static bool iwl_mvm_mld_can_activate_links(struct ieee80211_hw *hw,
					   struct ieee80211_vif *vif,
					   u16 desired_links)
{
	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
	int n_links = hweight16(desired_links);
	bool ret = true;

	if (n_links <= 1)
		return true;

	return hweight16(desired_links) <= iwl_mvm_max_active_links(mvm, vif);
	mutex_lock(&mvm->mutex);

	/* Check if HW supports the wanted number of links */
	if (n_links > iwl_mvm_max_active_links(mvm, vif)) {
		ret = false;
		goto unlock;
	}

	/* If it is an eSR device, check that we can enter eSR */
	if (iwl_mvm_is_esr_supported(mvm->fwrt.trans))
		ret = iwl_mvm_can_enter_esr(mvm, vif, desired_links);
unlock:
	mutex_unlock(&mvm->mutex);
	return ret;
}

const struct ieee80211_ops iwl_mvm_mld_hw_ops = {
+18 −3
Original line number Diff line number Diff line
@@ -359,6 +359,7 @@ struct iwl_mvm_vif_link_info {
 * @pm_enabled - indicate if MAC power management is allowed
 * @monitor_active: indicates that monitor context is configured, and that the
 *	interface should get quota etc.
 * @bt_coex_esr_disabled: indicates if esr is disabled due to bt coex
 * @low_latency: bit flags for low latency
 *	see enum &iwl_mvm_low_latency_cause for causes.
 * @low_latency_actual: boolean, indicates low latency is set,
@@ -389,6 +390,7 @@ struct iwl_mvm_vif {
	bool pm_enabled;
	bool monitor_active;
	bool esr_active;
	bool bt_coex_esr_disabled;

	u8 low_latency: 6;
	u8 low_latency_actual: 1;
@@ -1570,13 +1572,17 @@ static inline int iwl_mvm_max_active_links(struct iwl_mvm *mvm,
					   struct ieee80211_vif *vif)
{
	struct iwl_trans *trans = mvm->fwrt.trans;
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);

	lockdep_assert_held(&mvm->mutex);

	if (vif->type == NL80211_IFTYPE_AP)
		return mvm->fw->ucode_capa.num_beacons;

	if (iwl_mvm_is_esr_supported(trans) ||
	    (CSR_HW_RFID_TYPE(trans->hw_rf_id) == IWL_CFG_RF_TYPE_FM &&
	     CSR_HW_RFID_IS_CDB(trans->hw_rf_id)))
	if ((iwl_mvm_is_esr_supported(trans) &&
	     !mvmvif->bt_coex_esr_disabled) ||
	    ((CSR_HW_RFID_TYPE(trans->hw_rf_id) == IWL_CFG_RF_TYPE_FM &&
	     CSR_HW_RFID_IS_CDB(trans->hw_rf_id))))
		return IWL_MVM_FW_MAX_ACTIVE_LINKS_NUM;

	return 1;
@@ -2119,6 +2125,12 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
u8 iwl_mvm_bt_coex_get_single_ant_msk(struct iwl_mvm *mvm, u8 enabled_ants);
u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
			   struct ieee80211_tx_info *info, u8 ac);
bool iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm,
					struct ieee80211_vif *vif,
					int link_id, int primary_link);
void iwl_mvm_bt_coex_update_vif_esr(struct iwl_mvm *mvm,
				    struct ieee80211_vif *vif,
				    int link_id);

/* beacon filtering */
#ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -2733,4 +2745,7 @@ bool iwl_mvm_enable_fils(struct iwl_mvm *mvm,
			 struct ieee80211_chanctx_conf *ctx);
void iwl_mvm_mld_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
			      bool valid_links_changed);
int iwl_mvm_mld_get_primary_link(struct iwl_mvm *mvm,
				 struct ieee80211_vif *vif,
				 unsigned long usable_links);
#endif /* __IWL_MVM_H__ */
Loading