summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChih-Kang Chang <gary.chang@realtek.com>2025-07-10 07:24:15 +0300
committerPing-Ke Shih <pkshih@realtek.com>2025-07-15 04:29:56 +0300
commit83f84f263420b4a11c1b3c1ba0723c3d374b33ad (patch)
treee96d874698122131a2faa9829426e20af7d0c37b
parentd0b87d9eaf76703b81e511d05db29a5e7ef6c3e0 (diff)
downloadlinux-83f84f263420b4a11c1b3c1ba0723c3d374b33ad.tar.xz
wifi: rtw89: mcc: solve GO's TBTT change and TBTT too close to NoA issue
For some implementation acting as GO under MCC(GO+STA), the GO's TBTT might change after STA roams to another AP. This could result the new GO beacon TX at the STA timeslot of GC+STA, causing GC beacon loss. Therefore, if the GC detects beacon loss, it will pause MCC and remain on the GO side for 100 TU to detect the new TBTT beacon. Additionally, some implementation acting as GO under MCC might TX beacon too close to the NoA period. The GC calculates timeslot pattern the TOB (time offset behind) or TOA(time offset ahead) less than the minimum RX beacon time, which leads to beacon loss. Therefore, disable the beacon filter in this case. Then, if the GO's TBTT changed, the pattern TOB/TOA greater than the minimum RX beacon time, the beacon filter should be retriggered during MCC update. Moreover, if the beacon filter is disabled initially but the GO timeslot change, causing QoS null data detection fail, also pause MCC to detect new TBTT beacon. Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com> Link: https://patch.msgid.link/20250710042423.73617-7-pkshih@realtek.com
-rw-r--r--drivers/net/wireless/realtek/rtw89/chan.c143
-rw-r--r--drivers/net/wireless/realtek/rtw89/chan.h6
-rw-r--r--drivers/net/wireless/realtek/rtw89/core.h4
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac.c10
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac80211.c5
5 files changed, 154 insertions, 14 deletions
diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c
index 6c80e08ae99d..ed0d89ac3495 100644
--- a/drivers/net/wireless/realtek/rtw89/chan.c
+++ b/drivers/net/wireless/realtek/rtw89/chan.c
@@ -1000,6 +1000,11 @@ static void rtw89_mcc_assign_pattern(struct rtw89_dev *rtwdev,
*pattern = *new;
memset(&pattern->courtesy, 0, sizeof(pattern->courtesy));
+ if (RTW89_MCC_REQ_COURTESY(pattern, aux) && aux->is_gc)
+ aux->ignore_bcn = true;
+ else
+ aux->ignore_bcn = false;
+
if (RTW89_MCC_REQ_COURTESY(pattern, aux) && rtw89_mcc_can_courtesy(ref, aux)) {
crtz = &pattern->courtesy.ref;
ref->crtz = crtz;
@@ -1014,6 +1019,11 @@ static void rtw89_mcc_assign_pattern(struct rtw89_dev *rtwdev,
ref->crtz = NULL;
}
+ if (RTW89_MCC_REQ_COURTESY(pattern, ref) && ref->is_gc)
+ ref->ignore_bcn = true;
+ else
+ ref->ignore_bcn = false;
+
if (RTW89_MCC_REQ_COURTESY(pattern, ref) && rtw89_mcc_can_courtesy(aux, ref)) {
crtz = &pattern->courtesy.aux;
aux->crtz = crtz;
@@ -2255,15 +2265,6 @@ static int rtw89_mcc_start(struct rtw89_dev *rtwdev)
else
mcc->mode = RTW89_MCC_MODE_GC_STA;
- if (rtw89_mcc_ignore_bcn(rtwdev, ref)) {
- rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, aux->rtwvif_link, false);
- } else if (rtw89_mcc_ignore_bcn(rtwdev, aux)) {
- rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, ref->rtwvif_link, false);
- } else {
- rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, ref->rtwvif_link, true);
- rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, aux->rtwvif_link, true);
- }
-
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC sel mode: %d\n", mcc->mode);
mcc->group = RTW89_MCC_DFLT_GROUP;
@@ -2272,6 +2273,15 @@ static int rtw89_mcc_start(struct rtw89_dev *rtwdev)
if (ret)
return ret;
+ if (rtw89_mcc_ignore_bcn(rtwdev, ref) || aux->ignore_bcn) {
+ rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, aux->rtwvif_link, false);
+ } else if (rtw89_mcc_ignore_bcn(rtwdev, aux) || ref->ignore_bcn) {
+ rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, ref->rtwvif_link, false);
+ } else {
+ rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, ref->rtwvif_link, true);
+ rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, aux->rtwvif_link, true);
+ }
+
if (rtw89_concurrent_via_mrc(rtwdev))
ret = __mrc_fw_start(rtwdev, false);
else
@@ -2391,7 +2401,11 @@ static void rtw89_mcc_stop(struct rtw89_dev *rtwdev,
static int rtw89_mcc_update(struct rtw89_dev *rtwdev)
{
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
+ bool old_ref_ignore_bcn = mcc->role_ref.ignore_bcn;
+ bool old_aux_ignore_bcn = mcc->role_aux.ignore_bcn;
struct rtw89_mcc_config *config = &mcc->config;
+ struct rtw89_mcc_role *ref = &mcc->role_ref;
+ struct rtw89_mcc_role *aux = &mcc->role_aux;
struct rtw89_mcc_config old_cfg = *config;
bool courtesy_changed;
bool sync_changed;
@@ -2406,6 +2420,11 @@ static int rtw89_mcc_update(struct rtw89_dev *rtwdev)
if (ret)
return ret;
+ if (old_ref_ignore_bcn != ref->ignore_bcn)
+ rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, ref->rtwvif_link, !ref->ignore_bcn);
+ else if (old_aux_ignore_bcn != aux->ignore_bcn)
+ rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, aux->rtwvif_link, !aux->ignore_bcn);
+
if (memcmp(&old_cfg.pattern.courtesy, &config->pattern.courtesy,
sizeof(old_cfg.pattern.courtesy)) == 0)
courtesy_changed = false;
@@ -2441,10 +2460,105 @@ static int rtw89_mcc_update(struct rtw89_dev *rtwdev)
return 0;
}
+static int rtw89_mcc_search_gc_iterator(struct rtw89_dev *rtwdev,
+ struct rtw89_mcc_role *mcc_role,
+ unsigned int ordered_idx,
+ void *data)
+{
+ struct rtw89_mcc_role **role = data;
+
+ if (mcc_role->is_gc)
+ *role = mcc_role;
+
+ return 0;
+}
+
+static struct rtw89_mcc_role *rtw89_mcc_get_gc_role(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_mcc_info *mcc = &rtwdev->mcc;
+ struct rtw89_mcc_role *role = NULL;
+
+ if (mcc->mode != RTW89_MCC_MODE_GC_STA)
+ return NULL;
+
+ rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_search_gc_iterator, &role);
+
+ return role;
+}
+
+void rtw89_mcc_gc_detect_beacon_work(struct wiphy *wiphy, struct wiphy_work *work)
+{
+ struct rtw89_vif_link *rtwvif_link = container_of(work, struct rtw89_vif_link,
+ mcc_gc_detect_beacon_work.work);
+ struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link);
+ enum rtw89_entity_mode mode;
+ struct rtw89_dev *rtwdev;
+
+ lockdep_assert_wiphy(wiphy);
+
+ rtwdev = rtwvif_link->rtwvif->rtwdev;
+
+ mode = rtw89_get_entity_mode(rtwdev);
+ if (mode != RTW89_ENTITY_MODE_MCC)
+ return;
+
+ if (READ_ONCE(rtwvif_link->sync_bcn_tsf) > rtwvif_link->last_sync_bcn_tsf)
+ rtwvif_link->detect_bcn_count = 0;
+ else
+ rtwvif_link->detect_bcn_count++;
+
+ if (rtwvif_link->detect_bcn_count < RTW89_MCC_DETECT_BCN_MAX_TRIES)
+ rtw89_chanctx_proceed(rtwdev, NULL);
+ else
+ ieee80211_connection_loss(vif);
+}
+
+bool rtw89_mcc_detect_go_bcn(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link)
+{
+ enum rtw89_entity_mode mode = rtw89_get_entity_mode(rtwdev);
+ struct rtw89_chanctx_pause_parm pause_parm = {
+ .rsn = RTW89_CHANCTX_PAUSE_REASON_GC_BCN_LOSS,
+ .trigger = rtwvif_link,
+ };
+ struct ieee80211_bss_conf *bss_conf;
+ struct rtw89_mcc_role *role;
+ u16 bcn_int;
+
+ if (mode != RTW89_ENTITY_MODE_MCC)
+ return false;
+
+ role = rtw89_mcc_get_gc_role(rtwdev);
+ if (!role)
+ return false;
+
+ if (role->rtwvif_link != rtwvif_link)
+ return false;
+
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC GC beacon loss, pause MCC to detect GO beacon\n");
+
+ rcu_read_lock();
+
+ bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true);
+ bcn_int = bss_conf->beacon_int;
+
+ rcu_read_unlock();
+
+ rtw89_chanctx_pause(rtwdev, &pause_parm);
+ rtwvif_link->last_sync_bcn_tsf = READ_ONCE(rtwvif_link->sync_bcn_tsf);
+ wiphy_delayed_work_queue(rtwdev->hw->wiphy,
+ &rtwvif_link->mcc_gc_detect_beacon_work,
+ usecs_to_jiffies(ieee80211_tu_to_usec(bcn_int)));
+
+ return true;
+}
+
static void rtw89_mcc_detect_connection(struct rtw89_dev *rtwdev,
struct rtw89_mcc_role *role)
{
struct ieee80211_vif *vif;
+ bool start_detect;
int ret;
ret = rtw89_core_send_nullfunc(rtwdev, role->rtwvif_link, true, false,
@@ -2458,7 +2572,12 @@ static void rtw89_mcc_detect_connection(struct rtw89_dev *rtwdev,
return;
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
- "MCC <macid %d> can not detect AP\n", role->rtwvif_link->mac_id);
+ "MCC <macid %d> can not detect AP/GO\n", role->rtwvif_link->mac_id);
+
+ start_detect = rtw89_mcc_detect_go_bcn(rtwdev, role->rtwvif_link);
+ if (start_detect)
+ return;
+
vif = rtwvif_link_to_vif(role->rtwvif_link);
ieee80211_connection_loss(vif);
}
@@ -2474,9 +2593,9 @@ static void rtw89_mcc_track(struct rtw89_dev *rtwdev)
u16 bcn_ofst;
u16 diff;
- if (rtw89_mcc_ignore_bcn(rtwdev, ref))
+ if (rtw89_mcc_ignore_bcn(rtwdev, ref) || aux->ignore_bcn)
rtw89_mcc_detect_connection(rtwdev, aux);
- else if (rtw89_mcc_ignore_bcn(rtwdev, aux))
+ else if (rtw89_mcc_ignore_bcn(rtwdev, aux) || ref->ignore_bcn)
rtw89_mcc_detect_connection(rtwdev, ref);
if (mcc->mode != RTW89_MCC_MODE_GC_STA)
diff --git a/drivers/net/wireless/realtek/rtw89/chan.h b/drivers/net/wireless/realtek/rtw89/chan.h
index 9a62b7c98b83..b1175419f92b 100644
--- a/drivers/net/wireless/realtek/rtw89/chan.h
+++ b/drivers/net/wireless/realtek/rtw89/chan.h
@@ -23,6 +23,8 @@
#define RTW89_MCC_PROBE_TIMEOUT 100
#define RTW89_MCC_PROBE_MAX_TRIES 3
+#define RTW89_MCC_DETECT_BCN_MAX_TRIES 2
+
#define RTW89_MCC_MIN_GO_DURATION \
(RTW89_MCC_EARLY_TX_BCN_TIME + RTW89_MCC_MIN_RX_BCN_TIME)
@@ -94,6 +96,7 @@ struct rtw89_mr_chanctx_info {
enum rtw89_chanctx_pause_reasons {
RTW89_CHANCTX_PAUSE_REASON_HW_SCAN,
RTW89_CHANCTX_PAUSE_REASON_ROC,
+ RTW89_CHANCTX_PAUSE_REASON_GC_BCN_LOSS,
};
struct rtw89_chanctx_pause_parm {
@@ -188,6 +191,9 @@ struct rtw89_mcc_links_info {
void rtw89_mcc_get_links(struct rtw89_dev *rtwdev, struct rtw89_mcc_links_info *info);
void rtw89_mcc_prepare_done_work(struct wiphy *wiphy, struct wiphy_work *work);
+void rtw89_mcc_gc_detect_beacon_work(struct wiphy *wiphy, struct wiphy_work *work);
+bool rtw89_mcc_detect_go_bcn(struct rtw89_dev *rtwdev,
+ struct rtw89_vif_link *rtwvif_link);
int rtw89_chanctx_ops_add(struct rtw89_dev *rtwdev,
struct ieee80211_chanctx_conf *ctx);
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 2faf0889f7df..2e5f129ff4f8 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -3597,6 +3597,7 @@ struct rtw89_vif_link {
u8 hit_rule;
u8 last_noa_nr;
u64 sync_bcn_tsf;
+ u64 last_sync_bcn_tsf;
bool rand_tsf_done;
bool trigger;
bool lsig_txop;
@@ -3620,6 +3621,8 @@ struct rtw89_vif_link {
struct list_head general_pkt_list;
struct rtw89_p2p_noa_setter p2p_noa;
struct rtw89_ps_noa_once_handler noa_once;
+ struct wiphy_delayed_work mcc_gc_detect_beacon_work;
+ u8 detect_bcn_count;
};
enum rtw89_lv1_rcvy_step {
@@ -5778,6 +5781,7 @@ struct rtw89_mcc_role {
bool is_2ghz;
bool is_go;
bool is_gc;
+ bool ignore_bcn;
};
struct rtw89_mcc_bt_role {
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index 37b113e3bd26..f6bbc796329c 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -5088,6 +5088,7 @@ rtw89_mac_bcn_fltr_rpt(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l
const struct rtw89_c2h_mac_bcnfltr_rpt *c2h =
(const struct rtw89_c2h_mac_bcnfltr_rpt *)skb->data;
u8 type, event, mac_id;
+ bool start_detect;
s8 sig;
type = le32_get_bits(c2h->w2, RTW89_C2H_MAC_BCNFLTR_RPT_W2_TYPE);
@@ -5105,10 +5106,15 @@ rtw89_mac_bcn_fltr_rpt(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l
switch (type) {
case RTW89_BCN_FLTR_BEACON_LOSS:
if (!rtwdev->scanning && !rtwvif->offchan &&
- !rtwvif_link->noa_once.in_duration)
+ !rtwvif_link->noa_once.in_duration) {
+ start_detect = rtw89_mcc_detect_go_bcn(rtwdev, rtwvif_link);
+ if (start_detect)
+ return;
+
ieee80211_connection_loss(vif);
- else
+ } else {
rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true);
+ }
return;
case RTW89_BCN_FLTR_NOTIFY:
nl_event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index c982ad633b4a..c1ca6d741b32 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -113,6 +113,8 @@ static int __rtw89_ops_add_iface_link(struct rtw89_dev *rtwdev,
wiphy_work_init(&rtwvif_link->update_beacon_work, rtw89_core_update_beacon_work);
wiphy_delayed_work_init(&rtwvif_link->csa_beacon_work, rtw89_core_csa_beacon_work);
+ wiphy_delayed_work_init(&rtwvif_link->mcc_gc_detect_beacon_work,
+ rtw89_mcc_gc_detect_beacon_work);
INIT_LIST_HEAD(&rtwvif_link->general_pkt_list);
@@ -124,6 +126,7 @@ static int __rtw89_ops_add_iface_link(struct rtw89_dev *rtwdev,
rtwvif_link->chanctx_idx = RTW89_CHANCTX_0;
rtwvif_link->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT;
rtwvif_link->rand_tsf_done = false;
+ rtwvif_link->detect_bcn_count = 0;
rcu_read_lock();
@@ -147,6 +150,8 @@ static void __rtw89_ops_remove_iface_link(struct rtw89_dev *rtwdev,
wiphy_work_cancel(rtwdev->hw->wiphy, &rtwvif_link->update_beacon_work);
wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwvif_link->csa_beacon_work);
+ wiphy_delayed_work_cancel(rtwdev->hw->wiphy,
+ &rtwvif_link->mcc_gc_detect_beacon_work);
rtw89_p2p_noa_once_deinit(rtwvif_link);