61
61
*
62
62
*****************************************************************************/
63
63
64
+ #include <net/mac80211.h>
65
+
64
66
#include "fw-api-bt-coex.h"
65
67
#include "iwl-modparams.h"
66
68
#include "mvm.h"
@@ -96,6 +98,20 @@ static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
96
98
97
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
+
109
+ static inline bool is_loose_coex (void )
110
+ {
111
+ return iwlwifi_mod_params .ant_coupling >
112
+ IWL_BT_ANTENNA_COUPLING_THRESHOLD ;
113
+ }
114
+
99
115
int iwl_send_bt_prio_tbl (struct iwl_mvm * mvm )
100
116
{
101
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
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
-
194
205
int iwl_send_bt_init_conf (struct iwl_mvm * mvm )
195
206
{
196
207
struct iwl_bt_coex_cmd cmd = {
@@ -214,7 +225,7 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
214
225
BT_VALID_REDUCED_TX_POWER |
215
226
BT_VALID_LUT );
216
227
217
- if (iwlwifi_mod_params . ant_coupling > IWL_BT_ANTENNA_COUPLING_THRESHOLD )
228
+ if (is_loose_coex () )
218
229
memcpy (& cmd .decision_lut , iwl_loose_lookup ,
219
230
sizeof (iwl_tight_lookup ));
220
231
else
@@ -227,6 +238,8 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
227
238
cmd .kill_cts_msk =
228
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
243
/* go to CALIB state in internal BT-Coex state machine */
231
244
ret = iwl_send_bt_env (mvm , BT_COEX_ENV_OPEN ,
232
245
BT_COEX_PRIO_TBL_EVT_INIT_CALIB2 );
@@ -242,19 +255,101 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
242
255
sizeof (cmd ), & cmd );
243
256
}
244
257
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 {
247
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
250
343
static void iwl_mvm_bt_notif_iterator (void * _data , u8 * mac ,
251
344
struct ieee80211_vif * vif )
252
345
{
253
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
349
struct ieee80211_chanctx_conf * chanctx_conf ;
256
350
enum ieee80211_smps_mode smps_mode ;
257
351
enum ieee80211_band band ;
352
+ int ave_rssi ;
258
353
259
354
if (vif -> type != NL80211_IFTYPE_STATION )
260
355
return ;
@@ -284,20 +379,72 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
284
379
data -> notif -> bt_traffic_load , smps_mode );
285
380
286
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 */
289
436
int iwl_mvm_rx_bt_coex_notif (struct iwl_mvm * mvm ,
290
437
struct iwl_rx_cmd_buffer * rxb ,
291
438
struct iwl_device_cmd * dev_cmd )
292
439
{
293
440
struct iwl_rx_packet * pkt = rxb_addr (rxb );
294
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
443
.mvm = mvm ,
297
444
.notif = notif ,
445
+ .bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW ,
298
446
};
299
- struct iwl_bt_coex_cmd cmd = {};
300
- enum iwl_bt_kill_msk bt_kill_msk ;
447
+ bool reduced_tx_power ;
301
448
302
449
IWL_DEBUG_COEX (mvm , "BT Coex Notification received\n" );
303
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
461
mvm -> hw , IEEE80211_IFACE_ITER_NORMAL ,
315
462
iwl_mvm_bt_notif_iterator , & data );
316
463
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);
322
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 */
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" );
328
548
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 );
335
552
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 ;
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 ))
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" );
344
566
345
- /* This handler is ASYNC */
346
- return 0 ;
567
+ out_unlock :
568
+ mutex_unlock ( & mvm -> mutex ) ;
347
569
}
0 commit comments