From: Ping-Ke Shih <pkshih@realtek.com>
To: <linux-wireless@vger.kernel.org>
Cc: <ku920601@realtek.com>
Subject: [PATCH rtw-next 14/14] wifi: rtw89: coex: Handle Bluetooth LE-Audio related coexistence feature
Date: Fri, 24 Jul 2026 21:56:40 +0800 [thread overview]
Message-ID: <20260724135640.3195044-15-pkshih@realtek.com> (raw)
In-Reply-To: <20260724135640.3195044-1-pkshih@realtek.com>
From: Ching-Te Ku <ku920601@realtek.com>
Implement event handler of BTF_EVNT_BT_LEAUDIO_INFO C2H command, and
related coexistence mechanism for Bluetooth LE-Audio feature.
Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtw89/coex.c | 199 ++++++++++++++++++++++
drivers/net/wireless/realtek/rtw89/core.h | 35 +++-
2 files changed, 233 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 6b3e199398cf..0e42c720819a 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -951,6 +951,7 @@ enum btc_reason_and_action {
BTC_RSN_ACT1_WORK,
BTC_RSN_BT_DEVINFO_WORK,
BTC_RSN_RFK_CHK_WORK,
+ BTC_RSN_UPDATE_BT_LEAUDINFO,
BTC_RSN_NUM,
BTC_ACT_NONE = 100,
BTC_ACT_WL_ONLY,
@@ -975,6 +976,8 @@ enum btc_reason_and_action {
BTC_ACT_BT_A2DP_PAN,
BTC_ACT_BT_PAN_HID,
BTC_ACT_BT_A2DP_PAN_HID,
+ BTC_ACT_BT_BIS,
+ BTC_ACT_BT_CIS,
BTC_ACT_WL_25G_MCC,
BTC_ACT_WL_2G_MCC,
BTC_ACT_WL_2G_SCC,
@@ -6076,6 +6079,43 @@ static void _action_bt_pan_hid(struct rtw89_dev *rtwdev)
}
}
+static void _action_bt_bis(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ u16 policy_type;
+
+ if (dm->bis_tdma)
+ policy_type = BTC_CXP_OFFE_2GBWISOB;
+ else
+ policy_type = BTC_CXP_OFF_EQ0;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_PTA);
+ _set_policy(rtwdev, policy_type, BTC_ACT_BT_BIS);
+}
+
+static void _action_bt_cis(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt0.link_info;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ u16 policy_type;
+
+ if (bt_linfo->a2dp_desc.active) {
+ dm->slot_dur[CXST_W1] = 80;
+ dm->slot_dur[CXST_B1] = 20;
+ policy_type = BTC_CXP_PFIX_TDW1B1;
+ } else if (wl->status.map.traffic_dir & BIT(RTW89_TFC_UL)) {
+ policy_type = BTC_CXP_OFF_BWB0;
+ } else {
+ policy_type = BTC_CXP_OFF_BWB1;
+ }
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_PTA);
+ _set_policy(rtwdev, policy_type, BTC_ACT_BT_CIS);
+}
+
static void _action_bt_a2dp_pan_hid(struct rtw89_dev *rtwdev)
{
struct rtw89_btc *btc = &rtwdev->btc;
@@ -6692,6 +6732,7 @@ static void _action_by_bt(struct rtw89_dev *rtwdev)
struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;
+ struct rtw89_btc_bt_leaudio_desc leaudio = bt_linfo->leaudio_desc;
struct rtw89_btc_bt_hid_desc hid = bt_linfo->hid_desc;
struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc;
struct rtw89_btc_bt_pan_desc pan = bt_linfo->pan_desc;
@@ -6715,6 +6756,12 @@ static void _action_by_bt(struct rtw89_dev *rtwdev)
if (bt_linfo->pan_desc.exist)
profile_map |= BTC_BT_PAN;
+ if (leaudio.bis_exist)
+ profile_map |= BTC_BT_BIS;
+
+ if (leaudio.cis_exist)
+ profile_map |= BTC_BT_CIS;
+
switch (profile_map) {
case BTC_BT_NOPROFILE:
if (pan.active)
@@ -6740,6 +6787,13 @@ static void _action_by_bt(struct rtw89_dev *rtwdev)
case BTC_BT_PAN:
_action_bt_pan(rtwdev);
break;
+ case BTC_BT_BIS:
+ _action_bt_bis(rtwdev);
+ break;
+ case BTC_BT_CIS:
+ case BTC_BT_CIS | BTC_BT_HID:
+ _action_bt_cis(rtwdev);
+ break;
case BTC_BT_A2DP | BTC_BT_HFP:
case BTC_BT_A2DP | BTC_BT_HID:
case BTC_BT_A2DP | BTC_BT_HFP | BTC_BT_HID:
@@ -7909,6 +7963,140 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid)
}
}
+static void _update_bt_link_cnt(struct rtw89_dev *rtwdev,
+ struct rtw89_btc_bt_info *bt, bool b56g)
+{
+ struct rtw89_btc_bt_link_info *b = b56g ? &bt->link_info_56g :
+ &bt->link_info;
+ struct rtw89_btc_bt_leaudio_desc *leaudio = &b->leaudio_desc;
+ struct rtw89_btc_bt_a2dp_desc *a2dp = &b->a2dp_desc;
+ struct rtw89_btc_bt_hfp_desc *hfp = &b->hfp_desc;
+ struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc;
+ struct rtw89_btc_bt_pan_desc *pan = &b->pan_desc;
+
+ b->link_cnt.last = b->link_cnt.now;
+ b->link_cnt.now = 0;
+ b->link_cnt.chg = 0;
+
+ b->link_cnt.now += hfp->exist;
+ b->link_cnt.now += hid->exist;
+ b->link_cnt.now += a2dp->exist;
+ b->link_cnt.now += pan->exist;
+ b->link_cnt.now += leaudio->bis_exist;
+ b->link_cnt.now += leaudio->cis_exist;
+}
+
+static void _update_bt_leaudio_info(struct rtw89_dev *rtwdev, u8 bid,
+ void *buf, u32 len)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_bt_info *bt = (bid == BTC_BT_1ST) ? &cx->bt0 : &cx->bt1;
+ struct rtw89_btc_bt_link_info *b = &bt->link_info;
+ struct rtw89_btc_bt_leaudio_info *src = buf;
+ struct rtw89_btc_bt_leaudio_desc *leaudio;
+ bool bt_56g = false, cis_exist, bis_exist;
+ u8 bis_cnt, cis_cnt;
+
+ if ((src->len & BIT(7)) && bt->band_56G_support) {
+ b = &bt->link_info_56g;
+ bt_56g = true;
+ }
+
+ leaudio = &b->leaudio_desc;
+
+ if (!memcmp(b->leaudio_raw_info, src, BTC_BTINFO_MAX)) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s return by leaudio-info duplicate!\n",
+ __func__);
+ bt->bcnt[BTC_BCNT_LEAUDIO_INFOSAME]++;
+ return;
+ }
+
+ memcpy(b->leaudio_raw_info, src, BTC_BTINFO_MAX);
+
+ bis_exist = u8_get_bits(src->bis_cis, RTW89_BTC_LEAU_INFO_L2_BIS_EX);
+ cis_exist = u8_get_bits(src->bis_cis, RTW89_BTC_LEAU_INFO_L2_CIS_EX);
+ bis_cnt = u8_get_bits(src->bis_cis, RTW89_BTC_LEAU_INFO_L2_BIS_CNT);
+ cis_cnt = u8_get_bits(src->bis_cis, RTW89_BTC_LEAU_INFO_L2_CIS_CNT);
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s: bis_exist:%d bis_cnt:%d cis_exist:%d cis_cnt:%d rssi:%d\n",
+ __func__, bis_exist, bis_cnt, cis_exist, cis_cnt, src->rssi);
+
+ if (!bis_exist && !cis_exist)
+ memset(leaudio, 0, sizeof(*leaudio));
+
+ leaudio->bis_cnt = bis_cnt;
+ leaudio->bis_exist = bis_exist;
+ leaudio->cis_cnt = cis_cnt;
+ leaudio->cis_exist = cis_exist;
+
+ if (leaudio->bis_exist)
+ b->status.map.profile_map |= BTC_BT_BIS;
+ else
+ b->status.map.profile_map &= ~BTC_BT_BIS;
+
+ if (leaudio->cis_exist)
+ b->status.map.profile_map |= BTC_BT_CIS;
+ else
+ b->status.map.profile_map &= ~BTC_BT_CIS;
+
+ _update_bt_link_cnt(rtwdev, bt, bt_56g);
+
+ leaudio->rssi = chip->ops->btc_get_bt_rssi(rtwdev, src->rssi);
+
+ leaudio->bis_cnt_last = leaudio->bis_cnt;
+ leaudio->bis_exist_last = leaudio->bis_exist;
+ leaudio->cis_cnt_last = leaudio->cis_cnt;
+ leaudio->cis_exist_last = leaudio->cis_exist;
+}
+
+static void _update_bt_bistdma_info(struct rtw89_dev *rtwdev, u8 bid,
+ void *buf, u32 len)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_bt_info *bt = (bid == BTC_BT_1ST) ? &cx->bt0 : &cx->bt1;
+ struct rtw89_btc_bt_link_info *b = &bt->link_info;
+ struct rtw89_btc_bt_bistdma_info_le *src = buf;
+ struct rtw89_btc_bt_leaudio_desc *leaudio;
+
+ if ((src->len & BIT(7)) && bt->band_56G_support)
+ b = &bt->link_info_56g;
+
+ leaudio = &b->leaudio_desc;
+
+ if (!memcmp(b->bistdma_raw_info, src, BTC_BTINFO_MAX)) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s return by bistdma-info duplicate!\n",
+ __func__);
+ bt->bcnt[BTC_BCNT_LEAUDIO_INFOSAME]++;
+ return;
+ }
+
+ memcpy(b->bistdma_raw_info, src, BTC_BTINFO_MAX);
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s: bis_start_end:%d bis_trx:%d\n",
+ __func__,
+ u8_get_bits(src->bis, RTW89_BTC_BIS_INFO_L2_START_END),
+ u8_get_bits(src->bis, RTW89_BTC_BIS_INFO_L2_TRX));
+
+ leaudio->bis_trx = u8_get_bits(src->bis, RTW89_BTC_BIS_INFO_L2_TRX);
+ leaudio->bis_start_end = u8_get_bits(src->bis,
+ RTW89_BTC_BIS_INFO_L2_START_END);
+
+ leaudio->diff_t = (src->diff_t_hb * 256 + src->diff_t_lb) * 625 / 1000;
+
+ if (btc->ant_type == BTC_ANT_SHARED && leaudio->diff_t > 13)
+ dm->bis_tdma = true;
+ else
+ dm->bis_tdma = false;
+}
+
#define BTC_BTINFO_PWR_LEN 5
static void _update_bt_txpwr_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len)
{
@@ -9606,6 +9794,14 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
case BTF_EVNT_CX_RUNINFO:
btc->dm.cnt_dm[BTC_DCNT_CX_RUNINFO]++;
break;
+ case BTF_EVNT_BT_LEAUDIO_INFO:
+ bt->bcnt[BTC_BCNT_LEAUDIO_INFOUPDATE]++;
+ if (buf[BTC_BTINFO_L0] == BTC_BTINFO_BISTDMA)
+ _update_bt_bistdma_info(rtwdev, bid, buf, len);
+ else
+ _update_bt_leaudio_info(rtwdev, bid, buf, len);
+ _run_coex(rtwdev, BTC_RSN_UPDATE_BT_LEAUDINFO);
+ break;
case BTF_EVNT_BT_QUERY_TXPWR:
bt->bcnt[BTC_BCNT_TXPWR_UPDATE]++;
_update_bt_txpwr_info(rtwdev, buf, len);
@@ -10107,6 +10303,7 @@ static const char *steps_to_str(u16 step)
CASE_BTC_RSN_STR(ACT1_WORK);
CASE_BTC_RSN_STR(BT_DEVINFO_WORK);
CASE_BTC_RSN_STR(RFK_CHK_WORK);
+ CASE_BTC_RSN_STR(UPDATE_BT_LEAUDINFO);
CASE_BTC_ACT_STR(NONE);
CASE_BTC_ACT_STR(WL_ONLY);
@@ -10131,6 +10328,8 @@ static const char *steps_to_str(u16 step)
CASE_BTC_ACT_STR(BT_A2DP_PAN);
CASE_BTC_ACT_STR(BT_PAN_HID);
CASE_BTC_ACT_STR(BT_A2DP_PAN_HID);
+ CASE_BTC_ACT_STR(BT_BIS);
+ CASE_BTC_ACT_STR(BT_CIS);
CASE_BTC_ACT_STR(WL_25G_MCC);
CASE_BTC_ACT_STR(WL_2G_MCC);
CASE_BTC_ACT_STR(WL_2G_SCC);
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index aaed155ea423..09c19a6b9058 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -1383,6 +1383,8 @@ enum rtw89_btc_btinfo {
BTC_BTINFO_MAX
};
+#define BTC_BTINFO_BISTDMA 0x48 /* cmd value that identifies BISTDMA data */
+
enum rtw89_btc_dcnt {
BTC_DCNT_RUN = 0x0,
BTC_DCNT_CX_RUNINFO,
@@ -2167,6 +2169,31 @@ struct rtw89_btc_bt_txpwr_desc {
u8 le_gain_index;
};
+struct rtw89_btc_bt_leaudio_info {
+ u8 cmd;
+ u8 len;
+ u8 bis_cis;
+#define RTW89_BTC_LEAU_INFO_L2_BIS_EX BIT(0)
+#define RTW89_BTC_LEAU_INFO_L2_BIS_CNT GENMASK(3, 1)
+#define RTW89_BTC_LEAU_INFO_L2_CIS_EX BIT(4)
+#define RTW89_BTC_LEAU_INFO_L2_CIS_CNT GENMASK(7, 5)
+ u8 rssi;
+ __le32 hbrsvd;
+} __packed;
+
+struct rtw89_btc_bt_bistdma_info_le {
+ u8 cmd;
+ u8 len;
+ u8 bis; /* BIT(2) ~ BIT(7) is rsvd */
+#define RTW89_BTC_BIS_INFO_L2_START_END BIT(0) /* 0: BIS start, 1: BIS end */
+#define RTW89_BTC_BIS_INFO_L2_TRX BIT(1) /* 0: BIS Tx, 1: BIS Rx */
+ u8 diff_t_lb;
+ u8 diff_t_hb; /* diff_t = (diff_t_hb * 256 + diff_t_lb) * 0.625 ms */
+ u8 hb1rsvd;
+ u8 hb2rsvd;
+ u8 hb3rsvd;
+} __packed;
+
struct rtw89_btc_bt_leaudio_desc {
u32 bis_exist: 1;
u32 bis_exist_last: 1;
@@ -2177,7 +2204,9 @@ struct rtw89_btc_bt_leaudio_desc {
u32 rssi: 8;
u32 bis_cnt_last: 3;
u32 cis_cnt_last: 3;
- u32 rsvd: 8;
+ u32 bis_trx: 1;
+ u32 bis_start_end: 1;
+ u32 rsvd: 6;
u16 diff_t;
};
@@ -2213,6 +2242,9 @@ struct rtw89_btc_bt_link_info {
u8 ble_scan_en: 1;
u8 reinit: 1;
u8 rsvd: 6;
+
+ u8 leaudio_raw_info[BTC_BTINFO_MAX]; /* raw LE audio info from BT mailbox */
+ u8 bistdma_raw_info[BTC_BTINFO_MAX]; /* raw BIS-TDMA info from BT mailbox */
};
struct rtw89_btc_bind_bt_status {
@@ -4085,6 +4117,7 @@ struct rtw89_btc_dm {
u8 lps_ctrl_scbd: 1;
u8 lps_ctrl_scbd_last: 1;
u8 lps_ctrl_change: 1;
+ u8 bis_tdma: 1; /* BIS TDMA mode active */
u8 scbd_write_instant;
bool scbd_b2w_update;
bool scbd_w2b_update;
--
2.25.1
prev parent reply other threads:[~2026-07-24 13:57 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-24 13:56 [PATCH rtw-next 00/14] wifi: rtw89: coex: add more command / event formats for coming RTL8922D Ping-Ke Shih
2026-07-24 13:56 ` [PATCH rtw-next 01/14] wifi: rtw89: coex: Add chip initial info version 11 for RTL8922A/D Ping-Ke Shih
2026-07-24 13:56 ` [PATCH rtw-next 02/14] wifi: rtw89: coex: Add Wi-Fi MLO info version 2 H2C command Ping-Ke Shih
2026-07-24 13:56 ` [PATCH rtw-next 03/14] wifi: rtw89: coex: Add firmware 0.35.111.X support for RTL8922A/D Ping-Ke Shih
2026-07-24 13:56 ` [PATCH rtw-next 04/14] wifi: rtw89: coex: Rearrange _ntfy_role_info info Ping-Ke Shih
2026-07-24 13:56 ` [PATCH rtw-next 05/14] wifi: rtw89: coex: Separate _ntfy_role_info into two function Ping-Ke Shih
2026-07-24 13:56 ` [PATCH rtw-next 06/14] wifi: rtw89: coex: Decrease Wi-Fi special packet protect time Ping-Ke Shih
2026-07-24 13:56 ` [PATCH rtw-next 07/14] wifi: rtw89: coex: complete GPIO debug configuration handler Ping-Ke Shih
2026-07-24 13:56 ` [PATCH rtw-next 08/14] wifi: rtw89: coex: Refine RF calibration notify flow Ping-Ke Shih
2026-07-24 13:56 ` [PATCH rtw-next 09/14] wifi: rtw89: coex: Fix log dump format with proper newline Ping-Ke Shih
2026-07-24 13:56 ` [PATCH rtw-next 10/14] wifi: rtw89: coex: Add BTC report version 8 support for BT sub-reports Ping-Ke Shih
2026-07-24 13:56 ` [PATCH rtw-next 11/14] wifi: rtw89: coex: Add dual Bluetooth debug info dump Ping-Ke Shih
2026-07-24 13:56 ` [PATCH rtw-next 12/14] wifi: rtw89: coex: Fix TDMA v8 handling to unblock BTC report parsing Ping-Ke Shih
2026-07-24 13:56 ` [PATCH rtw-next 13/14] wifi: rtw89: coex: Fix H2C command redundant send & version fall through Ping-Ke Shih
2026-07-24 13:56 ` Ping-Ke Shih [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260724135640.3195044-15-pkshih@realtek.com \
--to=pkshih@realtek.com \
--cc=ku920601@realtek.com \
--cc=linux-wireless@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox