wifi: mt76: add multi-radio remain_on_channel functions
authorFelix Fietkau <nbd@nbd.name>
Thu, 2 Jan 2025 16:34:56 +0000 (17:34 +0100)
committerFelix Fietkau <nbd@nbd.name>
Tue, 14 Jan 2025 12:42:29 +0000 (13:42 +0100)
This allows a driver using the generic channel context functions to
temporarily switch to another channel for off-channel rx/tx.

Link: https://patch.msgid.link/20250102163508.52945-12-nbd@nbd.name
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/channel.c
drivers/net/wireless/mediatek/mt76/mac80211.c
drivers/net/wireless/mediatek/mt76/mt76.h
drivers/net/wireless/mediatek/mt76/scan.c

index ddb36e958c33cd099d25384dd2b5fb54442b4079..6a35c6ebd823e28139cef512489016da99bf2f01 100644 (file)
@@ -308,3 +308,99 @@ void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif,
        dev->drv->vif_link_remove(phy, vif, &vif->bss_conf, mlink);
        kfree(mlink);
 }
+
+static void mt76_roc_complete(struct mt76_phy *phy)
+{
+       struct mt76_vif_link *mlink = phy->roc_link;
+
+       if (!phy->roc_vif)
+               return;
+
+       if (mlink)
+               mlink->mvif->roc_phy = NULL;
+       if (phy->main_chandef.chan)
+               mt76_set_channel(phy, &phy->main_chandef, false);
+       mt76_put_vif_phy_link(phy, phy->roc_vif, phy->roc_link);
+       phy->roc_vif = NULL;
+       phy->roc_link = NULL;
+       ieee80211_remain_on_channel_expired(phy->hw);
+}
+
+void mt76_roc_complete_work(struct work_struct *work)
+{
+       struct mt76_phy *phy = container_of(work, struct mt76_phy, roc_work.work);
+       struct mt76_dev *dev = phy->dev;
+
+       mutex_lock(&dev->mutex);
+       mt76_roc_complete(phy);
+       mutex_unlock(&dev->mutex);
+}
+
+void mt76_abort_roc(struct mt76_phy *phy)
+{
+       struct mt76_dev *dev = phy->dev;
+
+       cancel_delayed_work_sync(&phy->roc_work);
+
+       mutex_lock(&dev->mutex);
+       mt76_roc_complete(phy);
+       mutex_unlock(&dev->mutex);
+}
+
+int mt76_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                          struct ieee80211_channel *chan, int duration,
+                          enum ieee80211_roc_type type)
+{
+       struct cfg80211_chan_def chandef = {};
+       struct mt76_phy *phy = hw->priv;
+       struct mt76_dev *dev = phy->dev;
+       struct mt76_vif_link *mlink;
+       int ret = 0;
+
+       phy = dev->band_phys[chan->band];
+       if (!phy)
+               return -EINVAL;
+
+       mutex_lock(&dev->mutex);
+
+       if (phy->roc_vif || dev->scan.phy == phy) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       mlink = mt76_get_vif_phy_link(phy, vif);
+       if (IS_ERR(mlink)) {
+               ret = PTR_ERR(mlink);
+               goto out;
+       }
+
+       mlink->mvif->roc_phy = phy;
+       phy->roc_vif = vif;
+       phy->roc_link = mlink;
+       cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
+       mt76_set_channel(phy, &chandef, true);
+       ieee80211_ready_on_channel(hw);
+       ieee80211_queue_delayed_work(phy->hw, &phy->roc_work,
+                                    msecs_to_jiffies(duration));
+
+out:
+       mutex_unlock(&dev->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(mt76_remain_on_channel);
+
+int mt76_cancel_remain_on_channel(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif *vif)
+{
+       struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
+       struct mt76_vif_data *mvif = mlink->mvif;
+       struct mt76_phy *phy = mvif->roc_phy;
+
+       if (!phy)
+               return 0;
+
+       mt76_abort_roc(phy);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mt76_cancel_remain_on_channel);
index 590c60f308237cdcc54961f447a0af38f92e634c..508b472408c200d330b5c8678d3b50679ffcbc53 100644 (file)
@@ -431,6 +431,7 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
 
        INIT_LIST_HEAD(&phy->tx_list);
        spin_lock_init(&phy->tx_lock);
+       INIT_DELAYED_WORK(&phy->roc_work, mt76_roc_complete_work);
 
        if ((void *)phy != hw->priv)
                return 0;
@@ -1999,5 +2000,7 @@ void mt76_vif_cleanup(struct mt76_dev *dev, struct ieee80211_vif *vif)
 
        rcu_assign_pointer(mvif->link[0], NULL);
        mt76_abort_scan(dev);
+       if (mvif->roc_phy)
+               mt76_abort_roc(mvif->roc_phy);
 }
 EXPORT_SYMBOL_GPL(mt76_vif_cleanup);
index b61f1eb138e8b15be572a01ccc35c2887ee6f175..132148f7b107018a2a315b0bde8dabd1e26eb01b 100644 (file)
@@ -787,6 +787,7 @@ struct mt76_vif_link {
 struct mt76_vif_data {
        struct mt76_vif_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
 
+       struct mt76_phy *roc_phy;
        u16 valid_links;
        u8 deflink_id;
 };
@@ -809,6 +810,10 @@ struct mt76_phy {
        bool offchannel;
        bool radar_enabled;
 
+       struct delayed_work roc_work;
+       struct ieee80211_vif *roc_vif;
+       struct mt76_vif_link *roc_link;
+
        struct mt76_chanctx *chanctx;
 
        struct mt76_channel_state *chan_state;
@@ -1521,6 +1526,11 @@ int mt76_switch_vif_chanctx(struct ieee80211_hw *hw,
                            struct ieee80211_vif_chanctx_switch *vifs,
                            int n_vifs,
                            enum ieee80211_chanctx_switch_mode mode);
+int mt76_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                          struct ieee80211_channel *chan, int duration,
+                          enum ieee80211_roc_type type);
+int mt76_cancel_remain_on_channel(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif *vif);
 int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                      void *data, int len);
 int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
@@ -1572,6 +1582,8 @@ int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
                     bool offchannel);
 void mt76_scan_work(struct work_struct *work);
 void mt76_abort_scan(struct mt76_dev *dev);
+void mt76_roc_complete_work(struct work_struct *work);
+void mt76_abort_roc(struct mt76_phy *phy);
 struct mt76_vif_link *mt76_get_vif_phy_link(struct mt76_phy *phy,
                                            struct ieee80211_vif *vif);
 void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif,
index 9f3485be57479fb9cd23ccc53d60dd3fafa89892..1c4f9deaaada5e428a9aa2db5f60830842d8d72c 100644 (file)
@@ -134,7 +134,7 @@ int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 
        mutex_lock(&dev->mutex);
 
-       if (dev->scan.req) {
+       if (dev->scan.req || phy->roc_vif) {
                ret = -EBUSY;
                goto out;
        }