]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: mt76: add chanctx functions for multi-channel phy support
authorFelix Fietkau <nbd@nbd.name>
Thu, 2 Jan 2025 16:34:51 +0000 (17:34 +0100)
committerFelix Fietkau <nbd@nbd.name>
Tue, 14 Jan 2025 12:42:28 +0000 (13:42 +0100)
This adds an implementation for the chanctx functions, which can be used
by multi-channel capable drivers. Preparation for adding MLO support.

Link: https://patch.msgid.link/20250102163508.52945-7-nbd@nbd.name
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/Makefile
drivers/net/wireless/mediatek/mt76/channel.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mac80211.c
drivers/net/wireless/mediatek/mt76/mt76.h
drivers/net/wireless/mediatek/mt76/scan.c

index f6e4de9334bfebb7781cf426c034bbaba67b7626..87512d101a919ba65ae191c20b7870e06c458f06 100644 (file)
@@ -10,7 +10,7 @@ obj-$(CONFIG_MT792x_USB) += mt792x-usb.o
 
 mt76-y := \
        mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o \
-       tx.o agg-rx.o mcu.o wed.o scan.o
+       tx.o agg-rx.o mcu.o wed.o scan.o channel.o
 
 mt76-$(CONFIG_PCI) += pci.o
 mt76-$(CONFIG_NL80211_TESTMODE) += testmode.o
diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c
new file mode 100644 (file)
index 0000000..1cfeb09
--- /dev/null
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2024 Felix Fietkau <nbd@nbd.name>
+ */
+#include "mt76.h"
+
+static int
+mt76_phy_update_channel(struct mt76_phy *phy,
+                       struct ieee80211_chanctx_conf *conf)
+{
+       phy->radar_enabled = conf->radar_enabled;
+       phy->main_chandef = conf->def;
+       phy->chanctx = (struct mt76_chanctx *)conf->drv_priv;
+
+       return __mt76_set_channel(phy, &phy->main_chandef, false);
+}
+
+int mt76_add_chanctx(struct ieee80211_hw *hw,
+                    struct ieee80211_chanctx_conf *conf)
+{
+       struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
+       struct mt76_phy *phy = hw->priv;
+       struct mt76_dev *dev = phy->dev;
+       int ret = -EINVAL;
+
+       phy = ctx->phy = dev->band_phys[conf->def.chan->band];
+       if (WARN_ON_ONCE(!phy))
+               return ret;
+
+       if (dev->scan.phy == phy)
+               mt76_abort_scan(dev);
+
+       mutex_lock(&dev->mutex);
+       if (!phy->chanctx)
+               ret = mt76_phy_update_channel(phy, conf);
+       else
+               ret = 0;
+       mutex_unlock(&dev->mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(mt76_add_chanctx);
+
+void mt76_remove_chanctx(struct ieee80211_hw *hw,
+                        struct ieee80211_chanctx_conf *conf)
+{
+       struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
+       struct mt76_phy *phy = hw->priv;
+       struct mt76_dev *dev = phy->dev;
+
+       phy = ctx->phy;
+       if (WARN_ON_ONCE(!phy))
+               return;
+
+       if (dev->scan.phy == phy)
+               mt76_abort_scan(dev);
+
+       mutex_lock(&dev->mutex);
+       if (phy->chanctx == ctx)
+               phy->chanctx = NULL;
+       mutex_unlock(&dev->mutex);
+}
+EXPORT_SYMBOL_GPL(mt76_remove_chanctx);
+
+void mt76_change_chanctx(struct ieee80211_hw *hw,
+                        struct ieee80211_chanctx_conf *conf,
+                        u32 changed)
+{
+       struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
+       struct mt76_phy *phy = ctx->phy;
+       struct mt76_dev *dev = phy->dev;
+
+       if (!(changed & (IEEE80211_CHANCTX_CHANGE_WIDTH |
+                        IEEE80211_CHANCTX_CHANGE_RADAR)))
+               return;
+
+       cancel_delayed_work_sync(&phy->mac_work);
+
+       mutex_lock(&dev->mutex);
+       mt76_phy_update_channel(phy, conf);
+       mutex_unlock(&dev->mutex);
+}
+EXPORT_SYMBOL_GPL(mt76_change_chanctx);
+
+
+int mt76_assign_vif_chanctx(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif,
+                           struct ieee80211_bss_conf *link_conf,
+                           struct ieee80211_chanctx_conf *conf)
+{
+       struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
+       struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
+       struct mt76_vif_data *mvif = mlink->mvif;
+       int link_id = link_conf->link_id;
+       struct mt76_phy *phy = ctx->phy;
+       struct mt76_dev *dev = phy->dev;
+       bool mlink_alloc = false;
+       int ret = 0;
+
+       if (dev->scan.vif == vif)
+               mt76_abort_scan(dev);
+
+       mutex_lock(&dev->mutex);
+
+       if (vif->type == NL80211_IFTYPE_MONITOR &&
+           is_zero_ether_addr(vif->addr))
+               goto out;
+
+       mlink = mt76_vif_conf_link(dev, vif, link_conf);
+       if (!mlink) {
+               mlink = kzalloc(dev->drv->link_data_size, GFP_KERNEL);
+               if (!mlink) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               mlink_alloc = true;
+       }
+
+       mlink->ctx = conf;
+       ret = dev->drv->vif_link_add(phy, vif, link_conf, mlink);
+       if (ret) {
+               if (mlink_alloc)
+                       kfree(mlink);
+               goto out;
+       }
+
+       if (link_conf != &vif->bss_conf)
+               rcu_assign_pointer(mvif->link[link_id], mlink);
+
+out:
+       mutex_unlock(&dev->mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(mt76_assign_vif_chanctx);
+
+void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif,
+                              struct ieee80211_bss_conf *link_conf,
+                              struct ieee80211_chanctx_conf *conf)
+{
+       struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
+       struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
+       struct mt76_vif_data *mvif = mlink->mvif;
+       int link_id = link_conf->link_id;
+       struct mt76_phy *phy = ctx->phy;
+       struct mt76_dev *dev = phy->dev;
+
+       if (dev->scan.vif == vif)
+               mt76_abort_scan(dev);
+
+       mutex_lock(&dev->mutex);
+
+       if (vif->type == NL80211_IFTYPE_MONITOR &&
+           is_zero_ether_addr(vif->addr))
+               goto out;
+
+       mlink = mt76_vif_conf_link(dev, vif, link_conf);
+       if (!mlink)
+               goto out;
+
+       if (link_conf != &vif->bss_conf)
+               rcu_assign_pointer(mvif->link[link_id], NULL);
+
+       dev->drv->vif_link_remove(phy, vif, link_conf, mlink);
+       mlink->ctx = NULL;
+
+       if (link_conf != &vif->bss_conf)
+               kfree_rcu(mlink, rcu_head);
+
+out:
+       mutex_unlock(&dev->mutex);
+}
+EXPORT_SYMBOL_GPL(mt76_unassign_vif_chanctx);
+
+int mt76_switch_vif_chanctx(struct ieee80211_hw *hw,
+                           struct ieee80211_vif_chanctx_switch *vifs,
+                           int n_vifs,
+                           enum ieee80211_chanctx_switch_mode mode)
+{
+       struct mt76_chanctx *old_ctx = (struct mt76_chanctx *)vifs->old_ctx->drv_priv;
+       struct mt76_chanctx *new_ctx = (struct mt76_chanctx *)vifs->new_ctx->drv_priv;
+       struct ieee80211_chanctx_conf *conf = vifs->new_ctx;
+       struct mt76_phy *old_phy = old_ctx->phy;
+       struct mt76_phy *phy = hw->priv;
+       struct mt76_dev *dev = phy->dev;
+       struct mt76_vif_link *mlink;
+       bool update_chan;
+       int i, ret = 0;
+
+       if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS)
+               phy = new_ctx->phy = dev->band_phys[conf->def.chan->band];
+       else
+               phy = new_ctx->phy;
+       if (!phy)
+               return -EINVAL;
+
+       update_chan = phy->chanctx != new_ctx;
+       if (update_chan) {
+               if (dev->scan.phy == phy)
+                       mt76_abort_scan(dev);
+
+               cancel_delayed_work_sync(&phy->mac_work);
+       }
+
+       mutex_lock(&dev->mutex);
+
+       if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&
+           phy != old_phy && old_phy->chanctx == old_ctx)
+               old_phy->chanctx = NULL;
+
+       if (update_chan)
+               ret = mt76_phy_update_channel(phy, vifs->new_ctx);
+
+       if (ret)
+               goto out;
+
+       if (old_phy == phy)
+               goto skip_link_replace;
+
+       for (i = 0; i < n_vifs; i++) {
+               mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf);
+               if (!mlink)
+                       continue;
+
+               dev->drv->vif_link_remove(old_phy, vifs[i].vif,
+                                         vifs[i].link_conf, mlink);
+
+               ret = dev->drv->vif_link_add(phy, vifs[i].vif,
+                                            vifs[i].link_conf, mlink);
+               if (ret)
+                       goto out;
+
+       }
+
+skip_link_replace:
+       for (i = 0; i < n_vifs; i++) {
+               mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf);
+               if (!mlink)
+                       continue;
+
+               mlink->ctx = vifs->new_ctx;
+       }
+
+out:
+       mutex_unlock(&dev->mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(mt76_switch_vif_chanctx);
index 8863d60dbd9f34266f65f044c8d02df0be2ae270..9549510057fa2ed68b0ee43e40d4f827ad233362 100644 (file)
@@ -414,6 +414,7 @@ mt76_check_sband(struct mt76_phy *phy, struct mt76_sband *msband,
                cfg80211_chandef_create(&phy->chandef, &sband->channels[0],
                                        NL80211_CHAN_HT20);
                phy->chan_state = &msband->chan[0];
+               phy->dev->band_phys[band] = phy;
                return;
        }
 
@@ -959,16 +960,13 @@ void mt76_update_survey(struct mt76_phy *phy)
 }
 EXPORT_SYMBOL_GPL(mt76_update_survey);
 
-int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
-                    bool offchannel)
+int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
+                      bool offchannel)
 {
        struct mt76_dev *dev = phy->dev;
        int timeout = HZ / 5;
        int ret;
 
-       cancel_delayed_work_sync(&phy->mac_work);
-
-       mutex_lock(&dev->mutex);
        set_bit(MT76_RESET, &phy->state);
 
        mt76_worker_disable(&dev->tx_worker);
@@ -995,6 +993,19 @@ int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
        mt76_worker_enable(&dev->tx_worker);
        mt76_worker_schedule(&dev->tx_worker);
 
+       return ret;
+}
+
+int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
+                    bool offchannel)
+{
+       struct mt76_dev *dev = phy->dev;
+       int ret;
+
+       cancel_delayed_work_sync(&phy->mac_work);
+
+       mutex_lock(&dev->mutex);
+       ret = __mt76_set_channel(phy, chandef, offchannel);
        mutex_unlock(&dev->mutex);
 
        return ret;
@@ -1006,6 +1017,8 @@ int mt76_update_channel(struct mt76_phy *phy)
        struct cfg80211_chan_def *chandef = &hw->conf.chandef;
        bool offchannel = hw->conf.flags & IEEE80211_CONF_OFFCHANNEL;
 
+       phy->radar_enabled = hw->conf.radar_enabled;
+
        return mt76_set_channel(phy, chandef, offchannel);
 }
 EXPORT_SYMBOL_GPL(mt76_update_channel);
@@ -1919,7 +1932,7 @@ enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy)
            test_bit(MT76_SCANNING, &phy->state))
                return MT_DFS_STATE_DISABLED;
 
-       if (!hw->conf.radar_enabled) {
+       if (!phy->radar_enabled) {
                if ((hw->conf.flags & IEEE80211_CONF_MONITOR) &&
                    (phy->chandef.chan->flags & IEEE80211_CHAN_RADAR))
                        return MT_DFS_STATE_ACTIVE;
@@ -1933,3 +1946,13 @@ enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy)
        return MT_DFS_STATE_ACTIVE;
 }
 EXPORT_SYMBOL_GPL(mt76_phy_dfs_state);
+
+void mt76_vif_cleanup(struct mt76_dev *dev, struct ieee80211_vif *vif)
+{
+       struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
+       struct mt76_vif_data *mvif = mlink->mvif;
+
+       rcu_assign_pointer(mvif->link[0], NULL);
+       mt76_abort_scan(dev);
+}
+EXPORT_SYMBOL_GPL(mt76_vif_cleanup);
index a9537131a0741cf6a543121b9dc0610dc0809a19..307e6255f19934f08bce2879e4cfe3f4bb697d18 100644 (file)
@@ -50,6 +50,8 @@ struct mt76_dev;
 struct mt76_phy;
 struct mt76_wcid;
 struct mt76s_intr;
+struct mt76_chanctx;
+struct mt76_vif_link;
 
 struct mt76_reg_pair {
        u32 reg;
@@ -497,6 +499,8 @@ struct mt76_driver_ops {
        u16 token_size;
        u8 mcs_rates;
 
+       unsigned int link_data_size;
+
        void (*update_survey)(struct mt76_phy *phy);
        int (*set_channel)(struct mt76_phy *phy);
 
@@ -528,6 +532,15 @@ struct mt76_driver_ops {
 
        void (*sta_remove)(struct mt76_dev *dev, struct ieee80211_vif *vif,
                           struct ieee80211_sta *sta);
+
+       int (*vif_link_add)(struct mt76_phy *phy, struct ieee80211_vif *vif,
+                           struct ieee80211_bss_conf *link_conf,
+                           struct mt76_vif_link *mlink);
+
+       void (*vif_link_remove)(struct mt76_phy *phy,
+                               struct ieee80211_vif *vif,
+                               struct ieee80211_bss_conf *link_conf,
+                               struct mt76_vif_link *mlink);
 };
 
 struct mt76_channel_state {
@@ -793,6 +806,9 @@ struct mt76_phy {
        struct cfg80211_chan_def chandef;
        struct cfg80211_chan_def main_chandef;
        bool offchannel;
+       bool radar_enabled;
+
+       struct mt76_chanctx *chanctx;
 
        struct mt76_channel_state *chan_state;
        enum mt76_dfs_state dfs_state;
@@ -837,6 +853,7 @@ struct mt76_phy {
 struct mt76_dev {
        struct mt76_phy phy; /* must be first */
        struct mt76_phy *phys[__MT_MAX_BAND];
+       struct mt76_phy *band_phys[NUM_NL80211_BANDS];
 
        struct ieee80211_hw *hw;
 
@@ -1057,6 +1074,10 @@ struct mt76_ethtool_worker_info {
        int sta_count;
 };
 
+struct mt76_chanctx {
+       struct mt76_phy *phy;
+};
+
 #define CCK_RATE(_idx, _rate) {                                        \
        .bitrate = _rate,                                       \
        .flags = IEEE80211_RATE_SHORT_PREAMBLE,                 \
@@ -1480,6 +1501,25 @@ void mt76_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 void mt76_sw_scan_complete(struct ieee80211_hw *hw,
                           struct ieee80211_vif *vif);
 enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy);
+int mt76_add_chanctx(struct ieee80211_hw *hw,
+                    struct ieee80211_chanctx_conf *conf);
+void mt76_remove_chanctx(struct ieee80211_hw *hw,
+                        struct ieee80211_chanctx_conf *conf);
+void mt76_change_chanctx(struct ieee80211_hw *hw,
+                        struct ieee80211_chanctx_conf *conf,
+                        u32 changed);
+int mt76_assign_vif_chanctx(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif,
+                           struct ieee80211_bss_conf *link_conf,
+                           struct ieee80211_chanctx_conf *conf);
+void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif,
+                              struct ieee80211_bss_conf *link_conf,
+                              struct ieee80211_chanctx_conf *conf);
+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_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,
@@ -1525,6 +1565,8 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames);
 void mt76_testmode_tx_pending(struct mt76_phy *phy);
 void mt76_queue_tx_complete(struct mt76_dev *dev, struct mt76_queue *q,
                            struct mt76_queue_entry *e);
+int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
+                      bool offchannel);
 int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
                     bool offchannel);
 void mt76_scan_work(struct work_struct *work);
@@ -1777,14 +1819,7 @@ mt76_vif_init(struct ieee80211_vif *vif, struct mt76_vif_data *mvif)
        rcu_assign_pointer(mvif->link[0], mlink);
 }
 
-static inline void
-mt76_vif_cleanup(struct mt76_dev *dev, struct ieee80211_vif *vif)
-{
-       struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
-       struct mt76_vif_data *mvif = mlink->mvif;
-
-       rcu_assign_pointer(mvif->link[0], NULL);
-}
+void mt76_vif_cleanup(struct mt76_dev *dev, struct ieee80211_vif *vif);
 
 static inline struct mt76_vif_link *
 mt76_vif_link(struct mt76_dev *dev, struct ieee80211_vif *vif, int link_id)
@@ -1808,4 +1843,17 @@ mt76_vif_conf_link(struct mt76_dev *dev, struct ieee80211_vif *vif,
        return mt76_dereference(mvif->link[link_conf->link_id], dev);
 }
 
+static inline struct mt76_phy *
+mt76_vif_link_phy(struct mt76_vif_link *mlink)
+{
+       struct mt76_chanctx *ctx;
+
+       if (!mlink->ctx)
+               return NULL;
+
+       ctx = (struct mt76_chanctx *)mlink->ctx->drv_priv;
+
+       return ctx->phy;
+}
+
 #endif
index 70884faab90505ec2376f7a060ad15188413136c..d186a68b0fb80b4884d010ab8d041c42bbd99860 100644 (file)
@@ -124,6 +124,12 @@ int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        struct mt76_dev *dev = phy->dev;
        int ret = 0;
 
+       if (hw->wiphy->n_radio > 1) {
+               phy = dev->band_phys[req->req.channels[0]->band];
+               if (!phy)
+                       return -EINVAL;
+       }
+
        mutex_lock(&dev->mutex);
 
        if (dev->scan.req) {