aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2013-01-24 03:35:13 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-04-03 16:49:12 -0400
commit2b76ef13086ff0170abfc7f7ebfd104abfdee463 (patch)
treec9770839ddd7de19baacf6b936c4f97a83ca81b3 /drivers/net/wireless/iwlwifi/mvm/bt-coex.c
parent6349437494c128b0ce9db74096019a5ad43ee02d (diff)
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 <emmanuel.grumbach@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/bt-coex.c')
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/bt-coex.c292
1 files changed, 257 insertions, 35 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
index 1ad68f9d6adb..91788fe011ad 100644
--- a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
+++ b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
@@ -61,6 +61,8 @@
61 * 61 *
62 *****************************************************************************/ 62 *****************************************************************************/
63 63
64#include <net/mac80211.h>
65
64#include "fw-api-bt-coex.h" 66#include "fw-api-bt-coex.h"
65#include "iwl-modparams.h" 67#include "iwl-modparams.h"
66#include "mvm.h" 68#include "mvm.h"
@@ -96,6 +98,20 @@ static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
96 98
97#undef EVENT_PRIO_ANT 99#undef EVENT_PRIO_ANT
98 100
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
109static inline bool is_loose_coex(void)
110{
111 return iwlwifi_mod_params.ant_coupling >
112 IWL_BT_ANTENNA_COUPLING_THRESHOLD;
113}
114
99int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) 115int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
100{ 116{
101 return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC, 117 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] = {
186 cpu_to_le32(0x00000000), 202 cpu_to_le32(0x00000000),
187}; 203};
188 204
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
194int iwl_send_bt_init_conf(struct iwl_mvm *mvm) 205int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
195{ 206{
196 struct iwl_bt_coex_cmd cmd = { 207 struct iwl_bt_coex_cmd cmd = {
@@ -214,7 +225,7 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
214 BT_VALID_REDUCED_TX_POWER | 225 BT_VALID_REDUCED_TX_POWER |
215 BT_VALID_LUT); 226 BT_VALID_LUT);
216 227
217 if (iwlwifi_mod_params.ant_coupling > IWL_BT_ANTENNA_COUPLING_THRESHOLD) 228 if (is_loose_coex())
218 memcpy(&cmd.decision_lut, iwl_loose_lookup, 229 memcpy(&cmd.decision_lut, iwl_loose_lookup,
219 sizeof(iwl_tight_lookup)); 230 sizeof(iwl_tight_lookup));
220 else 231 else
@@ -227,6 +238,8 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
227 cmd.kill_cts_msk = 238 cmd.kill_cts_msk =
228 cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]); 239 cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
229 240
241 memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
242
230 /* go to CALIB state in internal BT-Coex state machine */ 243 /* go to CALIB state in internal BT-Coex state machine */
231 ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN, 244 ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN,
232 BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); 245 BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
@@ -242,19 +255,101 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
242 sizeof(cmd), &cmd); 255 sizeof(cmd), &cmd);
243} 256}
244 257
245struct iwl_bt_notif_iterator_data { 258static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
246 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
301static 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
336struct iwl_bt_iterator_data {
247 struct iwl_bt_coex_profile_notif *notif; 337 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;
248}; 341};
249 342
250static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, 343static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
251 struct ieee80211_vif *vif) 344 struct ieee80211_vif *vif)
252{ 345{
253 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 346 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;
255 struct ieee80211_chanctx_conf *chanctx_conf; 349 struct ieee80211_chanctx_conf *chanctx_conf;
256 enum ieee80211_smps_mode smps_mode; 350 enum ieee80211_smps_mode smps_mode;
257 enum ieee80211_band band; 351 enum ieee80211_band band;
352 int ave_rssi;
258 353
259 if (vif->type != NL80211_IFTYPE_STATION) 354 if (vif->type != NL80211_IFTYPE_STATION)
260 return; 355 return;
@@ -284,20 +379,72 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
284 data->notif->bt_traffic_load, smps_mode); 379 data->notif->bt_traffic_load, smps_mode);
285 380
286 ieee80211_request_smps(vif, smps_mode); 381 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);
287} 433}
288 434
435/* upon association, the fw will send in BT Coex notification */
289int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, 436int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
290 struct iwl_rx_cmd_buffer *rxb, 437 struct iwl_rx_cmd_buffer *rxb,
291 struct iwl_device_cmd *dev_cmd) 438 struct iwl_device_cmd *dev_cmd)
292{ 439{
293 struct iwl_rx_packet *pkt = rxb_addr(rxb); 440 struct iwl_rx_packet *pkt = rxb_addr(rxb);
294 struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; 441 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 = {
296 .mvm = mvm, 443 .mvm = mvm,
297 .notif = notif, 444 .notif = notif,
445 .bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW,
298 }; 446 };
299 struct iwl_bt_coex_cmd cmd = {}; 447 bool reduced_tx_power;
300 enum iwl_bt_kill_msk bt_kill_msk;
301 448
302 IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); 449 IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
303 IWL_DEBUG_COEX(mvm, "\tBT %salive\n", notif->bt_status ? "" : "not "); 450 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,
314 mvm->hw, IEEE80211_IFACE_ITER_NORMAL, 461 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
315 iwl_mvm_bt_notif_iterator, &data); 462 iwl_mvm_bt_notif_iterator, &data);
316 463
317 /* Low latency BT profile is active: give higher prio to BT */ 464 /*
318 if (BT_MBOX_MSG(notif, 3, SCO_STATE) || 465 * If there are no BSS / P2P client interfaces, reduced Tx Power is
319 BT_MBOX_MSG(notif, 3, A2DP_STATE) || 466 * irrelevant since it is based on the RSSI coming from the beacon.
320 BT_MBOX_MSG(notif, 3, SNIFF_STATE)) 467 * Use BT_KILL_MSK_DEFAULT in that case.
321 bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP; 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
485static 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
512void 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);
322 else 543 else
323 bt_kill_msk = BT_KILL_MSK_DEFAULT; 544 ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true);
324 545
325 /* Don't send HCMD if there is no update */ 546 if (ret)
326 if (bt_kill_msk == mvm->bt_kill_msk) 547 IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
327 return 0;
328 548
329 IWL_DEBUG_COEX(mvm, 549 ieee80211_iterate_active_interfaces_atomic(
330 "Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n", 550 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
331 bt_kill_msk, 551 iwl_mvm_bt_rssi_iterator, &data);
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");
335 552
336 mvm->bt_kill_msk = bt_kill_msk; 553 /*
337 cmd.kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]); 554 * If there are no BSS / P2P client interfaces, reduced Tx Power is
338 cmd.kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]); 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;
339 560
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;
341 563
342 if (iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC, sizeof(cmd), &cmd)) 564 if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, reduced_tx_power))
343 IWL_ERR(mvm, "Failed to sent BT Coex CMD\n"); 565 IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
344 566
345 /* This handler is ASYNC */ 567 out_unlock:
346 return 0; 568 mutex_unlock(&mvm->mutex);
347} 569}