]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: rtw89: mcc: solve GO's TBTT change and TBTT too close to NoA issue
authorChih-Kang Chang <gary.chang@realtek.com>
Thu, 10 Jul 2025 04:24:15 +0000 (12:24 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Tue, 15 Jul 2025 01:29:56 +0000 (09:29 +0800)
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
drivers/net/wireless/realtek/rtw89/chan.c
drivers/net/wireless/realtek/rtw89/chan.h
drivers/net/wireless/realtek/rtw89/core.h
drivers/net/wireless/realtek/rtw89/mac.c
drivers/net/wireless/realtek/rtw89/mac80211.c

index 6c80e08ae99ddd2093077bf7454b6f0dd918d371..ed0d89ac349563b0494dd39971786fb7dcd7ed47 100644 (file)
@@ -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)
index 9a62b7c98b83d9db3e4576585ecd8d8fe4558d9a..b1175419f92b38eb08f2a8b64aa928c0b8e4870a 100644 (file)
@@ -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);
index 2faf0889f7dfa0681c3cbbf77a4eef325e381bba..2e5f129ff4f837bf213151ddc24f4335ca15ca23 100644 (file)
@@ -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 {
index 37b113e3bd26e5a82b13b7de6cd2e4a174d85f9a..f6bbc796329cd79ad967ecd5d7905df4045ba368 100644 (file)
@@ -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;
index c982ad633b4a516014850c686c167b9ee70814e7..c1ca6d741b329c1755bbe436c33e75b2fa898c9e 100644 (file)
@@ -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);