diff mbox series

[iwlwifi-next,09/14] wifi: iwlwifi: mld: allow EMLSR with 2.4 GHz when BT is ON

Message ID 20250429224932.a6d662721aec.I48e3d5bd6b0b8583f98057c38d2ee30fff5abd8a@changeid
State New
Headers show
Series [iwlwifi-next,01/14] wifi: iwlwifi: prepare for reading WPFC from UEFI | expand

Commit Message

Miri Korenblit April 29, 2025, 7:53 p.m. UTC
From: Somashekhar Puttagangaiah <somashekhar.puttagangaiah@intel.com>

When BT is ON, EMLSR with one of the links operating on 2.4 GHz
is allowed only if it meets following conditions.
In this patch:
1. during link selection, when BT is ON, allow emlsr only if BT
   pentalty is < 7%.
2. exit EMLSR if BT is turned ON and one of the links is operating
   on 2.4 GHz with BT penalty > 7%

Signed-off-by: Somashekhar Puttagangaiah <somashekhar.puttagangaiah@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/coex.c |  8 +--
 drivers/net/wireless/intel/iwlwifi/mld/mld.h  |  4 +-
 drivers/net/wireless/intel/iwlwifi/mld/mlo.c  | 62 ++++++++++++++++---
 .../intel/iwlwifi/mld/tests/link-selection.c  |  6 ++
 4 files changed, 62 insertions(+), 18 deletions(-)
diff mbox series

Patch

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/coex.c b/drivers/net/wireless/intel/iwlwifi/mld/coex.c
index 5f262bd43f21..32c727b3b391 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/coex.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/coex.c
@@ -24,17 +24,13 @@  int iwl_mld_send_bt_init_conf(struct iwl_mld *mld)
 void iwl_mld_handle_bt_coex_notif(struct iwl_mld *mld,
 				  struct iwl_rx_packet *pkt)
 {
-	const struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
+	const struct iwl_bt_coex_profile_notif *notif = (const void *)pkt->data;
 	const struct iwl_bt_coex_profile_notif zero_notif = {};
 	/* zeroed structure means that BT is OFF */
 	bool bt_is_active = memcmp(notif, &zero_notif, sizeof(*notif));
 
-	if (bt_is_active == mld->bt_is_active)
-		return;
-
+	mld->last_bt_notif = *notif;
 	IWL_DEBUG_INFO(mld, "BT was turned %s\n", bt_is_active ? "ON" : "OFF");
 
-	mld->bt_is_active = bt_is_active;
-
 	iwl_mld_emlsr_check_bt(mld);
 }
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
index a4a16da6ebf3..7007a43bca4d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
@@ -126,7 +126,6 @@ 
  *	cleanup using iwl_mld_free_internal_sta
  * @netdetect: indicates the FW is in suspend mode with netdetect configured
  * @p2p_device_vif: points to the p2p device vif if exists
- * @bt_is_active: indicates that BT is active
  * @dev: pointer to device struct. For printing purposes
  * @trans: pointer to the transport layer
  * @cfg: pointer to the device configuration
@@ -184,6 +183,7 @@ 
  * @ptp_data: data of the PTP clock
  * @time_sync: time sync data.
  * @ftm_initiator: FTM initiator data
+ * @last_bt_notif: last received BT Coex notif
  */
 struct iwl_mld {
 	/* Add here fields that need clean up on restart */
@@ -207,7 +207,7 @@  struct iwl_mld {
 		bool netdetect;
 #endif /* CONFIG_PM_SLEEP */
 		struct ieee80211_vif *p2p_device_vif;
-		bool bt_is_active;
+		struct iwl_bt_coex_profile_notif last_bt_notif;
 	);
 	struct ieee80211_link_sta __rcu *fw_id_to_link_sta[IWL_STATION_COUNT_MAX];
 	/* And here fields that survive a fw restart */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
index 91f3a48d0c4b..041375e1a492 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
@@ -636,6 +636,36 @@  s8 iwl_mld_get_emlsr_rssi_thresh(struct iwl_mld *mld,
 #undef RSSI_THRESHOLD
 }
 
+#define IWL_MLD_BT_COEX_DISABLE_EMLSR_RSSI_THRESH	-69
+#define IWL_MLD_BT_COEX_ENABLE_EMLSR_RSSI_THRESH	-63
+#define IWL_MLD_BT_COEX_WIFI_LOSS_THRESH		7
+
+static bool
+iwl_mld_bt_allows_emlsr(struct iwl_mld *mld, struct ieee80211_bss_conf *link,
+			bool check_entry)
+{
+	int bt_penalty;
+	s32 link_rssi = MBM_TO_DBM(link->bss->signal);
+	int rssi_thresh = check_entry ?
+			  IWL_MLD_BT_COEX_ENABLE_EMLSR_RSSI_THRESH :
+			  IWL_MLD_BT_COEX_DISABLE_EMLSR_RSSI_THRESH;
+
+	/* No valid RSSI - force to take low rssi */
+	if (!link_rssi)
+		link_rssi = rssi_thresh - 1;
+
+	if (link_rssi > rssi_thresh)
+		bt_penalty = max(mld->last_bt_notif.wifi_loss_mid_high_rssi[PHY_BAND_24][0],
+				 mld->last_bt_notif.wifi_loss_mid_high_rssi[PHY_BAND_24][1]);
+	else
+		bt_penalty = max(mld->last_bt_notif.wifi_loss_low_rssi[PHY_BAND_24][0],
+				 mld->last_bt_notif.wifi_loss_low_rssi[PHY_BAND_24][1]);
+
+	IWL_DEBUG_EHT(mld, "BT penalty for link-id %0X is %d\n",
+		      link->link_id, bt_penalty);
+	return bt_penalty < IWL_MLD_BT_COEX_WIFI_LOSS_THRESH;
+}
+
 static u32
 iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld,
 				   struct ieee80211_vif *vif,
@@ -650,7 +680,8 @@  iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld,
 	if (WARN_ON_ONCE(!conf))
 		return IWL_MLD_EMLSR_EXIT_INVALID;
 
-	if (link->chandef->chan->band == NL80211_BAND_2GHZ && mld->bt_is_active)
+	if (link->chandef->chan->band == NL80211_BAND_2GHZ &&
+	    !iwl_mld_bt_allows_emlsr(mld, conf, true))
 		ret |= IWL_MLD_EMLSR_EXIT_BT_COEX;
 
 	if (link->signal <
@@ -985,27 +1016,38 @@  static void iwl_mld_emlsr_check_bt_iter(void *_data, u8 *mac,
 					struct ieee80211_vif *vif)
 {
 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+	const struct iwl_bt_coex_profile_notif zero_notif = {};
 	struct iwl_mld *mld = mld_vif->mld;
 	struct ieee80211_bss_conf *link;
 	unsigned int link_id;
+	const struct iwl_bt_coex_profile_notif *notif = &mld->last_bt_notif;
 
-	if (!mld->bt_is_active) {
+	/* zeroed structure means that BT is OFF */
+	if (!memcmp(notif, &zero_notif, sizeof(*notif))) {
 		iwl_mld_retry_emlsr(mld, vif);
 		return;
 	}
 
-	/* BT is turned ON but we are not in EMLSR, nothing to do */
-	if (!iwl_mld_emlsr_active(vif))
-		return;
-
-	/* In EMLSR and BT is turned ON */
-
 	for_each_vif_active_link(vif, link, link_id) {
+		bool emlsr_active, emlsr_allowed;
+
 		if (WARN_ON(!link->chanreq.oper.chan))
 			continue;
 
-		if (link->chanreq.oper.chan->band == NL80211_BAND_2GHZ) {
-			iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_BT_COEX,
+		if (link->chanreq.oper.chan->band != NL80211_BAND_2GHZ)
+			continue;
+
+		emlsr_active = iwl_mld_emlsr_active(vif);
+		emlsr_allowed = iwl_mld_bt_allows_emlsr(mld, link,
+							!emlsr_active);
+		if (emlsr_allowed && !emlsr_active) {
+			iwl_mld_retry_emlsr(mld, vif);
+			return;
+		}
+
+		if (!emlsr_allowed && emlsr_active) {
+			iwl_mld_exit_emlsr(mld, vif,
+					   IWL_MLD_EMLSR_EXIT_BT_COEX,
 					   iwl_mld_get_primary_link(vif));
 			return;
 		}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c
index 766c24db3613..94a037bec1fa 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c
@@ -287,6 +287,7 @@  static void test_iwl_mld_link_pair_allows_emlsr(struct kunit *test)
 	const struct link_pair_case *params = test->param_value;
 	struct iwl_mld *mld = test->priv;
 	struct ieee80211_vif *vif;
+	struct ieee80211_bss_conf *link;
 	/* link A is the primary and link B is the secondary */
 	struct iwl_mld_link_sel_data a = {
 		.chandef = params->chandef_a,
@@ -310,6 +311,11 @@  static void test_iwl_mld_link_pair_allows_emlsr(struct kunit *test)
 
 	wiphy_lock(mld->wiphy);
 
+	link = wiphy_dereference(mld->wiphy, vif->link_conf[a.link_id]);
+	KUNIT_ALLOC_AND_ASSERT(test, link->bss);
+	link = wiphy_dereference(mld->wiphy, vif->link_conf[b.link_id]);
+	KUNIT_ALLOC_AND_ASSERT(test, link->bss);
+
 	/* Simulate channel load */
 	if (params->primary_link_active) {
 		struct iwl_mld_phy *phy =