blob: 0e99f390399fa194ca2739cd2c708b20cb328ccb [file] [log] [blame]
From d225140776409dcf5b526034682768803e551dbb Mon Sep 17 00:00:00 2001
From: Shayne Chen <shayne.chen@mediatek.com>
Date: Mon, 6 Nov 2023 16:17:11 +0800
Subject: [PATCH 083/116] wifi: mt76: mt7996: implement and switch to chanctx
callbacks
To support MLO, chanctx callbacks are mandatory, since one VIF (MLD) could
operate on multiple channels (links).
This is a preliminary patch to add MLO support for mt7996 chipsets.
Co-developed-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
---
mt7996/init.c | 22 ++++++
mt7996/mac.c | 10 ++-
mt7996/main.c | 178 ++++++++++++++++++++++++++++++++++++++++++++----
mt7996/mcu.c | 2 +-
mt7996/mt7996.h | 17 +++++
5 files changed, 211 insertions(+), 18 deletions(-)
diff --git a/mt7996/init.c b/mt7996/init.c
index 9523568..6eeec3b 100644
--- a/mt7996/init.c
+++ b/mt7996/init.c
@@ -382,6 +382,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
hw->sta_data_size = sizeof(struct mt7996_sta);
hw->vif_data_size = sizeof(struct mt7996_vif);
+ hw->chanctx_data_size = sizeof(struct mt7996_chanctx);
wiphy->iface_combinations = if_comb;
wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
@@ -417,6 +418,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
ieee80211_hw_set(hw, WANT_MONITOR_VIF);
ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
+ ieee80211_hw_set(hw, CHANCTX_STA_CSA);
hw->max_tx_fragments = 4;
@@ -637,6 +639,22 @@ static int mt7996_vow_init(struct mt7996_phy *phy)
return mt7996_mcu_set_vow_feature_ctrl(phy);
}
+static void mt7996_init_chanctx(struct mt7996_phy *phy)
+{
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+
+ if (phy->mt76->band_idx == MT_BAND2)
+ sband = &phy->mt76->sband_6g.sband;
+ else if (phy->mt76->band_idx == MT_BAND1)
+ sband = &phy->mt76->sband_5g.sband;
+ else
+ sband = &phy->mt76->sband_2g.sband;
+
+ chan = &sband->channels[0];
+ cfg80211_chandef_create(&phy->mt76->chandef, chan, NL80211_CHAN_HT20);
+}
+
static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
enum mt76_band_id band)
{
@@ -720,6 +738,8 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
if (ret)
goto error;
+ mt7996_init_chanctx(phy);
+
ret = mt7996_thermal_init(phy);
if (ret)
goto error;
@@ -1609,6 +1629,8 @@ int mt7996_register_device(struct mt7996_dev *dev)
if (ret)
return ret;
+ mt7996_init_chanctx(&dev->phy);
+
ret = mt7996_thermal_init(&dev->phy);
if (ret)
return ret;
diff --git a/mt7996/mac.c b/mt7996/mac.c
index 4e9dd2c..0879c77 100644
--- a/mt7996/mac.c
+++ b/mt7996/mac.c
@@ -2785,7 +2785,10 @@ void mt7996_scan_work(struct work_struct *work)
mt7996_scan_complete(phy, false);
mutex_unlock(&phy->dev->mt76.mutex);
- mt7996_set_channel(phy, &hw->conf.chandef);
+ if (phy->chanctx)
+ mt7996_set_channel(phy, &phy->chanctx->chandef);
+ else
+ mt7996_set_channel(phy, &phy->mt76->chandef);
return;
}
@@ -2797,7 +2800,10 @@ void mt7996_scan_work(struct work_struct *work)
phy->scan_chan = NULL;
mutex_unlock(&phy->dev->mt76.mutex);
- mt7996_set_channel(phy, &phy->mt76->chandef);
+ if (phy->chanctx)
+ mt7996_set_channel(phy, &phy->chanctx->chandef);
+ else
+ mt7996_set_channel(phy, &phy->mt76->chandef);
ieee80211_queue_delayed_work(hw, &phy->scan_work, HZ / 10);
diff --git a/mt7996/main.c b/mt7996/main.c
index 5c1735e..dff0f1f 100644
--- a/mt7996/main.c
+++ b/mt7996/main.c
@@ -76,6 +76,11 @@ int mt7996_run(struct ieee80211_hw *hw)
if (ret)
goto out;
+ /* set a parking channel */
+ ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
+ if (ret)
+ goto out;
+
ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX);
if (ret)
goto out;
@@ -510,21 +515,6 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
struct mt7996_phy *phy = mt7996_hw_phy(hw);
int ret;
- if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
- if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
- ret = mt7996_mcu_edcca_enable(phy, true);
- if (ret)
- return ret;
- }
-
- ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE,
- phy->mt76->chandef.punctured);
- if (ret)
- return ret;
-
- mt7996_set_channel(phy, &hw->conf.chandef);
- }
-
if (changed & (IEEE80211_CONF_CHANGE_POWER |
IEEE80211_CONF_CHANGE_CHANNEL)) {
ret = mt7996_mcu_set_txpower_sku(phy);
@@ -1725,6 +1715,158 @@ mt7996_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
mutex_unlock(&phy->dev->mt76.mutex);
}
+static int
+mt7996_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
+{
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ int ret;
+
+ wiphy_info(hw->wiphy, "%s: add %u\n", __func__, conf->def.chan->hw_value);
+ mutex_lock(&phy->dev->mt76.mutex);
+
+ if (ctx->assigned) {
+ mutex_unlock(&phy->dev->mt76.mutex);
+ return -ENOSPC;
+ }
+
+ ctx->assigned = true;
+ ctx->chandef = conf->def;
+ ctx->phy = phy;
+ if (phy->chanctx) {
+ mutex_unlock(&phy->dev->mt76.mutex);
+ return 0;
+ }
+
+ phy->chanctx = ctx;
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+ if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
+ ret = mt7996_mcu_edcca_enable(phy, true);
+ if (ret)
+ return ret;
+ }
+
+ ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE, ctx->chandef.punctured);
+ if (ret)
+ return ret;
+
+ return mt7996_set_channel(phy, &ctx->chandef);
+}
+
+static void
+mt7996_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf)
+{
+ struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ struct mt7996_phy *phy = ctx->phy;
+
+ wiphy_info(hw->wiphy, "%s: remove %u\n", __func__, conf->def.chan->hw_value);
+ cancel_delayed_work_sync(&phy->scan_work);
+ cancel_delayed_work_sync(&phy->mt76->mac_work);
+
+ mutex_lock(&phy->dev->mt76.mutex);
+ ctx->assigned = false;
+ if (ctx == phy->chanctx)
+ phy->chanctx = NULL;
+ mutex_unlock(&phy->dev->mt76.mutex);
+}
+
+static void
+mt7996_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf,
+ u32 changed)
+{
+ struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ struct mt7996_phy *phy = ctx->phy;
+
+ wiphy_info(hw->wiphy, "%s: change %u, 0x%x\n", __func__, conf->def.chan->hw_value, changed);
+ if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) {
+ ctx->chandef = conf->def;
+
+ mt7996_set_channel(phy, &ctx->chandef);
+ }
+}
+
+static int
+mt7996_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx_conf *conf)
+{
+ struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ struct mt7996_phy *phy = ctx->phy;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+
+ wiphy_info(hw->wiphy, "Assign VIF (addr: %pM, type: %d, link_id: %d) to channel context: %d MHz\n",
+ vif->addr, vif->type, link_conf->link_id,
+ conf->def.chan->center_freq);
+
+ mutex_lock(&phy->dev->mt76.mutex);
+
+ mvif->chanctx = ctx;
+ ctx->nbss_assigned++;
+
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+ return 0;
+}
+
+static void
+mt7996_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx_conf *conf)
+{
+ struct mt7996_chanctx *ctx = mt7996_chanctx_get(conf);
+ struct mt7996_phy *phy = ctx->phy;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+
+ wiphy_info(hw->wiphy, "Remove VIF (addr: %pM, type: %d, link_id: %d) from channel context: %d MHz\n",
+ vif->addr, vif->type, link_conf->link_id,
+ conf->def.chan->center_freq);
+ cancel_delayed_work_sync(&phy->scan_work);
+
+ mutex_lock(&phy->dev->mt76.mutex);
+
+ if (test_bit(MT76_SCANNING, &phy->mt76->state))
+ mt7996_scan_complete(phy, true);
+
+ mvif->chanctx = NULL;
+ ctx->nbss_assigned--;
+
+ mutex_unlock(&phy->dev->mt76.mutex);
+}
+
+static int
+mt7996_switch_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs,
+ int n_vifs,
+ enum ieee80211_chanctx_switch_mode mode)
+{
+ struct mt7996_chanctx *old_ctx = mt7996_chanctx_get(vifs->old_ctx);
+ struct mt7996_chanctx *new_ctx = mt7996_chanctx_get(vifs->new_ctx);
+ struct mt7996_phy *phy = old_ctx->phy;
+
+ wiphy_info(hw->wiphy, "%s: old=%d, new=%d\n", __func__, vifs->old_ctx->def.chan->hw_value, vifs->new_ctx->def.chan->hw_value);
+
+ if (new_ctx->nbss_assigned && phy->chanctx == new_ctx) {
+ new_ctx->nbss_assigned += n_vifs;
+ return 0;
+ }
+
+ if (WARN_ON(old_ctx != phy->chanctx))
+ return -EINVAL;
+
+ mutex_lock(&phy->dev->mt76.mutex);
+
+ phy->chanctx = new_ctx;
+ new_ctx->assigned = true;
+ new_ctx->chandef = vifs->new_ctx->def;
+ new_ctx->phy = phy;
+ new_ctx->nbss_assigned += n_vifs;
+
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+ return mt7996_set_channel(phy, &new_ctx->chandef);
+}
+
const struct ieee80211_ops mt7996_ops = {
.tx = mt7996_tx,
.start = mt7996_start,
@@ -1775,4 +1917,10 @@ const struct ieee80211_ops mt7996_ops = {
.net_fill_forward_path = mt7996_net_fill_forward_path,
.net_setup_tc = mt76_wed_net_setup_tc,
#endif
+ .add_chanctx = mt7996_add_chanctx,
+ .remove_chanctx = mt7996_remove_chanctx,
+ .change_chanctx = mt7996_change_chanctx,
+ .assign_vif_chanctx = mt7996_assign_vif_chanctx,
+ .unassign_vif_chanctx = mt7996_unassign_vif_chanctx,
+ .switch_vif_chanctx = mt7996_switch_vif_chanctx,
};
diff --git a/mt7996/mcu.c b/mt7996/mcu.c
index 9f2d125..5319cc3 100644
--- a/mt7996/mcu.c
+++ b/mt7996/mcu.c
@@ -5309,7 +5309,7 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
.bitmap = cpu_to_le16(bitmap),
};
- if (phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ ||
+ if (phy->chanctx->chandef.chan->band == NL80211_BAND_2GHZ ||
mode > PP_USR_MODE)
return 0;
diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
index fbf1e83..84aafb4 100644
--- a/mt7996/mt7996.h
+++ b/mt7996/mt7996.h
@@ -332,6 +332,8 @@ struct mt7996_vif {
struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
struct cfg80211_bitrate_mask bitrate_mask;
+
+ struct mt7996_chanctx *chanctx;
};
/* crash-dump */
@@ -421,6 +423,14 @@ struct mt7996_rro_ba_session {
u32 last_in_rxtime :12;
};
+struct mt7996_chanctx {
+ struct cfg80211_chan_def chandef;
+ struct mt7996_phy *phy;
+
+ bool assigned;
+ u8 nbss_assigned;
+};
+
struct mt7996_phy {
struct mt76_phy *mt76;
struct mt7996_dev *dev;
@@ -464,6 +474,7 @@ struct mt7996_phy {
struct cfg80211_scan_request *scan_req;
struct ieee80211_vif *scan_vif;
int scan_chan_idx;
+ struct mt7996_chanctx *chanctx;
struct mt7996_scs_ctrl scs_ctrl;
u32 red_drop;
@@ -752,6 +763,12 @@ mt7996_get_background_radar_cap(struct mt7996_dev *dev)
return 1;
}
+static inline struct mt7996_chanctx *
+mt7996_chanctx_get(struct ieee80211_chanctx_conf *ctx)
+{
+ return (struct mt7996_chanctx *)&ctx->drv_priv;
+}
+
extern const struct ieee80211_ops mt7996_ops;
extern struct pci_driver mt7996_pci_driver;
extern struct pci_driver mt7996_hif_driver;
--
2.18.0