Path: blob/main/sys/contrib/dev/iwlwifi/mvm/coex.c
48285 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2013-2014, 2018-2020, 2022-2025 Intel Corporation3* Copyright (C) 2013-2015 Intel Mobile Communications GmbH4*/5#include <linux/ieee80211.h>6#include <linux/etherdevice.h>7#include <net/mac80211.h>89#include "fw/api/coex.h"10#include "iwl-modparams.h"11#include "mvm.h"12#include "iwl-debug.h"1314/* 20MHz / 40MHz below / 40Mhz above*/15static const __le64 iwl_ci_mask[][3] = {16/* dummy entry for channel 0 */17{cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)},18{19cpu_to_le64(0x0000001FFFULL),20cpu_to_le64(0x0ULL),21cpu_to_le64(0x00007FFFFFULL),22},23{24cpu_to_le64(0x000000FFFFULL),25cpu_to_le64(0x0ULL),26cpu_to_le64(0x0003FFFFFFULL),27},28{29cpu_to_le64(0x000003FFFCULL),30cpu_to_le64(0x0ULL),31cpu_to_le64(0x000FFFFFFCULL),32},33{34cpu_to_le64(0x00001FFFE0ULL),35cpu_to_le64(0x0ULL),36cpu_to_le64(0x007FFFFFE0ULL),37},38{39cpu_to_le64(0x00007FFF80ULL),40cpu_to_le64(0x00007FFFFFULL),41cpu_to_le64(0x01FFFFFF80ULL),42},43{44cpu_to_le64(0x0003FFFC00ULL),45cpu_to_le64(0x0003FFFFFFULL),46cpu_to_le64(0x0FFFFFFC00ULL),47},48{49cpu_to_le64(0x000FFFF000ULL),50cpu_to_le64(0x000FFFFFFCULL),51cpu_to_le64(0x3FFFFFF000ULL),52},53{54cpu_to_le64(0x007FFF8000ULL),55cpu_to_le64(0x007FFFFFE0ULL),56cpu_to_le64(0xFFFFFF8000ULL),57},58{59cpu_to_le64(0x01FFFE0000ULL),60cpu_to_le64(0x01FFFFFF80ULL),61cpu_to_le64(0xFFFFFE0000ULL),62},63{64cpu_to_le64(0x0FFFF00000ULL),65cpu_to_le64(0x0FFFFFFC00ULL),66cpu_to_le64(0x0ULL),67},68{69cpu_to_le64(0x3FFFC00000ULL),70cpu_to_le64(0x3FFFFFF000ULL),71cpu_to_le64(0x0)72},73{74cpu_to_le64(0xFFFE000000ULL),75cpu_to_le64(0xFFFFFF8000ULL),76cpu_to_le64(0x0)77},78{79cpu_to_le64(0xFFF8000000ULL),80cpu_to_le64(0xFFFFFE0000ULL),81cpu_to_le64(0x0)82},83{84cpu_to_le64(0xFE00000000ULL),85cpu_to_le64(0x0ULL),86cpu_to_le64(0x0ULL)87},88};8990static enum iwl_bt_coex_lut_type91iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)92{93struct ieee80211_chanctx_conf *chanctx_conf;94enum iwl_bt_coex_lut_type ret;95u16 phy_ctx_id;96u32 primary_ch_phy_id, secondary_ch_phy_id;9798/*99* Checking that we hold mvm->mutex is a good idea, but the rate100* control can't acquire the mutex since it runs in Tx path.101* So this is racy in that case, but in the worst case, the AMPDU102* size limit will be wrong for a short time which is not a big103* issue.104*/105106rcu_read_lock();107108chanctx_conf = rcu_dereference(vif->bss_conf.chanctx_conf);109110if (!chanctx_conf ||111chanctx_conf->def.chan->band != NL80211_BAND_2GHZ) {112rcu_read_unlock();113return BT_COEX_INVALID_LUT;114}115116ret = BT_COEX_TX_DIS_LUT;117118phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);119primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id);120secondary_ch_phy_id =121le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id);122123if (primary_ch_phy_id == phy_ctx_id)124ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut);125else if (secondary_ch_phy_id == phy_ctx_id)126ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut);127/* else - default = TX TX disallowed */128129rcu_read_unlock();130131return ret;132}133134int iwl_mvm_send_bt_init_conf(struct iwl_mvm *mvm)135{136struct iwl_bt_coex_cmd bt_cmd = {};137u32 mode;138139lockdep_assert_held(&mvm->mutex);140141if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) {142switch (mvm->bt_force_ant_mode) {143case BT_FORCE_ANT_BT:144mode = BT_COEX_BT;145break;146case BT_FORCE_ANT_WIFI:147mode = BT_COEX_WIFI;148break;149default:150WARN_ON(1);151mode = 0;152}153154bt_cmd.mode = cpu_to_le32(mode);155goto send_cmd;156}157158bt_cmd.mode = cpu_to_le32(BT_COEX_NW);159160if (IWL_MVM_BT_COEX_SYNC2SCO)161bt_cmd.enabled_modules |=162cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED);163164if (iwl_mvm_is_mplut_supported(mvm))165bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED);166167bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET);168169send_cmd:170memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));171memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));172173return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, 0, sizeof(bt_cmd), &bt_cmd);174}175176static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,177bool enable)178{179struct iwl_bt_coex_reduced_txp_update_cmd cmd = {};180struct iwl_mvm_sta *mvmsta;181u32 value;182183if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)184return 0;185186mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);187if (!mvmsta)188return 0;189190/* nothing to do */191if (mvmsta->bt_reduced_txpower == enable)192return 0;193194value = mvmsta->deflink.sta_id;195196if (enable)197value |= BT_REDUCED_TX_POWER_BIT;198199IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",200enable ? "en" : "dis", sta_id);201202cmd.reduced_txp = cpu_to_le32(value);203mvmsta->bt_reduced_txpower = enable;204205return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP,206CMD_ASYNC, sizeof(cmd), &cmd);207}208209struct iwl_bt_iterator_data {210struct iwl_bt_coex_prof_old_notif *notif;211struct iwl_mvm *mvm;212struct ieee80211_chanctx_conf *primary;213struct ieee80211_chanctx_conf *secondary;214bool primary_ll;215u8 primary_load;216u8 secondary_load;217};218219static inline220void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,221struct iwl_mvm_vif_link_info *link_info,222bool enable, int rssi)223{224link_info->bf_data.last_bt_coex_event = rssi;225link_info->bf_data.bt_coex_max_thold =226enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0;227link_info->bf_data.bt_coex_min_thold =228enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0;229}230231#define MVM_COEX_TCM_PERIOD (HZ * 10)232233static void iwl_mvm_bt_coex_tcm_based_ci(struct iwl_mvm *mvm,234struct iwl_bt_iterator_data *data)235{236unsigned long now = jiffies;237238if (!time_after(now, mvm->bt_coex_last_tcm_ts + MVM_COEX_TCM_PERIOD))239return;240241mvm->bt_coex_last_tcm_ts = now;242243/* We assume here that we don't have more than 2 vifs on 2.4GHz */244245/* if the primary is low latency, it will stay primary */246if (data->primary_ll)247return;248249if (data->primary_load >= data->secondary_load)250return;251252swap(data->primary, data->secondary);253}254255/*256* This function receives the LB link id and checks if eSR should be257* enabled or disabled (due to BT coex)258*/259bool260iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm,261struct ieee80211_vif *vif,262s32 link_rssi,263bool primary)264{265struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);266bool have_wifi_loss_rate =267iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,268BT_PROFILE_NOTIFICATION, 0) > 4 ||269iwl_fw_lookup_notif_ver(mvm->fw, BT_COEX_GROUP,270PROFILE_NOTIF, 0) >= 1;271u8 wifi_loss_mid_high_rssi;272u8 wifi_loss_low_rssi;273u8 wifi_loss_rate;274275if (iwl_fw_lookup_notif_ver(mvm->fw, BT_COEX_GROUP,276PROFILE_NOTIF, 0) >= 1) {277/* For now, we consider 2.4 GHz band / ANT_A only */278wifi_loss_mid_high_rssi =279mvm->last_bt_wifi_loss.wifi_loss_mid_high_rssi[PHY_BAND_24][0];280wifi_loss_low_rssi =281mvm->last_bt_wifi_loss.wifi_loss_low_rssi[PHY_BAND_24][0];282} else {283wifi_loss_mid_high_rssi = mvm->last_bt_notif.wifi_loss_mid_high_rssi;284wifi_loss_low_rssi = mvm->last_bt_notif.wifi_loss_low_rssi;285}286287if (wifi_loss_low_rssi == BT_OFF)288return true;289290if (primary)291return false;292293/* The feature is not supported */294if (!have_wifi_loss_rate)295return true;296297298/*299* In case we don't know the RSSI - take the lower wifi loss,300* so we will more likely enter eSR, and if RSSI is low -301* we will get an update on this and exit eSR.302*/303if (!link_rssi)304wifi_loss_rate = wifi_loss_mid_high_rssi;305306else if (mvmvif->esr_active)307/* RSSI needs to get really low to disable eSR... */308wifi_loss_rate =309link_rssi <= -IWL_MVM_BT_COEX_DISABLE_ESR_THRESH ?310wifi_loss_low_rssi :311wifi_loss_mid_high_rssi;312else313/* ...And really high before we enable it back */314wifi_loss_rate =315link_rssi <= -IWL_MVM_BT_COEX_ENABLE_ESR_THRESH ?316wifi_loss_low_rssi :317wifi_loss_mid_high_rssi;318319return wifi_loss_rate <= IWL_MVM_BT_COEX_WIFI_LOSS_THRESH;320}321322void iwl_mvm_bt_coex_update_link_esr(struct iwl_mvm *mvm,323struct ieee80211_vif *vif,324int link_id)325{326struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);327struct iwl_mvm_vif_link_info *link = mvmvif->link[link_id];328329if (!ieee80211_vif_is_mld(vif) ||330!iwl_mvm_vif_from_mac80211(vif)->authorized ||331WARN_ON(!link))332return;333334if (!iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif,335(s8)link->beacon_stats.avg_signal,336link_id == iwl_mvm_get_primary_link(vif)))337/* In case we decided to exit eSR - stay with the primary */338iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_COEX,339iwl_mvm_get_primary_link(vif));340}341342static void iwl_mvm_bt_notif_per_link(struct iwl_mvm *mvm,343struct ieee80211_vif *vif,344struct iwl_bt_iterator_data *data,345unsigned int link_id)346{347/* default smps_mode is AUTOMATIC - only used for client modes */348enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC;349struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);350u32 bt_activity_grading, min_ag_for_static_smps;351struct ieee80211_chanctx_conf *chanctx_conf;352struct iwl_mvm_vif_link_info *link_info;353struct ieee80211_bss_conf *link_conf;354int ave_rssi;355356lockdep_assert_held(&mvm->mutex);357358link_info = mvmvif->link[link_id];359if (!link_info)360return;361362link_conf = rcu_dereference(vif->link_conf[link_id]);363/* This can happen due to races: if we receive the notification364* and have the mutex held, while mac80211 is stuck on our mutex365* in the middle of removing the link.366*/367if (!link_conf)368return;369370chanctx_conf = rcu_dereference(link_conf->chanctx_conf);371372/* If channel context is invalid or not on 2.4GHz .. */373if ((!chanctx_conf ||374chanctx_conf->def.chan->band != NL80211_BAND_2GHZ)) {375if (vif->type == NL80211_IFTYPE_STATION) {376/* ... relax constraints and disable rssi events */377iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,378smps_mode, link_id);379iwl_mvm_bt_coex_reduced_txp(mvm, link_info->ap_sta_id,380false);381iwl_mvm_bt_coex_enable_rssi_event(mvm, link_info, false,3820);383}384return;385}386387iwl_mvm_bt_coex_update_link_esr(mvm, vif, link_id);388389if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2))390min_ag_for_static_smps = BT_VERY_HIGH_TRAFFIC;391else392min_ag_for_static_smps = BT_HIGH_TRAFFIC;393394bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading);395if (bt_activity_grading >= min_ag_for_static_smps)396smps_mode = IEEE80211_SMPS_STATIC;397else if (bt_activity_grading >= BT_LOW_TRAFFIC)398smps_mode = IEEE80211_SMPS_DYNAMIC;399400/* relax SMPS constraints for next association */401if (!vif->cfg.assoc)402smps_mode = IEEE80211_SMPS_AUTOMATIC;403404if (link_info->phy_ctxt &&405(mvm->last_bt_notif.rrc_status & BIT(link_info->phy_ctxt->id)))406smps_mode = IEEE80211_SMPS_AUTOMATIC;407408IWL_DEBUG_COEX(data->mvm,409"mac %d link %d: bt_activity_grading %d smps_req %d\n",410mvmvif->id, link_info->fw_link_id,411bt_activity_grading, smps_mode);412413if (vif->type == NL80211_IFTYPE_STATION)414iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,415smps_mode, link_id);416417/* low latency is always primary */418if (iwl_mvm_vif_low_latency(mvmvif)) {419data->primary_ll = true;420421data->secondary = data->primary;422data->primary = chanctx_conf;423}424425if (vif->type == NL80211_IFTYPE_AP) {426if (!mvmvif->ap_ibss_active)427return;428429if (chanctx_conf == data->primary)430return;431432if (!data->primary_ll) {433/*434* downgrade the current primary no matter what its435* type is.436*/437data->secondary = data->primary;438data->primary = chanctx_conf;439} else {440/* there is low latency vif - we will be secondary */441data->secondary = chanctx_conf;442}443444/* FIXME: TCM load per interface? or need something per link? */445if (data->primary == chanctx_conf)446data->primary_load = mvm->tcm.result.load[mvmvif->id];447else if (data->secondary == chanctx_conf)448data->secondary_load = mvm->tcm.result.load[mvmvif->id];449return;450}451452/*453* STA / P2P Client, try to be primary if first vif. If we are in low454* latency mode, we are already in primary and just don't do much455*/456if (!data->primary || data->primary == chanctx_conf)457data->primary = chanctx_conf;458else if (!data->secondary)459/* if secondary is not NULL, it might be a GO */460data->secondary = chanctx_conf;461462/* FIXME: TCM load per interface? or need something per link? */463if (data->primary == chanctx_conf)464data->primary_load = mvm->tcm.result.load[mvmvif->id];465else if (data->secondary == chanctx_conf)466data->secondary_load = mvm->tcm.result.load[mvmvif->id];467/*468* don't reduce the Tx power if one of these is true:469* we are in LOOSE470* BT is inactive471* we are not associated472*/473if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||474le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF ||475!vif->cfg.assoc) {476iwl_mvm_bt_coex_reduced_txp(mvm, link_info->ap_sta_id, false);477iwl_mvm_bt_coex_enable_rssi_event(mvm, link_info, false, 0);478return;479}480481/* try to get the avg rssi from fw */482ave_rssi = link_info->bf_data.ave_beacon_signal;483484/* if the RSSI isn't valid, fake it is very low */485if (!ave_rssi)486ave_rssi = -100;487if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {488if (iwl_mvm_bt_coex_reduced_txp(mvm, link_info->ap_sta_id,489true))490IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");491} else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {492if (iwl_mvm_bt_coex_reduced_txp(mvm, link_info->ap_sta_id,493false))494IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");495}496497/* Begin to monitor the RSSI: it may influence the reduced Tx power */498iwl_mvm_bt_coex_enable_rssi_event(mvm, link_info, true, ave_rssi);499}500501/* must be called under rcu_read_lock */502static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,503struct ieee80211_vif *vif)504{505struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);506struct iwl_bt_iterator_data *data = _data;507struct iwl_mvm *mvm = data->mvm;508unsigned int link_id;509510lockdep_assert_held(&mvm->mutex);511512switch (vif->type) {513case NL80211_IFTYPE_STATION:514break;515case NL80211_IFTYPE_AP:516if (!mvmvif->ap_ibss_active)517return;518break;519default:520return;521}522523for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++)524iwl_mvm_bt_notif_per_link(mvm, vif, data, link_id);525}526527/* must be called under rcu_read_lock */528static void iwl_mvm_bt_coex_notif_iterator(void *_data, u8 *mac,529struct ieee80211_vif *vif)530{531struct iwl_mvm *mvm = _data;532struct ieee80211_bss_conf *link_conf;533unsigned int link_id;534535lockdep_assert_held(&mvm->mutex);536537if (vif->type != NL80211_IFTYPE_STATION)538return;539540for_each_vif_active_link(vif, link_conf, link_id) {541struct ieee80211_chanctx_conf *chanctx_conf =542rcu_dereference_check(link_conf->chanctx_conf,543lockdep_is_held(&mvm->mutex));544545if ((!chanctx_conf ||546chanctx_conf->def.chan->band != NL80211_BAND_2GHZ))547continue;548549iwl_mvm_bt_coex_update_link_esr(mvm, vif, link_id);550}551}552553static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)554{555struct iwl_bt_iterator_data data = {556.mvm = mvm,557.notif = &mvm->last_bt_notif,558};559struct iwl_bt_coex_ci_cmd cmd = {};560u8 ci_bw_idx;561562/* Ignore updates if we are in force mode */563if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))564return;565566rcu_read_lock();567ieee80211_iterate_active_interfaces_atomic(568mvm->hw, IEEE80211_IFACE_ITER_NORMAL,569iwl_mvm_bt_notif_iterator, &data);570571if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {572rcu_read_unlock();573return;574}575576iwl_mvm_bt_coex_tcm_based_ci(mvm, &data);577578if (data.primary) {579struct ieee80211_chanctx_conf *chan = data.primary;580if (WARN_ON(!chan->def.chan)) {581rcu_read_unlock();582return;583}584585if (chan->def.width < NL80211_CHAN_WIDTH_40) {586ci_bw_idx = 0;587} else {588if (chan->def.center_freq1 >589chan->def.chan->center_freq)590ci_bw_idx = 2;591else592ci_bw_idx = 1;593}594595cmd.bt_primary_ci =596iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];597cmd.primary_ch_phy_id =598cpu_to_le32(*((u16 *)data.primary->drv_priv));599}600601if (data.secondary) {602struct ieee80211_chanctx_conf *chan = data.secondary;603if (WARN_ON(!data.secondary->def.chan)) {604rcu_read_unlock();605return;606}607608if (chan->def.width < NL80211_CHAN_WIDTH_40) {609ci_bw_idx = 0;610} else {611if (chan->def.center_freq1 >612chan->def.chan->center_freq)613ci_bw_idx = 2;614else615ci_bw_idx = 1;616}617618cmd.bt_secondary_ci =619iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];620cmd.secondary_ch_phy_id =621cpu_to_le32(*((u16 *)data.secondary->drv_priv));622}623624rcu_read_unlock();625626/* Don't spam the fw with the same command over and over */627if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) {628if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0,629sizeof(cmd), &cmd))630IWL_ERR(mvm, "Failed to send BT_CI cmd\n");631memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd));632}633}634635void iwl_mvm_rx_bt_coex_old_notif(struct iwl_mvm *mvm,636struct iwl_rx_cmd_buffer *rxb)637{638struct iwl_rx_packet *pkt = rxb_addr(rxb);639struct iwl_bt_coex_prof_old_notif *notif = (void *)pkt->data;640641IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");642IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);643IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",644le32_to_cpu(notif->primary_ch_lut));645IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n",646le32_to_cpu(notif->secondary_ch_lut));647IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",648le32_to_cpu(notif->bt_activity_grading));649650/* remember this notification for future use: rssi fluctuations */651memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));652653iwl_mvm_bt_coex_notif_handle(mvm);654}655656void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,657struct iwl_rx_cmd_buffer *rxb)658{659const struct iwl_rx_packet *pkt = rxb_addr(rxb);660const struct iwl_bt_coex_profile_notif *notif = (const void *)pkt->data;661662lockdep_assert_held(&mvm->mutex);663664mvm->last_bt_wifi_loss = *notif;665666ieee80211_iterate_active_interfaces(mvm->hw,667IEEE80211_IFACE_ITER_NORMAL,668iwl_mvm_bt_coex_notif_iterator,669mvm);670}671672void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,673enum ieee80211_rssi_event_data rssi_event)674{675struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);676int ret;677678lockdep_assert_held(&mvm->mutex);679680/* Ignore updates if we are in force mode */681if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))682return;683684/*685* Rssi update while not associated - can happen since the statistics686* are handled asynchronously687*/688if (mvmvif->deflink.ap_sta_id == IWL_INVALID_STA)689return;690691/* No BT - reports should be disabled */692if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF)693return;694695IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,696rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");697698/*699* Check if rssi is good enough for reduced Tx power, but not in loose700* scheme.701*/702if (rssi_event == RSSI_EVENT_LOW ||703iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT)704ret = iwl_mvm_bt_coex_reduced_txp(mvm,705mvmvif->deflink.ap_sta_id,706false);707else708ret = iwl_mvm_bt_coex_reduced_txp(mvm,709mvmvif->deflink.ap_sta_id,710true);711712if (ret)713IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");714}715716#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000)717#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200)718719u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,720struct ieee80211_sta *sta)721{722struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);723struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);724struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->deflink.phy_ctxt;725enum iwl_bt_coex_lut_type lut_type;726727if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id))728return LINK_QUAL_AGG_TIME_LIMIT_DEF;729730if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <731BT_HIGH_TRAFFIC)732return LINK_QUAL_AGG_TIME_LIMIT_DEF;733734lut_type = iwl_get_coex_type(mvm, mvmsta->vif);735736if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT)737return LINK_QUAL_AGG_TIME_LIMIT_DEF;738739/* tight coex, high bt traffic, reduce AGG time limit */740return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT;741}742743bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,744struct ieee80211_sta *sta)745{746struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);747struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);748struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->deflink.phy_ctxt;749enum iwl_bt_coex_lut_type lut_type;750751if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id))752return true;753754if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <755BT_HIGH_TRAFFIC)756return true;757758/*759* In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas760* since BT is already killed.761* In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while762* we Tx.763* When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO.764*/765lut_type = iwl_get_coex_type(mvm, mvmsta->vif);766return lut_type != BT_COEX_LOOSE_LUT;767}768769bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant)770{771if (ant & mvm->cfg->non_shared_ant)772return true;773774return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <775BT_HIGH_TRAFFIC;776}777778bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm)779{780return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC;781}782783bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,784enum nl80211_band band)785{786u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);787788if (band != NL80211_BAND_2GHZ)789return false;790791return bt_activity >= BT_LOW_TRAFFIC;792}793794u8 iwl_mvm_bt_coex_get_single_ant_msk(struct iwl_mvm *mvm, u8 enabled_ants)795{796if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2) &&797(mvm->cfg->non_shared_ant & enabled_ants))798return mvm->cfg->non_shared_ant;799800return first_antenna(enabled_ants);801}802803u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,804struct ieee80211_tx_info *info, u8 ac)805{806__le16 fc = hdr->frame_control;807bool mplut_enabled = iwl_mvm_is_mplut_supported(mvm);808809if (info->band != NL80211_BAND_2GHZ)810return 0;811812if (unlikely(mvm->bt_tx_prio))813return mvm->bt_tx_prio - 1;814815if (likely(ieee80211_is_data(fc))) {816if (likely(ieee80211_is_data_qos(fc))) {817switch (ac) {818case IEEE80211_AC_BE:819return mplut_enabled ? 1 : 0;820case IEEE80211_AC_VI:821return mplut_enabled ? 2 : 3;822case IEEE80211_AC_VO:823return 3;824default:825return 0;826}827} else if (is_multicast_ether_addr(hdr->addr1)) {828return 3;829} else830return 0;831} else if (ieee80211_is_mgmt(fc)) {832return ieee80211_is_disassoc(fc) ? 0 : 3;833} else if (ieee80211_is_ctl(fc)) {834/* ignore cfend and cfendack frames as we never send those */835return 3;836}837838return 0;839}840841void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)842{843iwl_mvm_bt_coex_notif_handle(mvm);844}845846847