1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
3 * Copyright (C) 2013-2014, 2018-2020, 2022-2024 Intel Corporation
4 * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
6 #include <linux/ieee80211.h>
7 #include <linux/etherdevice.h>
8 #include <net/mac80211.h>
10 #include "fw/api/coex.h"
11 #include "iwl-modparams.h"
13 #include "iwl-debug.h"
15 /* 20MHz / 40MHz below / 40Mhz above*/
16 static const __le64 iwl_ci_mask
[][3] = {
17 /* dummy entry for channel 0 */
18 {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)},
20 cpu_to_le64(0x0000001FFFULL
),
22 cpu_to_le64(0x00007FFFFFULL
),
25 cpu_to_le64(0x000000FFFFULL
),
27 cpu_to_le64(0x0003FFFFFFULL
),
30 cpu_to_le64(0x000003FFFCULL
),
32 cpu_to_le64(0x000FFFFFFCULL
),
35 cpu_to_le64(0x00001FFFE0ULL
),
37 cpu_to_le64(0x007FFFFFE0ULL
),
40 cpu_to_le64(0x00007FFF80ULL
),
41 cpu_to_le64(0x00007FFFFFULL
),
42 cpu_to_le64(0x01FFFFFF80ULL
),
45 cpu_to_le64(0x0003FFFC00ULL
),
46 cpu_to_le64(0x0003FFFFFFULL
),
47 cpu_to_le64(0x0FFFFFFC00ULL
),
50 cpu_to_le64(0x000FFFF000ULL
),
51 cpu_to_le64(0x000FFFFFFCULL
),
52 cpu_to_le64(0x3FFFFFF000ULL
),
55 cpu_to_le64(0x007FFF8000ULL
),
56 cpu_to_le64(0x007FFFFFE0ULL
),
57 cpu_to_le64(0xFFFFFF8000ULL
),
60 cpu_to_le64(0x01FFFE0000ULL
),
61 cpu_to_le64(0x01FFFFFF80ULL
),
62 cpu_to_le64(0xFFFFFE0000ULL
),
65 cpu_to_le64(0x0FFFF00000ULL
),
66 cpu_to_le64(0x0FFFFFFC00ULL
),
70 cpu_to_le64(0x3FFFC00000ULL
),
71 cpu_to_le64(0x3FFFFFF000ULL
),
75 cpu_to_le64(0xFFFE000000ULL
),
76 cpu_to_le64(0xFFFFFF8000ULL
),
80 cpu_to_le64(0xFFF8000000ULL
),
81 cpu_to_le64(0xFFFFFE0000ULL
),
85 cpu_to_le64(0xFE00000000ULL
),
91 static enum iwl_bt_coex_lut_type
92 iwl_get_coex_type(struct iwl_mvm
*mvm
, const struct ieee80211_vif
*vif
)
94 struct ieee80211_chanctx_conf
*chanctx_conf
;
95 enum iwl_bt_coex_lut_type ret
;
97 u32 primary_ch_phy_id
, secondary_ch_phy_id
;
100 * Checking that we hold mvm->mutex is a good idea, but the rate
101 * control can't acquire the mutex since it runs in Tx path.
102 * So this is racy in that case, but in the worst case, the AMPDU
103 * size limit will be wrong for a short time which is not a big
109 chanctx_conf
= rcu_dereference(vif
->bss_conf
.chanctx_conf
);
112 chanctx_conf
->def
.chan
->band
!= NL80211_BAND_2GHZ
) {
114 return BT_COEX_INVALID_LUT
;
117 ret
= BT_COEX_TX_DIS_LUT
;
119 phy_ctx_id
= *((u16
*)chanctx_conf
->drv_priv
);
120 primary_ch_phy_id
= le32_to_cpu(mvm
->last_bt_ci_cmd
.primary_ch_phy_id
);
121 secondary_ch_phy_id
=
122 le32_to_cpu(mvm
->last_bt_ci_cmd
.secondary_ch_phy_id
);
124 if (primary_ch_phy_id
== phy_ctx_id
)
125 ret
= le32_to_cpu(mvm
->last_bt_notif
.primary_ch_lut
);
126 else if (secondary_ch_phy_id
== phy_ctx_id
)
127 ret
= le32_to_cpu(mvm
->last_bt_notif
.secondary_ch_lut
);
128 /* else - default = TX TX disallowed */
135 int iwl_mvm_send_bt_init_conf(struct iwl_mvm
*mvm
)
137 struct iwl_bt_coex_cmd bt_cmd
= {};
140 lockdep_assert_held(&mvm
->mutex
);
142 if (unlikely(mvm
->bt_force_ant_mode
!= BT_FORCE_ANT_DIS
)) {
143 switch (mvm
->bt_force_ant_mode
) {
144 case BT_FORCE_ANT_BT
:
147 case BT_FORCE_ANT_WIFI
:
155 bt_cmd
.mode
= cpu_to_le32(mode
);
159 bt_cmd
.mode
= cpu_to_le32(BT_COEX_NW
);
161 if (IWL_MVM_BT_COEX_SYNC2SCO
)
162 bt_cmd
.enabled_modules
|=
163 cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED
);
165 if (iwl_mvm_is_mplut_supported(mvm
))
166 bt_cmd
.enabled_modules
|= cpu_to_le32(BT_COEX_MPLUT_ENABLED
);
168 bt_cmd
.enabled_modules
|= cpu_to_le32(BT_COEX_HIGH_BAND_RET
);
171 memset(&mvm
->last_bt_notif
, 0, sizeof(mvm
->last_bt_notif
));
172 memset(&mvm
->last_bt_ci_cmd
, 0, sizeof(mvm
->last_bt_ci_cmd
));
174 return iwl_mvm_send_cmd_pdu(mvm
, BT_CONFIG
, 0, sizeof(bt_cmd
), &bt_cmd
);
177 static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm
*mvm
, u8 sta_id
,
180 struct iwl_bt_coex_reduced_txp_update_cmd cmd
= {};
181 struct iwl_mvm_sta
*mvmsta
;
184 if (mvm
->trans
->trans_cfg
->device_family
>= IWL_DEVICE_FAMILY_AX210
)
187 mvmsta
= iwl_mvm_sta_from_staid_protected(mvm
, sta_id
);
192 if (mvmsta
->bt_reduced_txpower
== enable
)
195 value
= mvmsta
->deflink
.sta_id
;
198 value
|= BT_REDUCED_TX_POWER_BIT
;
200 IWL_DEBUG_COEX(mvm
, "%sable reduced Tx Power for sta %d\n",
201 enable
? "en" : "dis", sta_id
);
203 cmd
.reduced_txp
= cpu_to_le32(value
);
204 mvmsta
->bt_reduced_txpower
= enable
;
206 return iwl_mvm_send_cmd_pdu(mvm
, BT_COEX_UPDATE_REDUCED_TXP
,
207 CMD_ASYNC
, sizeof(cmd
), &cmd
);
210 struct iwl_bt_iterator_data
{
211 struct iwl_bt_coex_prof_old_notif
*notif
;
213 struct ieee80211_chanctx_conf
*primary
;
214 struct ieee80211_chanctx_conf
*secondary
;
221 void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm
*mvm
,
222 struct iwl_mvm_vif_link_info
*link_info
,
223 bool enable
, int rssi
)
225 link_info
->bf_data
.last_bt_coex_event
= rssi
;
226 link_info
->bf_data
.bt_coex_max_thold
=
227 enable
? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH
: 0;
228 link_info
->bf_data
.bt_coex_min_thold
=
229 enable
? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH
: 0;
232 #define MVM_COEX_TCM_PERIOD (HZ * 10)
234 static void iwl_mvm_bt_coex_tcm_based_ci(struct iwl_mvm
*mvm
,
235 struct iwl_bt_iterator_data
*data
)
237 unsigned long now
= jiffies
;
239 if (!time_after(now
, mvm
->bt_coex_last_tcm_ts
+ MVM_COEX_TCM_PERIOD
))
242 mvm
->bt_coex_last_tcm_ts
= now
;
244 /* We assume here that we don't have more than 2 vifs on 2.4GHz */
246 /* if the primary is low latency, it will stay primary */
247 if (data
->primary_ll
)
250 if (data
->primary_load
>= data
->secondary_load
)
253 swap(data
->primary
, data
->secondary
);
257 * This function receives the LB link id and checks if eSR should be
258 * enabled or disabled (due to BT coex)
261 iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm
*mvm
,
262 struct ieee80211_vif
*vif
,
266 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
267 bool have_wifi_loss_rate
=
268 iwl_fw_lookup_notif_ver(mvm
->fw
, LEGACY_GROUP
,
269 BT_PROFILE_NOTIFICATION
, 0) > 4 ||
270 iwl_fw_lookup_notif_ver(mvm
->fw
, BT_COEX_GROUP
,
271 PROFILE_NOTIF
, 0) >= 1;
272 u8 wifi_loss_mid_high_rssi
;
273 u8 wifi_loss_low_rssi
;
276 if (iwl_fw_lookup_notif_ver(mvm
->fw
, BT_COEX_GROUP
,
277 PROFILE_NOTIF
, 0) >= 1) {
278 /* For now, we consider 2.4 GHz band / ANT_A only */
279 wifi_loss_mid_high_rssi
=
280 mvm
->last_bt_wifi_loss
.wifi_loss_mid_high_rssi
[PHY_BAND_24
][0];
282 mvm
->last_bt_wifi_loss
.wifi_loss_low_rssi
[PHY_BAND_24
][0];
284 wifi_loss_mid_high_rssi
= mvm
->last_bt_notif
.wifi_loss_mid_high_rssi
;
285 wifi_loss_low_rssi
= mvm
->last_bt_notif
.wifi_loss_low_rssi
;
288 if (wifi_loss_low_rssi
== BT_OFF
)
294 /* The feature is not supported */
295 if (!have_wifi_loss_rate
)
300 * In case we don't know the RSSI - take the lower wifi loss,
301 * so we will more likely enter eSR, and if RSSI is low -
302 * we will get an update on this and exit eSR.
305 wifi_loss_rate
= wifi_loss_mid_high_rssi
;
307 else if (mvmvif
->esr_active
)
308 /* RSSI needs to get really low to disable eSR... */
310 link_rssi
<= -IWL_MVM_BT_COEX_DISABLE_ESR_THRESH
?
312 wifi_loss_mid_high_rssi
;
314 /* ...And really high before we enable it back */
316 link_rssi
<= -IWL_MVM_BT_COEX_ENABLE_ESR_THRESH
?
318 wifi_loss_mid_high_rssi
;
320 return wifi_loss_rate
<= IWL_MVM_BT_COEX_WIFI_LOSS_THRESH
;
323 void iwl_mvm_bt_coex_update_link_esr(struct iwl_mvm
*mvm
,
324 struct ieee80211_vif
*vif
,
327 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
328 struct iwl_mvm_vif_link_info
*link
= mvmvif
->link
[link_id
];
330 if (!ieee80211_vif_is_mld(vif
) ||
331 !iwl_mvm_vif_from_mac80211(vif
)->authorized
||
335 if (!iwl_mvm_bt_coex_calculate_esr_mode(mvm
, vif
,
336 (s8
)link
->beacon_stats
.avg_signal
,
337 link_id
== iwl_mvm_get_primary_link(vif
)))
338 /* In case we decided to exit eSR - stay with the primary */
339 iwl_mvm_exit_esr(mvm
, vif
, IWL_MVM_ESR_EXIT_COEX
,
340 iwl_mvm_get_primary_link(vif
));
343 static void iwl_mvm_bt_notif_per_link(struct iwl_mvm
*mvm
,
344 struct ieee80211_vif
*vif
,
345 struct iwl_bt_iterator_data
*data
,
346 unsigned int link_id
)
348 /* default smps_mode is AUTOMATIC - only used for client modes */
349 enum ieee80211_smps_mode smps_mode
= IEEE80211_SMPS_AUTOMATIC
;
350 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
351 u32 bt_activity_grading
, min_ag_for_static_smps
;
352 struct ieee80211_chanctx_conf
*chanctx_conf
;
353 struct iwl_mvm_vif_link_info
*link_info
;
354 struct ieee80211_bss_conf
*link_conf
;
357 lockdep_assert_held(&mvm
->mutex
);
359 link_info
= mvmvif
->link
[link_id
];
363 link_conf
= rcu_dereference(vif
->link_conf
[link_id
]);
364 /* This can happen due to races: if we receive the notification
365 * and have the mutex held, while mac80211 is stuck on our mutex
366 * in the middle of removing the link.
371 chanctx_conf
= rcu_dereference(link_conf
->chanctx_conf
);
373 /* If channel context is invalid or not on 2.4GHz .. */
374 if ((!chanctx_conf
||
375 chanctx_conf
->def
.chan
->band
!= NL80211_BAND_2GHZ
)) {
376 if (vif
->type
== NL80211_IFTYPE_STATION
) {
377 /* ... relax constraints and disable rssi events */
378 iwl_mvm_update_smps(mvm
, vif
, IWL_MVM_SMPS_REQ_BT_COEX
,
380 iwl_mvm_bt_coex_reduced_txp(mvm
, link_info
->ap_sta_id
,
382 iwl_mvm_bt_coex_enable_rssi_event(mvm
, link_info
, false,
388 iwl_mvm_bt_coex_update_link_esr(mvm
, vif
, link_id
);
390 if (fw_has_capa(&mvm
->fw
->ucode_capa
, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2
))
391 min_ag_for_static_smps
= BT_VERY_HIGH_TRAFFIC
;
393 min_ag_for_static_smps
= BT_HIGH_TRAFFIC
;
395 bt_activity_grading
= le32_to_cpu(data
->notif
->bt_activity_grading
);
396 if (bt_activity_grading
>= min_ag_for_static_smps
)
397 smps_mode
= IEEE80211_SMPS_STATIC
;
398 else if (bt_activity_grading
>= BT_LOW_TRAFFIC
)
399 smps_mode
= IEEE80211_SMPS_DYNAMIC
;
401 /* relax SMPS constraints for next association */
403 smps_mode
= IEEE80211_SMPS_AUTOMATIC
;
405 if (link_info
->phy_ctxt
&&
406 (mvm
->last_bt_notif
.rrc_status
& BIT(link_info
->phy_ctxt
->id
)))
407 smps_mode
= IEEE80211_SMPS_AUTOMATIC
;
409 IWL_DEBUG_COEX(data
->mvm
,
410 "mac %d link %d: bt_activity_grading %d smps_req %d\n",
411 mvmvif
->id
, link_info
->fw_link_id
,
412 bt_activity_grading
, smps_mode
);
414 if (vif
->type
== NL80211_IFTYPE_STATION
)
415 iwl_mvm_update_smps(mvm
, vif
, IWL_MVM_SMPS_REQ_BT_COEX
,
418 /* low latency is always primary */
419 if (iwl_mvm_vif_low_latency(mvmvif
)) {
420 data
->primary_ll
= true;
422 data
->secondary
= data
->primary
;
423 data
->primary
= chanctx_conf
;
426 if (vif
->type
== NL80211_IFTYPE_AP
) {
427 if (!mvmvif
->ap_ibss_active
)
430 if (chanctx_conf
== data
->primary
)
433 if (!data
->primary_ll
) {
435 * downgrade the current primary no matter what its
438 data
->secondary
= data
->primary
;
439 data
->primary
= chanctx_conf
;
441 /* there is low latency vif - we will be secondary */
442 data
->secondary
= chanctx_conf
;
445 /* FIXME: TCM load per interface? or need something per link? */
446 if (data
->primary
== chanctx_conf
)
447 data
->primary_load
= mvm
->tcm
.result
.load
[mvmvif
->id
];
448 else if (data
->secondary
== chanctx_conf
)
449 data
->secondary_load
= mvm
->tcm
.result
.load
[mvmvif
->id
];
454 * STA / P2P Client, try to be primary if first vif. If we are in low
455 * latency mode, we are already in primary and just don't do much
457 if (!data
->primary
|| data
->primary
== chanctx_conf
)
458 data
->primary
= chanctx_conf
;
459 else if (!data
->secondary
)
460 /* if secondary is not NULL, it might be a GO */
461 data
->secondary
= chanctx_conf
;
463 /* FIXME: TCM load per interface? or need something per link? */
464 if (data
->primary
== chanctx_conf
)
465 data
->primary_load
= mvm
->tcm
.result
.load
[mvmvif
->id
];
466 else if (data
->secondary
== chanctx_conf
)
467 data
->secondary_load
= mvm
->tcm
.result
.load
[mvmvif
->id
];
469 * don't reduce the Tx power if one of these is true:
472 * we are not associated
474 if (iwl_get_coex_type(mvm
, vif
) == BT_COEX_LOOSE_LUT
||
475 le32_to_cpu(mvm
->last_bt_notif
.bt_activity_grading
) == BT_OFF
||
477 iwl_mvm_bt_coex_reduced_txp(mvm
, link_info
->ap_sta_id
, false);
478 iwl_mvm_bt_coex_enable_rssi_event(mvm
, link_info
, false, 0);
482 /* try to get the avg rssi from fw */
483 ave_rssi
= link_info
->bf_data
.ave_beacon_signal
;
485 /* if the RSSI isn't valid, fake it is very low */
488 if (ave_rssi
> -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH
) {
489 if (iwl_mvm_bt_coex_reduced_txp(mvm
, link_info
->ap_sta_id
,
491 IWL_ERR(mvm
, "Couldn't send BT_CONFIG cmd\n");
492 } else if (ave_rssi
< -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH
) {
493 if (iwl_mvm_bt_coex_reduced_txp(mvm
, link_info
->ap_sta_id
,
495 IWL_ERR(mvm
, "Couldn't send BT_CONFIG cmd\n");
498 /* Begin to monitor the RSSI: it may influence the reduced Tx power */
499 iwl_mvm_bt_coex_enable_rssi_event(mvm
, link_info
, true, ave_rssi
);
502 /* must be called under rcu_read_lock */
503 static void iwl_mvm_bt_notif_iterator(void *_data
, u8
*mac
,
504 struct ieee80211_vif
*vif
)
506 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
507 struct iwl_bt_iterator_data
*data
= _data
;
508 struct iwl_mvm
*mvm
= data
->mvm
;
509 unsigned int link_id
;
511 lockdep_assert_held(&mvm
->mutex
);
514 case NL80211_IFTYPE_STATION
:
516 case NL80211_IFTYPE_AP
:
517 if (!mvmvif
->ap_ibss_active
)
524 for (link_id
= 0; link_id
< IEEE80211_MLD_MAX_NUM_LINKS
; link_id
++)
525 iwl_mvm_bt_notif_per_link(mvm
, vif
, data
, link_id
);
528 /* must be called under rcu_read_lock */
529 static void iwl_mvm_bt_coex_notif_iterator(void *_data
, u8
*mac
,
530 struct ieee80211_vif
*vif
)
532 struct iwl_mvm
*mvm
= _data
;
534 lockdep_assert_held(&mvm
->mutex
);
536 if (vif
->type
!= NL80211_IFTYPE_STATION
)
539 for (int link_id
= 0;
540 link_id
< IEEE80211_MLD_MAX_NUM_LINKS
;
542 struct ieee80211_bss_conf
*link_conf
=
543 rcu_dereference_check(vif
->link_conf
[link_id
],
544 lockdep_is_held(&mvm
->mutex
));
545 struct ieee80211_chanctx_conf
*chanctx_conf
=
546 rcu_dereference_check(link_conf
->chanctx_conf
,
547 lockdep_is_held(&mvm
->mutex
));
549 if ((!chanctx_conf
||
550 chanctx_conf
->def
.chan
->band
!= NL80211_BAND_2GHZ
))
553 iwl_mvm_bt_coex_update_link_esr(mvm
, vif
, link_id
);
557 static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm
*mvm
)
559 struct iwl_bt_iterator_data data
= {
561 .notif
= &mvm
->last_bt_notif
,
563 struct iwl_bt_coex_ci_cmd cmd
= {};
566 /* Ignore updates if we are in force mode */
567 if (unlikely(mvm
->bt_force_ant_mode
!= BT_FORCE_ANT_DIS
))
571 ieee80211_iterate_active_interfaces_atomic(
572 mvm
->hw
, IEEE80211_IFACE_ITER_NORMAL
,
573 iwl_mvm_bt_notif_iterator
, &data
);
575 if (mvm
->trans
->trans_cfg
->device_family
>= IWL_DEVICE_FAMILY_AX210
) {
580 iwl_mvm_bt_coex_tcm_based_ci(mvm
, &data
);
583 struct ieee80211_chanctx_conf
*chan
= data
.primary
;
584 if (WARN_ON(!chan
->def
.chan
)) {
589 if (chan
->def
.width
< NL80211_CHAN_WIDTH_40
) {
592 if (chan
->def
.center_freq1
>
593 chan
->def
.chan
->center_freq
)
600 iwl_ci_mask
[chan
->def
.chan
->hw_value
][ci_bw_idx
];
601 cmd
.primary_ch_phy_id
=
602 cpu_to_le32(*((u16
*)data
.primary
->drv_priv
));
605 if (data
.secondary
) {
606 struct ieee80211_chanctx_conf
*chan
= data
.secondary
;
607 if (WARN_ON(!data
.secondary
->def
.chan
)) {
612 if (chan
->def
.width
< NL80211_CHAN_WIDTH_40
) {
615 if (chan
->def
.center_freq1
>
616 chan
->def
.chan
->center_freq
)
622 cmd
.bt_secondary_ci
=
623 iwl_ci_mask
[chan
->def
.chan
->hw_value
][ci_bw_idx
];
624 cmd
.secondary_ch_phy_id
=
625 cpu_to_le32(*((u16
*)data
.secondary
->drv_priv
));
630 /* Don't spam the fw with the same command over and over */
631 if (memcmp(&cmd
, &mvm
->last_bt_ci_cmd
, sizeof(cmd
))) {
632 if (iwl_mvm_send_cmd_pdu(mvm
, BT_COEX_CI
, 0,
634 IWL_ERR(mvm
, "Failed to send BT_CI cmd\n");
635 memcpy(&mvm
->last_bt_ci_cmd
, &cmd
, sizeof(cmd
));
639 void iwl_mvm_rx_bt_coex_old_notif(struct iwl_mvm
*mvm
,
640 struct iwl_rx_cmd_buffer
*rxb
)
642 struct iwl_rx_packet
*pkt
= rxb_addr(rxb
);
643 struct iwl_bt_coex_prof_old_notif
*notif
= (void *)pkt
->data
;
645 IWL_DEBUG_COEX(mvm
, "BT Coex Notification received\n");
646 IWL_DEBUG_COEX(mvm
, "\tBT ci compliance %d\n", notif
->bt_ci_compliance
);
647 IWL_DEBUG_COEX(mvm
, "\tBT primary_ch_lut %d\n",
648 le32_to_cpu(notif
->primary_ch_lut
));
649 IWL_DEBUG_COEX(mvm
, "\tBT secondary_ch_lut %d\n",
650 le32_to_cpu(notif
->secondary_ch_lut
));
651 IWL_DEBUG_COEX(mvm
, "\tBT activity grading %d\n",
652 le32_to_cpu(notif
->bt_activity_grading
));
654 /* remember this notification for future use: rssi fluctuations */
655 memcpy(&mvm
->last_bt_notif
, notif
, sizeof(mvm
->last_bt_notif
));
657 iwl_mvm_bt_coex_notif_handle(mvm
);
660 void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm
*mvm
,
661 struct iwl_rx_cmd_buffer
*rxb
)
663 const struct iwl_rx_packet
*pkt
= rxb_addr(rxb
);
664 const struct iwl_bt_coex_profile_notif
*notif
= (const void *)pkt
->data
;
666 lockdep_assert_held(&mvm
->mutex
);
668 mvm
->last_bt_wifi_loss
= *notif
;
670 ieee80211_iterate_active_interfaces(mvm
->hw
,
671 IEEE80211_IFACE_ITER_NORMAL
,
672 iwl_mvm_bt_coex_notif_iterator
,
676 void iwl_mvm_bt_rssi_event(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
677 enum ieee80211_rssi_event_data rssi_event
)
679 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
682 lockdep_assert_held(&mvm
->mutex
);
684 /* Ignore updates if we are in force mode */
685 if (unlikely(mvm
->bt_force_ant_mode
!= BT_FORCE_ANT_DIS
))
689 * Rssi update while not associated - can happen since the statistics
690 * are handled asynchronously
692 if (mvmvif
->deflink
.ap_sta_id
== IWL_INVALID_STA
)
695 /* No BT - reports should be disabled */
696 if (le32_to_cpu(mvm
->last_bt_notif
.bt_activity_grading
) == BT_OFF
)
699 IWL_DEBUG_COEX(mvm
, "RSSI for %pM is now %s\n", vif
->bss_conf
.bssid
,
700 rssi_event
== RSSI_EVENT_HIGH
? "HIGH" : "LOW");
703 * Check if rssi is good enough for reduced Tx power, but not in loose
706 if (rssi_event
== RSSI_EVENT_LOW
||
707 iwl_get_coex_type(mvm
, vif
) == BT_COEX_LOOSE_LUT
)
708 ret
= iwl_mvm_bt_coex_reduced_txp(mvm
,
709 mvmvif
->deflink
.ap_sta_id
,
712 ret
= iwl_mvm_bt_coex_reduced_txp(mvm
,
713 mvmvif
->deflink
.ap_sta_id
,
717 IWL_ERR(mvm
, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
720 #define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000)
721 #define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200)
723 u16
iwl_mvm_coex_agg_time_limit(struct iwl_mvm
*mvm
,
724 struct ieee80211_sta
*sta
)
726 struct iwl_mvm_sta
*mvmsta
= iwl_mvm_sta_from_mac80211(sta
);
727 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(mvmsta
->vif
);
728 struct iwl_mvm_phy_ctxt
*phy_ctxt
= mvmvif
->deflink
.phy_ctxt
;
729 enum iwl_bt_coex_lut_type lut_type
;
731 if (mvm
->last_bt_notif
.ttc_status
& BIT(phy_ctxt
->id
))
732 return LINK_QUAL_AGG_TIME_LIMIT_DEF
;
734 if (le32_to_cpu(mvm
->last_bt_notif
.bt_activity_grading
) <
736 return LINK_QUAL_AGG_TIME_LIMIT_DEF
;
738 lut_type
= iwl_get_coex_type(mvm
, mvmsta
->vif
);
740 if (lut_type
== BT_COEX_LOOSE_LUT
|| lut_type
== BT_COEX_INVALID_LUT
)
741 return LINK_QUAL_AGG_TIME_LIMIT_DEF
;
743 /* tight coex, high bt traffic, reduce AGG time limit */
744 return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT
;
747 bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm
*mvm
,
748 struct ieee80211_sta
*sta
)
750 struct iwl_mvm_sta
*mvmsta
= iwl_mvm_sta_from_mac80211(sta
);
751 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(mvmsta
->vif
);
752 struct iwl_mvm_phy_ctxt
*phy_ctxt
= mvmvif
->deflink
.phy_ctxt
;
753 enum iwl_bt_coex_lut_type lut_type
;
755 if (mvm
->last_bt_notif
.ttc_status
& BIT(phy_ctxt
->id
))
758 if (le32_to_cpu(mvm
->last_bt_notif
.bt_activity_grading
) <
763 * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas
764 * since BT is already killed.
765 * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while
767 * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO.
769 lut_type
= iwl_get_coex_type(mvm
, mvmsta
->vif
);
770 return lut_type
!= BT_COEX_LOOSE_LUT
;
773 bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm
*mvm
, u8 ant
)
775 if (ant
& mvm
->cfg
->non_shared_ant
)
778 return le32_to_cpu(mvm
->last_bt_notif
.bt_activity_grading
) <
782 bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm
*mvm
)
784 return le32_to_cpu(mvm
->last_bt_notif
.bt_activity_grading
) < BT_HIGH_TRAFFIC
;
787 bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm
*mvm
,
788 enum nl80211_band band
)
790 u32 bt_activity
= le32_to_cpu(mvm
->last_bt_notif
.bt_activity_grading
);
792 if (band
!= NL80211_BAND_2GHZ
)
795 return bt_activity
>= BT_LOW_TRAFFIC
;
798 u8
iwl_mvm_bt_coex_get_single_ant_msk(struct iwl_mvm
*mvm
, u8 enabled_ants
)
800 if (fw_has_capa(&mvm
->fw
->ucode_capa
, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2
) &&
801 (mvm
->cfg
->non_shared_ant
& enabled_ants
))
802 return mvm
->cfg
->non_shared_ant
;
804 return first_antenna(enabled_ants
);
807 u8
iwl_mvm_bt_coex_tx_prio(struct iwl_mvm
*mvm
, struct ieee80211_hdr
*hdr
,
808 struct ieee80211_tx_info
*info
, u8 ac
)
810 __le16 fc
= hdr
->frame_control
;
811 bool mplut_enabled
= iwl_mvm_is_mplut_supported(mvm
);
813 if (info
->band
!= NL80211_BAND_2GHZ
)
816 if (unlikely(mvm
->bt_tx_prio
))
817 return mvm
->bt_tx_prio
- 1;
819 if (likely(ieee80211_is_data(fc
))) {
820 if (likely(ieee80211_is_data_qos(fc
))) {
822 case IEEE80211_AC_BE
:
823 return mplut_enabled
? 1 : 0;
824 case IEEE80211_AC_VI
:
825 return mplut_enabled
? 2 : 3;
826 case IEEE80211_AC_VO
:
831 } else if (is_multicast_ether_addr(hdr
->addr1
)) {
835 } else if (ieee80211_is_mgmt(fc
)) {
836 return ieee80211_is_disassoc(fc
) ? 0 : 3;
837 } else if (ieee80211_is_ctl(fc
)) {
838 /* ignore cfend and cfendack frames as we never send those */
845 void iwl_mvm_bt_coex_vif_change(struct iwl_mvm
*mvm
)
847 iwl_mvm_bt_coex_notif_handle(mvm
);