wifi: iwlwifi: mld: allow EMLSR with 2.4 GHz when BT is ON

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>
Link: https://patch.msgid.link/20250430151952.d30212ec3901.I48e3d5bd6b0b8583f98057c38d2ee30fff5abd8a@changeid
This commit is contained in:
Somashekhar Puttagangaiah 2025-04-30 15:23:15 +03:00 committed by Miri Korenblit
parent 822c7bd5ef
commit 37808a3788
4 changed files with 69 additions and 18 deletions

View file

@ -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, void iwl_mld_handle_bt_coex_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt) 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 = {}; const struct iwl_bt_coex_profile_notif zero_notif = {};
/* zeroed structure means that BT is OFF */ /* zeroed structure means that BT is OFF */
bool bt_is_active = memcmp(notif, &zero_notif, sizeof(*notif)); bool bt_is_active = memcmp(notif, &zero_notif, sizeof(*notif));
if (bt_is_active == mld->bt_is_active) mld->last_bt_notif = *notif;
return;
IWL_DEBUG_INFO(mld, "BT was turned %s\n", bt_is_active ? "ON" : "OFF"); 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); iwl_mld_emlsr_check_bt(mld);
} }

View file

@ -126,7 +126,6 @@
* cleanup using iwl_mld_free_internal_sta * cleanup using iwl_mld_free_internal_sta
* @netdetect: indicates the FW is in suspend mode with netdetect configured * @netdetect: indicates the FW is in suspend mode with netdetect configured
* @p2p_device_vif: points to the p2p device vif if exists * @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 * @dev: pointer to device struct. For printing purposes
* @trans: pointer to the transport layer * @trans: pointer to the transport layer
* @cfg: pointer to the device configuration * @cfg: pointer to the device configuration
@ -184,6 +183,7 @@
* @ptp_data: data of the PTP clock * @ptp_data: data of the PTP clock
* @time_sync: time sync data. * @time_sync: time sync data.
* @ftm_initiator: FTM initiator data * @ftm_initiator: FTM initiator data
* @last_bt_notif: last received BT Coex notif
*/ */
struct iwl_mld { struct iwl_mld {
/* Add here fields that need clean up on restart */ /* Add here fields that need clean up on restart */
@ -207,7 +207,7 @@ struct iwl_mld {
bool netdetect; bool netdetect;
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
struct ieee80211_vif *p2p_device_vif; 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]; struct ieee80211_link_sta __rcu *fw_id_to_link_sta[IWL_STATION_COUNT_MAX];
/* And here fields that survive a fw restart */ /* And here fields that survive a fw restart */

View file

@ -636,6 +636,40 @@ s8 iwl_mld_get_emlsr_rssi_thresh(struct iwl_mld *mld,
#undef RSSI_THRESHOLD #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, rssi_thresh;
s32 link_rssi;
if (WARN_ON_ONCE(!link->bss))
return false;
link_rssi = MBM_TO_DBM(link->bss->signal);
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 static u32
iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld, iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
@ -650,7 +684,8 @@ iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld,
if (WARN_ON_ONCE(!conf)) if (WARN_ON_ONCE(!conf))
return IWL_MLD_EMLSR_EXIT_INVALID; 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; ret |= IWL_MLD_EMLSR_EXIT_BT_COEX;
if (link->signal < if (link->signal <
@ -985,27 +1020,41 @@ static void iwl_mld_emlsr_check_bt_iter(void *_data, u8 *mac,
struct ieee80211_vif *vif) struct ieee80211_vif *vif)
{ {
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(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 iwl_mld *mld = mld_vif->mld;
struct ieee80211_bss_conf *link; struct ieee80211_bss_conf *link;
unsigned int link_id; unsigned int link_id;
const struct iwl_bt_coex_profile_notif *notif = &mld->last_bt_notif;
if (!mld->bt_is_active) { if (!iwl_mld_vif_has_emlsr_cap(vif))
return;
/* zeroed structure means that BT is OFF */
if (!memcmp(notif, &zero_notif, sizeof(*notif))) {
iwl_mld_retry_emlsr(mld, vif); iwl_mld_retry_emlsr(mld, vif);
return; 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) { for_each_vif_active_link(vif, link, link_id) {
bool emlsr_active, emlsr_allowed;
if (WARN_ON(!link->chanreq.oper.chan)) if (WARN_ON(!link->chanreq.oper.chan))
continue; continue;
if (link->chanreq.oper.chan->band == NL80211_BAND_2GHZ) { if (link->chanreq.oper.chan->band != NL80211_BAND_2GHZ)
iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_BT_COEX, 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)); iwl_mld_get_primary_link(vif));
return; return;
} }

View file

@ -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; const struct link_pair_case *params = test->param_value;
struct iwl_mld *mld = test->priv; struct iwl_mld *mld = test->priv;
struct ieee80211_vif *vif; struct ieee80211_vif *vif;
struct ieee80211_bss_conf *link;
/* link A is the primary and link B is the secondary */ /* link A is the primary and link B is the secondary */
struct iwl_mld_link_sel_data a = { struct iwl_mld_link_sel_data a = {
.chandef = params->chandef_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); 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 */ /* Simulate channel load */
if (params->primary_link_active) { if (params->primary_link_active) {
struct iwl_mld_phy *phy = struct iwl_mld_phy *phy =