| From 8d5864d26f56f7ca0d137271701ee443c3330489 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 076/193] mtk: 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. |
| |
| Change-Id: Ie4530a3bc2ac9e51045184d5aecca14118177042 |
| 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 441300a..c6ca865 100644 |
| --- a/mt7996/init.c |
| +++ b/mt7996/init.c |
| @@ -381,6 +381,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); |
| @@ -416,6 +417,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; |
| |
| @@ -636,6 +638,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) |
| { |
| @@ -719,6 +737,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; |
| @@ -1626,6 +1646,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 5c29fe7..7cfc25b 100644 |
| --- a/mt7996/mac.c |
| +++ b/mt7996/mac.c |
| @@ -2797,7 +2797,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; |
| } |
| @@ -2809,7 +2812,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 c6dfefd..54a179c 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; |
| @@ -508,21 +513,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); |
| @@ -1722,6 +1712,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 = { |
| .add_chanctx = ieee80211_emulate_add_chanctx, |
| .remove_chanctx = ieee80211_emulate_remove_chanctx, |
| @@ -1776,4 +1918,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 a54f645..b8bfcbf 100644 |
| --- a/mt7996/mcu.c |
| +++ b/mt7996/mcu.c |
| @@ -5322,7 +5322,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 87b166a..0779025 100644 |
| --- a/mt7996/mt7996.h |
| +++ b/mt7996/mt7996.h |
| @@ -334,6 +334,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 */ |
| @@ -423,6 +425,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; |
| @@ -466,6 +476,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; |
| @@ -757,6 +768,12 @@ mt7996_has_background_radar(struct mt7996_dev *dev) |
| return true; |
| } |
| |
| +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.45.2 |
| |