wifi: mt76: mt7921: introduce CSA support
authorLeon Yen <leon.yen@mediatek.com>
Thu, 7 Nov 2024 06:14:40 +0000 (14:14 +0800)
committerFelix Fietkau <nbd@nbd.name>
Tue, 14 Jan 2025 12:34:35 +0000 (13:34 +0100)
Add CSA (Channel Switch Announcement) related implementation
in collaboration with mac80211 to deal with dynamic channel
switching.

Signed-off-by: Leon Yen <leon.yen@mediatek.com>
Signed-off-by: Ming Yen Hsieh <mingyen.hsieh@mediatek.com>
Link: https://patch.msgid.link/20241107061440.6545-1-mingyen.hsieh@mediatek.com
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt7921/main.c
drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
drivers/net/wireless/mediatek/mt76/mt792x.h
drivers/net/wireless/mediatek/mt76/mt792x_core.c

index 1542c762e2a60d724dc94f9f67ae70a3f89f877d..c77ead405a7fa2e42282730a84d968e5e09dd409 100644 (file)
@@ -339,6 +339,9 @@ mt7921_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
        if (phy->chip_cap & MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN)
                vif->driver_flags |= IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+
+       INIT_WORK(&mvif->csa_work, mt7921_csa_work);
+       timer_setup(&mvif->csa_timer, mt792x_csa_timer, 0);
 out:
        mt792x_mutex_release(dev);
 
@@ -1342,6 +1345,9 @@ static int
 mt7921_add_chanctx(struct ieee80211_hw *hw,
                   struct ieee80211_chanctx_conf *ctx)
 {
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+       dev->new_ctx = ctx;
        return 0;
 }
 
@@ -1349,6 +1355,10 @@ static void
 mt7921_remove_chanctx(struct ieee80211_hw *hw,
                      struct ieee80211_chanctx_conf *ctx)
 {
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+       if (dev->new_ctx == ctx)
+               dev->new_ctx = NULL;
 }
 
 static void
@@ -1399,6 +1409,89 @@ static void mt7921_mgd_complete_tx(struct ieee80211_hw *hw,
        mt7921_abort_roc(mvif->phy, mvif);
 }
 
+static int mt7921_switch_vif_chanctx(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif_chanctx_switch *vifs,
+                                    int n_vifs,
+                                    enum ieee80211_chanctx_switch_mode mode)
+{
+       return mt792x_assign_vif_chanctx(hw, vifs->vif, vifs->link_conf,
+                                        vifs->new_ctx);
+}
+
+void mt7921_csa_work(struct work_struct *work)
+{
+       struct mt792x_vif *mvif;
+       struct mt792x_dev *dev;
+       struct ieee80211_vif *vif;
+       int ret;
+
+       mvif = (struct mt792x_vif *)container_of(work, struct mt792x_vif,
+                                               csa_work);
+       dev = mvif->phy->dev;
+       vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv);
+
+       mt792x_mutex_acquire(dev);
+       ret = mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->bss_conf.mt76,
+                                           dev->new_ctx);
+       mt792x_mutex_release(dev);
+
+       ieee80211_chswitch_done(vif, !ret, 0);
+}
+
+static int mt7921_pre_channel_switch(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif,
+                                    struct ieee80211_channel_switch *chsw)
+{
+       if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc)
+               return -EOPNOTSUPP;
+
+       /* Avoid beacon loss due to the CAC(Channel Availability Check) time
+        * of the AP.
+        */
+       if (!cfg80211_chandef_usable(hw->wiphy, &chsw->chandef,
+                                    IEEE80211_CHAN_RADAR))
+               return -EOPNOTSUPP;
+
+       return 0;
+}
+
+static void mt7921_channel_switch(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif *vif,
+                                 struct ieee80211_channel_switch *chsw)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       u16 beacon_interval = vif->bss_conf.beacon_int;
+
+       mvif->csa_timer.expires = TU_TO_EXP_TIME(beacon_interval * chsw->count);
+       add_timer(&mvif->csa_timer);
+}
+
+static void mt7921_abort_channel_switch(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_bss_conf *link_conf)
+{
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+
+       del_timer_sync(&mvif->csa_timer);
+       cancel_work_sync(&mvif->csa_work);
+}
+
+static void mt7921_channel_switch_rx_beacon(struct ieee80211_hw *hw,
+                                           struct ieee80211_vif *vif,
+                                           struct ieee80211_channel_switch *chsw)
+{
+       struct mt792x_dev *dev = mt792x_hw_dev(hw);
+       struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+       u16 beacon_interval = vif->bss_conf.beacon_int;
+
+       if (cfg80211_chandef_identical(&chsw->chandef,
+                                      &dev->new_ctx->def) &&
+                                      chsw->count) {
+               mod_timer(&mvif->csa_timer,
+                         TU_TO_EXP_TIME(beacon_interval * chsw->count));
+       }
+}
+
 const struct ieee80211_ops mt7921_ops = {
        .tx = mt792x_tx,
        .start = mt7921_start,
@@ -1458,6 +1551,11 @@ const struct ieee80211_ops mt7921_ops = {
        .unassign_vif_chanctx = mt792x_unassign_vif_chanctx,
        .mgd_prepare_tx = mt7921_mgd_prepare_tx,
        .mgd_complete_tx = mt7921_mgd_complete_tx,
+       .switch_vif_chanctx = mt7921_switch_vif_chanctx,
+       .pre_channel_switch = mt7921_pre_channel_switch,
+       .channel_switch = mt7921_channel_switch,
+       .abort_channel_switch = mt7921_abort_channel_switch,
+       .channel_switch_rx_beacon = mt7921_channel_switch_rx_beacon,
 };
 EXPORT_SYMBOL_GPL(mt7921_ops);
 
index 16c89815c0b8a324ebbf35fc5deb04773ccb930b..e5f83fd3cc5d27e87968ff970c0e3316d27dfacb 100644 (file)
@@ -273,6 +273,7 @@ int mt7921_mcu_uni_rx_ba(struct mt792x_dev *dev,
                         bool enable);
 void mt7921_scan_work(struct work_struct *work);
 void mt7921_roc_work(struct work_struct *work);
+void mt7921_csa_work(struct work_struct *work);
 int mt7921_mcu_uni_bss_ps(struct mt792x_dev *dev, struct ieee80211_vif *vif);
 void mt7921_coredump_work(struct work_struct *work);
 int mt7921_get_txpwr_info(struct mt792x_dev *dev, struct mt7921_txpwr *txpwr);
index cd497491cfc11f239ae2949ba1e9e67209787cca..2667cd939ee5548eef80017e52daf74b2a768a95 100644 (file)
@@ -133,6 +133,9 @@ struct mt792x_vif {
        struct mt792x_phy *phy;
        u16 valid_links;
        u8 deflink_id;
+
+       struct work_struct csa_work;
+       struct timer_list csa_timer;
 };
 
 struct mt792x_phy {
@@ -237,6 +240,8 @@ struct mt792x_dev {
        enum environment_cap country_ie_env;
        u32 backup_l1;
        u32 backup_l2;
+
+       struct ieee80211_chanctx_conf *new_ctx;
 };
 
 static inline struct mt792x_bss_conf *
@@ -369,6 +374,7 @@ void mt792x_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                    u64 timestamp);
 void mt792x_tx_worker(struct mt76_worker *w);
 void mt792x_roc_timer(struct timer_list *timer);
+void mt792x_csa_timer(struct timer_list *timer);
 void mt792x_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                  u32 queues, bool drop);
 int mt792x_assign_vif_chanctx(struct ieee80211_hw *hw,
index edb01941aa8296da358b73cb2f8a4a551038013d..8799627f629269bb1ea012fb6d20f9be86c960d1 100644 (file)
@@ -289,6 +289,14 @@ void mt792x_roc_timer(struct timer_list *timer)
 }
 EXPORT_SYMBOL_GPL(mt792x_roc_timer);
 
+void mt792x_csa_timer(struct timer_list *timer)
+{
+       struct mt792x_vif *mvif = from_timer(mvif, timer, csa_timer);
+
+       ieee80211_queue_work(mvif->phy->mt76->hw, &mvif->csa_work);
+}
+EXPORT_SYMBOL_GPL(mt792x_csa_timer);
+
 void mt792x_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                  u32 queues, bool drop)
 {
@@ -330,6 +338,11 @@ void mt792x_unassign_vif_chanctx(struct ieee80211_hw *hw,
        mctx->bss_conf = NULL;
        mvif->bss_conf.mt76.ctx = NULL;
        mutex_unlock(&dev->mt76.mutex);
+
+       if (vif->bss_conf.csa_active) {
+               del_timer_sync(&mvif->csa_timer);
+               cancel_work_sync(&mvif->csa_work);
+       }
 }
 EXPORT_SYMBOL_GPL(mt792x_unassign_vif_chanctx);
 
@@ -652,6 +665,7 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw)
        ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
        ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
        ieee80211_hw_set(hw, CONNECTION_MONITOR);
+       ieee80211_hw_set(hw, CHANCTX_STA_CSA);
 
        if (dev->pm.enable)
                ieee80211_hw_set(hw, CONNECTION_MONITOR);