]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: rtw89: implement channel switch support
authorZong-Zhe Yang <kevin_yang@realtek.com>
Thu, 5 Jun 2025 11:42:05 +0000 (19:42 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Tue, 10 Jun 2025 01:46:10 +0000 (09:46 +0800)
To support channel switch on STA mode, declare IEEE80211_HW_CHANCTX_STA_CSA
and implement ieee80211_ops::switch_vif_chanctx. Handling of CSA procedure
still relies on mac80211 SW flow, since FW doesn't support chanctx offload.
To support channel switch on AP mode, declare WIPHY_FLAG_HAS_CHANNEL_SWITCH
and implement ieee80211_ops::channel_switch_beacon additionally.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250605114207.12381-4-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/chan.c
drivers/net/wireless/realtek/rtw89/chan.h
drivers/net/wireless/realtek/rtw89/core.c
drivers/net/wireless/realtek/rtw89/core.h
drivers/net/wireless/realtek/rtw89/mac80211.c

index 31e15f472740a62f449197deae6ab4a925b4da1d..b2bc650a911bcd0a866026c731c9284efe0b592a 100644 (file)
@@ -2912,3 +2912,35 @@ out:
                break;
        }
 }
+
+int rtw89_chanctx_ops_reassign_vif(struct rtw89_dev *rtwdev,
+                                  struct rtw89_vif_link *rtwvif_link,
+                                  struct ieee80211_chanctx_conf *old_ctx,
+                                  struct ieee80211_chanctx_conf *new_ctx,
+                                  bool replace)
+{
+       int ret;
+
+       rtw89_chanctx_ops_unassign_vif(rtwdev, rtwvif_link, old_ctx);
+
+       if (!replace)
+               goto assign;
+
+       rtw89_chanctx_ops_remove(rtwdev, old_ctx);
+       ret = rtw89_chanctx_ops_add(rtwdev, new_ctx);
+       if (ret) {
+               rtw89_err(rtwdev, "%s: failed to add chanctx: %d\n",
+                         __func__, ret);
+               return ret;
+       }
+
+assign:
+       ret = rtw89_chanctx_ops_assign_vif(rtwdev, rtwvif_link, new_ctx);
+       if (ret) {
+               rtw89_err(rtwdev, "%s: failed to assign chanctx: %d\n",
+                         __func__, ret);
+               return ret;
+       }
+
+       return 0;
+}
index c75260eca71d1bfd31a0ac19f5905bf032e4d7ae..9c5e61ccab88f1977979bede4db24400b4375617 100644 (file)
@@ -143,5 +143,10 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev,
 void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev,
                                    struct rtw89_vif_link *rtwvif_link,
                                    struct ieee80211_chanctx_conf *ctx);
+int rtw89_chanctx_ops_reassign_vif(struct rtw89_dev *rtwdev,
+                                  struct rtw89_vif_link *rtwvif_link,
+                                  struct ieee80211_chanctx_conf *old_ctx,
+                                  struct ieee80211_chanctx_conf *new_ctx,
+                                  bool replace);
 
 #endif
index d45a0b8134163ff67243886ec54a27ab6e8167d6..0a4c420000b853cbd2cba76be2817b08623e1290 100644 (file)
@@ -204,6 +204,7 @@ static const struct ieee80211_iface_combination rtw89_iface_combs[] = {
 };
 
 static const u8 rtw89_ext_capa_sta[] = {
+       [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
        [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
        [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF,
 };
@@ -4655,6 +4656,43 @@ void rtw89_core_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work)
        rtw89_chip_h2c_update_beacon(rtwdev, rtwvif_link);
 }
 
+void rtw89_core_csa_beacon_work(struct wiphy *wiphy, struct wiphy_work *work)
+{
+       struct rtw89_vif_link *rtwvif_link =
+               container_of(work, struct rtw89_vif_link, csa_beacon_work.work);
+       struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;
+       struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+       struct rtw89_dev *rtwdev = rtwvif->rtwdev;
+       struct ieee80211_bss_conf *bss_conf;
+       unsigned int delay;
+
+       lockdep_assert_wiphy(wiphy);
+
+       if (rtwvif_link->net_type != RTW89_NET_TYPE_AP_MODE)
+               return;
+
+       rcu_read_lock();
+
+       bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true);
+       if (!bss_conf->csa_active) {
+               rcu_read_unlock();
+               return;
+       }
+
+       delay = ieee80211_tu_to_usec(bss_conf->beacon_int);
+
+       rcu_read_unlock();
+
+       if (!ieee80211_beacon_cntdwn_is_complete(vif, rtwvif_link->link_id)) {
+               rtw89_chip_h2c_update_beacon(rtwdev, rtwvif_link);
+
+               wiphy_delayed_work_queue(wiphy, &rtwvif_link->csa_beacon_work,
+                                        usecs_to_jiffies(delay));
+       } else {
+               ieee80211_csa_finish(vif, rtwvif_link->link_id);
+       }
+}
+
 int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond)
 {
        struct completion *cmpl = &wait->completion;
@@ -5505,6 +5543,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
        ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
        ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
        ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+       ieee80211_hw_set(hw, CHANCTX_STA_CSA);
 
        if (chip->support_bandwidths & BIT(NL80211_CHAN_WIDTH_160))
                ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
@@ -5531,6 +5570,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
        hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
                            WIPHY_FLAG_TDLS_EXTERNAL_SETUP |
                            WIPHY_FLAG_AP_UAPSD |
+                           WIPHY_FLAG_HAS_CHANNEL_SWITCH |
                            WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK;
 
        if (!chip->support_rnr)
index a081fce1466f3530e33fd4dad8ff70e93cad4f02..ddd207326503bab80d7fd0e2fd13c065e7543cfd 100644 (file)
@@ -3533,6 +3533,7 @@ struct rtw89_vif_link {
        bool pwr_diff_en;
        u8 def_tri_idx;
        struct wiphy_work update_beacon_work;
+       struct wiphy_delayed_work csa_beacon_work;
        struct rtw89_addr_cam_entry addr_cam;
        struct rtw89_bssid_cam_entry bssid_cam;
        struct ieee80211_tx_queue_params tx_params[IEEE80211_NUM_ACS];
@@ -7317,6 +7318,7 @@ void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond,
 int rtw89_core_start(struct rtw89_dev *rtwdev);
 void rtw89_core_stop(struct rtw89_dev *rtwdev);
 void rtw89_core_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work);
+void rtw89_core_csa_beacon_work(struct wiphy *wiphy, struct wiphy_work *work);
 void rtw89_roc_work(struct wiphy *wiphy, struct wiphy_work *work);
 void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif);
 void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif);
index a8e8b098cc9ccf9279a0d64f5f2db858146e5773..7dc91c06397982eedee848285b8253317eb4ffeb 100644 (file)
@@ -112,6 +112,8 @@ static int __rtw89_ops_add_iface_link(struct rtw89_dev *rtwdev,
        rtw89_vif_type_mapping(rtwvif_link, false);
 
        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);
+
        INIT_LIST_HEAD(&rtwvif_link->general_pkt_list);
 
        rtw89_p2p_noa_once_init(rtwvif_link);
@@ -144,6 +146,7 @@ static void __rtw89_ops_remove_iface_link(struct rtw89_dev *rtwdev,
        lockdep_assert_wiphy(rtwdev->hw->wiphy);
 
        wiphy_work_cancel(rtwdev->hw->wiphy, &rtwvif_link->update_beacon_work);
+       wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwvif_link->csa_beacon_work);
 
        rtw89_p2p_noa_once_deinit(rtwvif_link);
 
@@ -1354,6 +1357,73 @@ static void rtw89_ops_unassign_vif_chanctx(struct ieee80211_hw *hw,
        rtw89_chanctx_ops_unassign_vif(rtwdev, rtwvif_link, ctx);
 }
 
+static
+int rtw89_ops_switch_vif_chanctx(struct ieee80211_hw *hw,
+                                struct ieee80211_vif_chanctx_switch *vifs,
+                                int n_vifs,
+                                enum ieee80211_chanctx_switch_mode mode)
+{
+       struct rtw89_dev *rtwdev = hw->priv;
+       bool replace;
+       int ret;
+       int i;
+
+       lockdep_assert_wiphy(hw->wiphy);
+
+       switch (mode) {
+       case CHANCTX_SWMODE_REASSIGN_VIF:
+               replace = false;
+               break;
+       case CHANCTX_SWMODE_SWAP_CONTEXTS:
+               replace = true;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       for (i = 0; i < n_vifs; i++) {
+               struct ieee80211_vif_chanctx_switch *p = &vifs[i];
+               struct ieee80211_bss_conf *link_conf = p->link_conf;
+               struct rtw89_vif *rtwvif = vif_to_rtwvif(p->vif);
+               struct rtw89_vif_link *rtwvif_link;
+
+               rtwvif_link = rtwvif->links[link_conf->link_id];
+               if (unlikely(!rtwvif_link)) {
+                       rtw89_err(rtwdev,
+                                 "%s: rtwvif link (link_id %u) is not active\n",
+                                 __func__, link_conf->link_id);
+                       return -ENOLINK;
+               }
+
+               ret = rtw89_chanctx_ops_reassign_vif(rtwdev, rtwvif_link,
+                                                    p->old_ctx, p->new_ctx,
+                                                    replace);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void rtw89_ops_channel_switch_beacon(struct ieee80211_hw *hw,
+                                           struct ieee80211_vif *vif,
+                                           struct cfg80211_chan_def *chandef)
+{
+       struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
+       struct rtw89_dev *rtwdev = hw->priv;
+       struct rtw89_vif_link *rtwvif_link;
+
+       BUILD_BUG_ON(RTW89_MLD_NON_STA_LINK_NUM != 1);
+
+       rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0);
+       if (unlikely(!rtwvif_link)) {
+               rtw89_err(rtwdev, "chsw bcn: find no link on HW-0\n");
+               return;
+       }
+
+       wiphy_delayed_work_queue(hw->wiphy, &rtwvif_link->csa_beacon_work, 0);
+}
+
 static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif,
                                       struct ieee80211_channel *chan,
@@ -1805,6 +1875,8 @@ const struct ieee80211_ops rtw89_ops = {
        .change_chanctx         = rtw89_ops_change_chanctx,
        .assign_vif_chanctx     = rtw89_ops_assign_vif_chanctx,
        .unassign_vif_chanctx   = rtw89_ops_unassign_vif_chanctx,
+       .switch_vif_chanctx     = rtw89_ops_switch_vif_chanctx,
+       .channel_switch_beacon  = rtw89_ops_channel_switch_beacon,
        .remain_on_channel              = rtw89_ops_remain_on_channel,
        .cancel_remain_on_channel       = rtw89_ops_cancel_remain_on_channel,
        .set_sar_specs          = rtw89_ops_set_sar_specs,