Skip to content

Commit 2b76ef1

Browse files
egrumbachjmberg-intel
authored andcommitted
iwlwifi: mvm: implement reduced Tx power
This allows to have better wifi TPT when BT is active under good RSSI conditions. Wifi will have better chance to send Acks and Cts even if BT is active. Signed-off-by: Emmanuel Grumbach <[email protected]> Signed-off-by: Johannes Berg <[email protected]>
1 parent 6349437 commit 2b76ef1

File tree

5 files changed

+283
-38
lines changed

5 files changed

+283
-38
lines changed

drivers/net/wireless/iwlwifi/mvm/bt-coex.c

Lines changed: 257 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
*
6262
*****************************************************************************/
6363

64+
#include <net/mac80211.h>
65+
6466
#include "fw-api-bt-coex.h"
6567
#include "iwl-modparams.h"
6668
#include "mvm.h"
@@ -96,6 +98,20 @@ static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
9698

9799
#undef EVENT_PRIO_ANT
98100

101+
/* BT Antenna Coupling Threshold (dB) */
102+
#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35)
103+
#define IWL_BT_LOAD_FORCE_SISO_THRESHOLD (3)
104+
105+
#define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD (-62)
106+
#define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD (-65)
107+
#define BT_REDUCED_TX_POWER_BIT BIT(7)
108+
109+
static inline bool is_loose_coex(void)
110+
{
111+
return iwlwifi_mod_params.ant_coupling >
112+
IWL_BT_ANTENNA_COUPLING_THRESHOLD;
113+
}
114+
99115
int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
100116
{
101117
return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC,
@@ -186,11 +202,6 @@ static const __le32 iwl_concurrent_lookup[BT_COEX_LUT_SIZE] = {
186202
cpu_to_le32(0x00000000),
187203
};
188204

189-
/* BT Antenna Coupling Threshold (dB) */
190-
#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35)
191-
#define IWL_BT_LOAD_FORCE_SISO_THRESHOLD (3)
192-
193-
194205
int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
195206
{
196207
struct iwl_bt_coex_cmd cmd = {
@@ -214,7 +225,7 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
214225
BT_VALID_REDUCED_TX_POWER |
215226
BT_VALID_LUT);
216227

217-
if (iwlwifi_mod_params.ant_coupling > IWL_BT_ANTENNA_COUPLING_THRESHOLD)
228+
if (is_loose_coex())
218229
memcpy(&cmd.decision_lut, iwl_loose_lookup,
219230
sizeof(iwl_tight_lookup));
220231
else
@@ -227,6 +238,8 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
227238
cmd.kill_cts_msk =
228239
cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
229240

241+
memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
242+
230243
/* go to CALIB state in internal BT-Coex state machine */
231244
ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN,
232245
BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
@@ -242,19 +255,101 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
242255
sizeof(cmd), &cmd);
243256
}
244257

245-
struct iwl_bt_notif_iterator_data {
246-
struct iwl_mvm *mvm;
258+
static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
259+
bool reduced_tx_power)
260+
{
261+
enum iwl_bt_kill_msk bt_kill_msk;
262+
struct iwl_bt_coex_cmd cmd = {};
263+
struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
264+
265+
lockdep_assert_held(&mvm->mutex);
266+
267+
if (reduced_tx_power) {
268+
/* Reduced Tx power has precedence on the type of the profile */
269+
bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW;
270+
} else {
271+
/* Low latency BT profile is active: give higher prio to BT */
272+
if (BT_MBOX_MSG(notif, 3, SCO_STATE) ||
273+
BT_MBOX_MSG(notif, 3, A2DP_STATE) ||
274+
BT_MBOX_MSG(notif, 3, SNIFF_STATE))
275+
bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP;
276+
else
277+
bt_kill_msk = BT_KILL_MSK_DEFAULT;
278+
}
279+
280+
IWL_DEBUG_COEX(mvm,
281+
"Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n",
282+
bt_kill_msk,
283+
BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in",
284+
BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in",
285+
BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in");
286+
287+
/* Don't send HCMD if there is no update */
288+
if (bt_kill_msk == mvm->bt_kill_msk)
289+
return 0;
290+
291+
mvm->bt_kill_msk = bt_kill_msk;
292+
cmd.kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
293+
cmd.kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
294+
cmd.valid_bit_msk = cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS);
295+
296+
IWL_DEBUG_COEX(mvm, "bt_kill_msk = %d\n", bt_kill_msk);
297+
return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC,
298+
sizeof(cmd), &cmd);
299+
}
300+
301+
static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
302+
bool enable)
303+
{
304+
struct iwl_bt_coex_cmd cmd = {
305+
.valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER),
306+
.bt_reduced_tx_power = sta_id,
307+
};
308+
struct ieee80211_sta *sta;
309+
struct iwl_mvm_sta *mvmsta;
310+
311+
/* This can happen if the station has been removed right now */
312+
if (sta_id == IWL_MVM_STATION_COUNT)
313+
return 0;
314+
315+
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
316+
lockdep_is_held(&mvm->mutex));
317+
mvmsta = (void *)sta->drv_priv;
318+
319+
/* nothing to do */
320+
if (mvmsta->bt_reduced_txpower == enable)
321+
return 0;
322+
323+
if (enable)
324+
cmd.bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
325+
326+
IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
327+
enable ? "en" : "dis", sta_id);
328+
329+
mvmsta->bt_reduced_txpower = enable;
330+
331+
/* Send ASYNC since this can be sent from an atomic context */
332+
return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_ASYNC,
333+
sizeof(cmd), &cmd);
334+
}
335+
336+
struct iwl_bt_iterator_data {
247337
struct iwl_bt_coex_profile_notif *notif;
338+
enum iwl_bt_kill_msk bt_kill_msk;
339+
struct iwl_mvm *mvm;
340+
u32 num_bss_ifaces;
248341
};
249342

250343
static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
251344
struct ieee80211_vif *vif)
252345
{
253346
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
254-
struct iwl_bt_notif_iterator_data *data = _data;
347+
struct iwl_bt_iterator_data *data = _data;
348+
struct iwl_mvm *mvm = data->mvm;
255349
struct ieee80211_chanctx_conf *chanctx_conf;
256350
enum ieee80211_smps_mode smps_mode;
257351
enum ieee80211_band band;
352+
int ave_rssi;
258353

259354
if (vif->type != NL80211_IFTYPE_STATION)
260355
return;
@@ -284,20 +379,72 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
284379
data->notif->bt_traffic_load, smps_mode);
285380

286381
ieee80211_request_smps(vif, smps_mode);
382+
383+
/* don't reduce the Tx power if in loose scheme */
384+
if (is_loose_coex())
385+
return;
386+
387+
data->num_bss_ifaces++;
388+
389+
/* reduced Txpower only if there are open BT connections, so ...*/
390+
if (!BT_MBOX_MSG(data->notif, 3, OPEN_CON_2)) {
391+
/* ... cancel reduced Tx power ... */
392+
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
393+
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
394+
395+
/* ... use default values for bt_kill_msk ... */
396+
data->bt_kill_msk = BT_KILL_MSK_DEFAULT;
397+
398+
/* ... and there is no need to get reports on RSSI any more. */
399+
ieee80211_disable_rssi_reports(vif);
400+
return;
401+
}
402+
403+
ave_rssi = ieee80211_ave_rssi(vif);
404+
405+
/* if the RSSI isn't valid, fake it is very low */
406+
if (!ave_rssi)
407+
ave_rssi = -100;
408+
if (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) {
409+
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
410+
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
411+
412+
/*
413+
* bt_kill_msk can be BT_KILL_MSK_REDUCED_TXPOW only if all the
414+
* BSS / P2P clients have rssi above threshold.
415+
* We set the bt_kill_msk to BT_KILL_MSK_REDUCED_TXPOW before
416+
* the iteration, if one interface's rssi isn't good enough,
417+
* bt_kill_msk will be set to default values.
418+
*/
419+
} else if (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) {
420+
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
421+
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
422+
423+
/*
424+
* One interface hasn't rssi above threshold, bt_kill_msk must
425+
* be set to default values.
426+
*/
427+
data->bt_kill_msk = BT_KILL_MSK_DEFAULT;
428+
}
429+
430+
/* Begin to monitor the RSSI: it may influence the reduced Tx power */
431+
ieee80211_enable_rssi_reports(vif, BT_DISABLE_REDUCED_TXPOWER_THRESHOLD,
432+
BT_ENABLE_REDUCED_TXPOWER_THRESHOLD);
287433
}
288434

435+
/* upon association, the fw will send in BT Coex notification */
289436
int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
290437
struct iwl_rx_cmd_buffer *rxb,
291438
struct iwl_device_cmd *dev_cmd)
292439
{
293440
struct iwl_rx_packet *pkt = rxb_addr(rxb);
294441
struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
295-
struct iwl_bt_notif_iterator_data data = {
442+
struct iwl_bt_iterator_data data = {
296443
.mvm = mvm,
297444
.notif = notif,
445+
.bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW,
298446
};
299-
struct iwl_bt_coex_cmd cmd = {};
300-
enum iwl_bt_kill_msk bt_kill_msk;
447+
bool reduced_tx_power;
301448

302449
IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
303450
IWL_DEBUG_COEX(mvm, "\tBT %salive\n", notif->bt_status ? "" : "not ");
@@ -314,34 +461,109 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
314461
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
315462
iwl_mvm_bt_notif_iterator, &data);
316463

317-
/* Low latency BT profile is active: give higher prio to BT */
318-
if (BT_MBOX_MSG(notif, 3, SCO_STATE) ||
319-
BT_MBOX_MSG(notif, 3, A2DP_STATE) ||
320-
BT_MBOX_MSG(notif, 3, SNIFF_STATE))
321-
bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP;
464+
/*
465+
* If there are no BSS / P2P client interfaces, reduced Tx Power is
466+
* irrelevant since it is based on the RSSI coming from the beacon.
467+
* Use BT_KILL_MSK_DEFAULT in that case.
468+
*/
469+
if (!data.num_bss_ifaces)
470+
data.bt_kill_msk = BT_KILL_MSK_DEFAULT;
471+
472+
reduced_tx_power = data.num_bss_ifaces &&
473+
data.bt_kill_msk == BT_KILL_MSK_REDUCED_TXPOW;
474+
475+
if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, reduced_tx_power))
476+
IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
477+
478+
/*
479+
* This is an async handler for a notification, returning anything other
480+
* than 0 doesn't make sense even if HCMD failed.
481+
*/
482+
return 0;
483+
}
484+
485+
static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
486+
struct ieee80211_vif *vif)
487+
{
488+
struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
489+
struct iwl_bt_iterator_data *data = _data;
490+
struct iwl_mvm *mvm = data->mvm;
491+
492+
struct ieee80211_sta *sta;
493+
struct iwl_mvm_sta *mvmsta;
494+
495+
if (vif->type != NL80211_IFTYPE_STATION ||
496+
mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
497+
return;
498+
499+
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
500+
lockdep_is_held(&mvm->mutex));
501+
mvmsta = (void *)sta->drv_priv;
502+
503+
/*
504+
* This interface doesn't support reduced Tx power (because of low
505+
* RSSI probably), then set bt_kill_msk to default values.
506+
*/
507+
if (!mvmsta->bt_reduced_txpower)
508+
data->bt_kill_msk = BT_KILL_MSK_DEFAULT;
509+
/* else - possibly leave it to BT_KILL_MSK_REDUCED_TXPOW */
510+
}
511+
512+
void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
513+
enum ieee80211_rssi_event rssi_event)
514+
{
515+
struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
516+
bool reduced_tx_power;
517+
struct iwl_bt_iterator_data data = {
518+
.mvm = mvm,
519+
.bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW,
520+
};
521+
int ret;
522+
523+
mutex_lock(&mvm->mutex);
524+
525+
/* Rssi update while not associated ?! */
526+
if (WARN_ON_ONCE(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT))
527+
goto out_unlock;
528+
529+
/* No open connection - reports should be disabled */
530+
if (!BT_MBOX_MSG(&mvm->last_bt_notif, 3, OPEN_CON_2))
531+
goto out_unlock;
532+
533+
IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
534+
rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
535+
536+
/*
537+
* Check if rssi is good enough for reduced Tx power, but not in loose
538+
* scheme.
539+
*/
540+
if (rssi_event == RSSI_EVENT_LOW || is_loose_coex())
541+
ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
542+
false);
322543
else
323-
bt_kill_msk = BT_KILL_MSK_DEFAULT;
544+
ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true);
324545

325-
/* Don't send HCMD if there is no update */
326-
if (bt_kill_msk == mvm->bt_kill_msk)
327-
return 0;
546+
if (ret)
547+
IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
328548

329-
IWL_DEBUG_COEX(mvm,
330-
"Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n",
331-
bt_kill_msk,
332-
BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in",
333-
BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in",
334-
BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in");
549+
ieee80211_iterate_active_interfaces_atomic(
550+
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
551+
iwl_mvm_bt_rssi_iterator, &data);
335552

336-
mvm->bt_kill_msk = bt_kill_msk;
337-
cmd.kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
338-
cmd.kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
553+
/*
554+
* If there are no BSS / P2P client interfaces, reduced Tx Power is
555+
* irrelevant since it is based on the RSSI coming from the beacon.
556+
* Use BT_KILL_MSK_DEFAULT in that case.
557+
*/
558+
if (!data.num_bss_ifaces)
559+
data.bt_kill_msk = BT_KILL_MSK_DEFAULT;
339560

340-
cmd.valid_bit_msk = cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS);
561+
reduced_tx_power = data.num_bss_ifaces &&
562+
data.bt_kill_msk == BT_KILL_MSK_REDUCED_TXPOW;
341563

342-
if (iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC, sizeof(cmd), &cmd))
343-
IWL_ERR(mvm, "Failed to sent BT Coex CMD\n");
564+
if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, reduced_tx_power))
565+
IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
344566

345-
/* This handler is ASYNC */
346-
return 0;
567+
out_unlock:
568+
mutex_unlock(&mvm->mutex);
347569
}

drivers/net/wireless/iwlwifi/mvm/debugfs.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,18 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
333333
mvmvif->queue_params[i].uapsd);
334334
}
335335

336-
if (vif->type == NL80211_IFTYPE_STATION)
337-
pos += scnprintf(buf+pos, bufsz-pos, "ap_sta_id %d\n",
338-
ap_sta_id);
336+
if (vif->type == NL80211_IFTYPE_STATION &&
337+
ap_sta_id != IWL_MVM_STATION_COUNT) {
338+
struct ieee80211_sta *sta;
339+
struct iwl_mvm_sta *mvm_sta;
340+
341+
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id],
342+
lockdep_is_held(&mvm->mutex));
343+
mvm_sta = (void *)sta->drv_priv;
344+
pos += scnprintf(buf+pos, bufsz-pos,
345+
"ap_sta_id %d - reduced Tx power %d\n",
346+
ap_sta_id, mvm_sta->bt_reduced_txpower);
347+
}
339348

340349
rcu_read_lock();
341350
chanctx_conf = rcu_dereference(vif->chanctx_conf);

0 commit comments

Comments
 (0)