]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: rtw89: add MLO track for MLSR switch decision
authorPo-Hao Huang <phhuang@realtek.com>
Mon, 5 May 2025 07:24:35 +0000 (15:24 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Sat, 10 May 2025 00:55:27 +0000 (08:55 +0800)
Add MLSR switch mechanism based on tracking RSSI. Switch to a 2 GHz link
(if any) when average RSSI is lower than threshold -53. And, switch out
from a 2 GHz link when average RSSI is larger than threshold -38.

The sequence of MLSR switch handling is like the following.
1. initialize target link and configure to PS mode
2. configure current designated link to PS mode
3. configure target link to non-PS mode
4. deinitialize currently active links except target link

The above flow also implies that target link becomes new designated link.

Signed-off-by: Po-Hao Huang <phhuang@realtek.com>
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250505072440.45113-7-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/core.c
drivers/net/wireless/realtek/rtw89/core.h
drivers/net/wireless/realtek/rtw89/fw.c
drivers/net/wireless/realtek/rtw89/mac80211.c

index b7b2670cbc887f4c7a205ad9aac7756150f74b51..95bdd46376bf33e411798df16e89c95463b4d88a 100644 (file)
@@ -3638,6 +3638,94 @@ void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev,
        ewma_tp_init(&stats->rx_ewma_tp);
 }
 
+#define RTW89_MLSR_GOTO_2GHZ_THRESHOLD -53
+#define RTW89_MLSR_EXIT_2GHZ_THRESHOLD -38
+static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev,
+                                         struct rtw89_vif *rtwvif)
+{
+       unsigned int sel_link_id = IEEE80211_MLD_MAX_NUM_LINKS;
+       struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+       struct rtw89_vif_link *rtwvif_link;
+       const struct rtw89_chan *chan;
+       unsigned long usable_links;
+       unsigned int link_id;
+       u8 decided_bands;
+       u8 rssi;
+
+       rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi);
+       if (unlikely(!rssi))
+               return;
+
+       if (RTW89_RSSI_RAW_TO_DBM(rssi) >= RTW89_MLSR_EXIT_2GHZ_THRESHOLD)
+               decided_bands = BIT(RTW89_BAND_5G) | BIT(RTW89_BAND_6G);
+       else if (RTW89_RSSI_RAW_TO_DBM(rssi) <= RTW89_MLSR_GOTO_2GHZ_THRESHOLD)
+               decided_bands = BIT(RTW89_BAND_2G);
+       else
+               return;
+
+       usable_links = ieee80211_vif_usable_links(vif);
+
+       rtwvif_link = rtw89_get_designated_link(rtwvif);
+       if (unlikely(!rtwvif_link))
+               goto select;
+
+       chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx);
+       if (decided_bands & BIT(chan->band_type))
+               return;
+
+       usable_links &= ~BIT(rtwvif_link->link_id);
+
+select:
+       rcu_read_lock();
+
+       for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+               struct ieee80211_bss_conf *link_conf;
+               struct ieee80211_channel *channel;
+               enum rtw89_band band;
+
+               link_conf = rcu_dereference(vif->link_conf[link_id]);
+               if (unlikely(!link_conf))
+                       continue;
+
+               channel = link_conf->chanreq.oper.chan;
+               if (unlikely(!channel))
+                       continue;
+
+               band = rtw89_nl80211_to_hw_band(channel->band);
+               if (decided_bands & BIT(band)) {
+                       sel_link_id = link_id;
+                       break;
+               }
+       }
+
+       rcu_read_unlock();
+
+       if (sel_link_id == IEEE80211_MLD_MAX_NUM_LINKS)
+               return;
+
+       rtw89_core_mlsr_switch(rtwdev, rtwvif, sel_link_id);
+}
+
+static void rtw89_core_mlo_track(struct rtw89_dev *rtwdev)
+{
+       struct ieee80211_vif *vif;
+       struct rtw89_vif *rtwvif;
+
+       rtw89_for_each_rtwvif(rtwdev, rtwvif) {
+               vif = rtwvif_to_vif(rtwvif);
+               if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif))
+                       continue;
+
+               switch (rtwvif->mlo_mode) {
+               case RTW89_MLO_MODE_MLSR:
+                       rtw89_core_mlsr_link_decision(rtwdev, rtwvif);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
 static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work)
 {
        struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
@@ -3679,6 +3767,7 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work)
        rtw89_sar_track(rtwdev);
        rtw89_chanctx_track(rtwdev);
        rtw89_core_rfkill_poll(rtwdev, false);
+       rtw89_core_mlo_track(rtwdev);
 
        if (rtwdev->lps_enabled && !rtwdev->btc.lps)
                rtw89_enter_lps_track(rtwdev);
@@ -5125,6 +5214,76 @@ out:
        rtw89_load_txpwr_table(rtwdev, rtwdev->rfe_parms->byr_tbl);
 }
 
+int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
+                          unsigned int link_id)
+{
+       struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+       u16 usable_links = ieee80211_vif_usable_links(vif);
+       u16 active_links = vif->active_links;
+       struct rtw89_vif_link *target, *cur;
+       int ret;
+
+       lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+       if (unlikely(!ieee80211_vif_is_mld(vif)))
+               return -EOPNOTSUPP;
+
+       if (unlikely(!(usable_links & BIT(link_id)))) {
+               rtw89_warn(rtwdev, "%s: link id %u is not usable\n", __func__,
+                          link_id);
+               return -ENOLINK;
+       }
+
+       if (active_links == BIT(link_id))
+               return 0;
+
+       rtw89_debug(rtwdev, RTW89_DBG_STATE, "%s: switch to link id %u MLSR\n",
+                   __func__, link_id);
+
+       rtw89_leave_lps(rtwdev);
+
+       ieee80211_stop_queues(rtwdev->hw);
+       flush_work(&rtwdev->txq_work);
+
+       cur = rtw89_get_designated_link(rtwvif);
+
+       ret = ieee80211_set_active_links(vif, active_links | BIT(link_id));
+       if (ret) {
+               rtw89_err(rtwdev, "%s: failed to activate link id %u\n",
+                         __func__, link_id);
+               goto wake_queue;
+       }
+
+       target = rtwvif->links[link_id];
+       if (unlikely(!target)) {
+               rtw89_err(rtwdev, "%s: failed to confirm link id %u\n",
+                         __func__, link_id);
+
+               ieee80211_set_active_links(vif, active_links);
+               ret = -EFAULT;
+               goto wake_queue;
+       }
+
+       if (likely(cur))
+               rtw89_fw_h2c_mlo_link_cfg(rtwdev, cur, false);
+
+       rtw89_fw_h2c_mlo_link_cfg(rtwdev, target, true);
+
+       ret = ieee80211_set_active_links(vif, BIT(link_id));
+       if (ret)
+               rtw89_err(rtwdev, "%s: failed to inactivate links 0x%x\n",
+                         __func__, active_links);
+
+       rtw89_chip_rfk_channel(rtwdev, target);
+
+       rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR;
+
+wake_queue:
+       ieee80211_wake_queues(rtwdev->hw);
+
+       return ret;
+}
+
 static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev)
 {
        const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
index 86bbe8cf8cc0b16fd79cba17e4d1d85a2fbb8809..a432dd846a1fb84f9ed8f0ce020a650b96f9b174 100644 (file)
@@ -5738,6 +5738,12 @@ struct rtw89_mcc_info {
        struct rtw89_mcc_config config;
 };
 
+enum rtw89_mlo_mode {
+       RTW89_MLO_MODE_MLSR = 0,
+
+       NUM_OF_RTW89_MLO_MODE,
+};
+
 struct rtw89_mlo_info {
        struct rtw89_wait_info wait;
 };
@@ -5894,6 +5900,8 @@ struct rtw89_vif {
        struct rtw89_roc roc;
        bool offchan;
 
+       enum rtw89_mlo_mode mlo_mode;
+
        struct list_head dlink_pool;
        u8 links_inst_valid_num;
        DECLARE_BITMAP(links_inst_map, __RTW89_MLD_MAX_LINK_NUM);
@@ -7312,5 +7320,7 @@ void rtw89_core_update_p2p_ps(struct rtw89_dev *rtwdev,
                              struct rtw89_vif_link *rtwvif_link,
                              struct ieee80211_bss_conf *bss_conf);
 void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, enum rtw89_btc_hmsg event);
+int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
+                          unsigned int link_id);
 
 #endif
index aa748fe3f39eea1acf5d2824903f4e47fafc37e8..deb4fb21d0e06a1c80f50d50431e3c1216973ad1 100644 (file)
@@ -4043,6 +4043,7 @@ int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv
        bool format_v1 = false;
        struct sk_buff *skb;
        u8 main_mac_id;
+       bool init_ps;
        int ret;
 
        if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) {
@@ -4084,6 +4085,7 @@ int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv
        h2c_v1 = (struct rtw89_h2c_join_v1 *)skb->data;
 
        sta_type = rtw89_fw_get_sta_type(rtwdev, rtwvif_link, rtwsta_link);
+       init_ps = rtwvif_link != rtw89_get_designated_link(rtwvif_link->rtwvif);
 
        if (rtwsta_link)
                main_mac_id = rtw89_sta_get_main_macid(rtwsta_link->rtwsta);
@@ -4097,7 +4099,7 @@ int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv
                                      RTW89_H2C_JOININFO_W1_MLO_MODE) |
                     le32_encode_bits(0, RTW89_H2C_JOININFO_W1_EMLSR_CAB) |
                     le32_encode_bits(0, RTW89_H2C_JOININFO_W1_NSTR_EN) |
-                    le32_encode_bits(0, RTW89_H2C_JOININFO_W1_INIT_PWR_STATE) |
+                    le32_encode_bits(init_ps, RTW89_H2C_JOININFO_W1_INIT_PWR_STATE) |
                     le32_encode_bits(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US,
                                      RTW89_H2C_JOININFO_W1_EMLSR_PADDING) |
                     le32_encode_bits(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US,
index 182a952127c4568d7084c9d8a889142b1186c94e..22d13a0d5b8af6582018985975ba09b9b42cca95 100644 (file)
@@ -481,6 +481,8 @@ static int __rtw89_ops_sta_add(struct rtw89_dev *rtwdev,
        int i;
 
        if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) {
+               rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR;
+
                /* for station mode, assign the mac_id from itself */
                macid = rtw89_vif_get_main_macid(rtwvif);
        } else {