Path: blob/main/sys/contrib/dev/mediatek/mt76/channel.c
48375 views
// SPDX-License-Identifier: ISC1/*2* Copyright (C) 2024 Felix Fietkau <[email protected]>3*/4#include "mt76.h"56static struct mt76_vif_link *7mt76_alloc_mlink(struct mt76_dev *dev, struct mt76_vif_data *mvif)8{9struct mt76_vif_link *mlink;1011mlink = kzalloc(dev->drv->link_data_size, GFP_KERNEL);12if (!mlink)13return NULL;1415mlink->mvif = mvif;1617return mlink;18}1920static int21mt76_phy_update_channel(struct mt76_phy *phy,22struct ieee80211_chanctx_conf *conf)23{24phy->radar_enabled = conf->radar_enabled;25phy->main_chandef = conf->def;26phy->chanctx = (struct mt76_chanctx *)conf->drv_priv;2728return __mt76_set_channel(phy, &phy->main_chandef, false);29}3031int mt76_add_chanctx(struct ieee80211_hw *hw,32struct ieee80211_chanctx_conf *conf)33{34struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;35struct mt76_phy *phy = hw->priv;36struct mt76_dev *dev = phy->dev;37int ret = -EINVAL;3839phy = ctx->phy = dev->band_phys[conf->def.chan->band];40if (WARN_ON_ONCE(!phy))41return ret;4243if (dev->scan.phy == phy)44mt76_abort_scan(dev);4546mutex_lock(&dev->mutex);47if (!phy->chanctx)48ret = mt76_phy_update_channel(phy, conf);49else50ret = 0;51mutex_unlock(&dev->mutex);5253return ret;54}55EXPORT_SYMBOL_GPL(mt76_add_chanctx);5657void mt76_remove_chanctx(struct ieee80211_hw *hw,58struct ieee80211_chanctx_conf *conf)59{60struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;61struct mt76_phy *phy = hw->priv;62struct mt76_dev *dev = phy->dev;6364phy = ctx->phy;65if (WARN_ON_ONCE(!phy))66return;6768if (dev->scan.phy == phy)69mt76_abort_scan(dev);7071mutex_lock(&dev->mutex);72if (phy->chanctx == ctx)73phy->chanctx = NULL;74mutex_unlock(&dev->mutex);75}76EXPORT_SYMBOL_GPL(mt76_remove_chanctx);7778void mt76_change_chanctx(struct ieee80211_hw *hw,79struct ieee80211_chanctx_conf *conf,80u32 changed)81{82struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;83struct mt76_phy *phy = ctx->phy;84struct mt76_dev *dev = phy->dev;8586if (!(changed & (IEEE80211_CHANCTX_CHANGE_WIDTH |87IEEE80211_CHANCTX_CHANGE_RADAR)))88return;8990cancel_delayed_work_sync(&phy->mac_work);9192mutex_lock(&dev->mutex);93mt76_phy_update_channel(phy, conf);94mutex_unlock(&dev->mutex);95}96EXPORT_SYMBOL_GPL(mt76_change_chanctx);979899int mt76_assign_vif_chanctx(struct ieee80211_hw *hw,100struct ieee80211_vif *vif,101struct ieee80211_bss_conf *link_conf,102struct ieee80211_chanctx_conf *conf)103{104struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;105struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;106struct mt76_vif_data *mvif = mlink->mvif;107int link_id = link_conf->link_id;108struct mt76_phy *phy = ctx->phy;109struct mt76_dev *dev = phy->dev;110bool mlink_alloc = false;111int ret = 0;112113if (dev->scan.vif == vif)114mt76_abort_scan(dev);115116mutex_lock(&dev->mutex);117118if (vif->type == NL80211_IFTYPE_MONITOR &&119is_zero_ether_addr(vif->addr))120goto out;121122mlink = mt76_vif_conf_link(dev, vif, link_conf);123if (!mlink) {124mlink = mt76_alloc_mlink(dev, mvif);125if (!mlink) {126ret = -ENOMEM;127goto out;128}129mlink_alloc = true;130}131132mlink->ctx = conf;133ret = dev->drv->vif_link_add(phy, vif, link_conf, mlink);134if (ret) {135if (mlink_alloc)136kfree(mlink);137goto out;138}139140if (link_conf != &vif->bss_conf)141rcu_assign_pointer(mvif->link[link_id], mlink);142143out:144mutex_unlock(&dev->mutex);145146return ret;147}148EXPORT_SYMBOL_GPL(mt76_assign_vif_chanctx);149150void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw,151struct ieee80211_vif *vif,152struct ieee80211_bss_conf *link_conf,153struct ieee80211_chanctx_conf *conf)154{155struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;156struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;157struct mt76_vif_data *mvif = mlink->mvif;158int link_id = link_conf->link_id;159struct mt76_phy *phy = ctx->phy;160struct mt76_dev *dev = phy->dev;161162if (dev->scan.vif == vif)163mt76_abort_scan(dev);164165mutex_lock(&dev->mutex);166167if (vif->type == NL80211_IFTYPE_MONITOR &&168is_zero_ether_addr(vif->addr))169goto out;170171mlink = mt76_vif_conf_link(dev, vif, link_conf);172if (!mlink)173goto out;174175if (mlink != (struct mt76_vif_link *)vif->drv_priv)176rcu_assign_pointer(mvif->link[link_id], NULL);177178dev->drv->vif_link_remove(phy, vif, link_conf, mlink);179mlink->ctx = NULL;180181if (mlink != (struct mt76_vif_link *)vif->drv_priv)182kfree_rcu(mlink, rcu_head);183184out:185mutex_unlock(&dev->mutex);186}187EXPORT_SYMBOL_GPL(mt76_unassign_vif_chanctx);188189int mt76_switch_vif_chanctx(struct ieee80211_hw *hw,190struct ieee80211_vif_chanctx_switch *vifs,191int n_vifs,192enum ieee80211_chanctx_switch_mode mode)193{194struct mt76_chanctx *old_ctx = (struct mt76_chanctx *)vifs->old_ctx->drv_priv;195struct mt76_chanctx *new_ctx = (struct mt76_chanctx *)vifs->new_ctx->drv_priv;196struct ieee80211_chanctx_conf *conf = vifs->new_ctx;197struct mt76_phy *old_phy = old_ctx->phy;198struct mt76_phy *phy = hw->priv;199struct mt76_dev *dev = phy->dev;200struct mt76_vif_link *mlink;201bool update_chan;202int i, ret = 0;203204if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS)205phy = new_ctx->phy = dev->band_phys[conf->def.chan->band];206else207phy = new_ctx->phy;208if (!phy)209return -EINVAL;210211update_chan = phy->chanctx != new_ctx;212if (update_chan) {213if (dev->scan.phy == phy)214mt76_abort_scan(dev);215216cancel_delayed_work_sync(&phy->mac_work);217}218219mutex_lock(&dev->mutex);220221if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&222phy != old_phy && old_phy->chanctx == old_ctx)223old_phy->chanctx = NULL;224225if (update_chan)226ret = mt76_phy_update_channel(phy, vifs->new_ctx);227228if (ret)229goto out;230231if (old_phy == phy)232goto skip_link_replace;233234for (i = 0; i < n_vifs; i++) {235mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf);236if (!mlink)237continue;238239dev->drv->vif_link_remove(old_phy, vifs[i].vif,240vifs[i].link_conf, mlink);241242ret = dev->drv->vif_link_add(phy, vifs[i].vif,243vifs[i].link_conf, mlink);244if (ret)245goto out;246247}248249skip_link_replace:250for (i = 0; i < n_vifs; i++) {251mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf);252if (!mlink)253continue;254255mlink->ctx = vifs->new_ctx;256}257258out:259mutex_unlock(&dev->mutex);260261return ret;262}263EXPORT_SYMBOL_GPL(mt76_switch_vif_chanctx);264265struct mt76_vif_link *mt76_get_vif_phy_link(struct mt76_phy *phy,266struct ieee80211_vif *vif)267{268struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;269struct mt76_vif_data *mvif = mlink->mvif;270struct mt76_dev *dev = phy->dev;271int i, ret;272273for (i = 0; i < ARRAY_SIZE(mvif->link); i++) {274mlink = mt76_dereference(mvif->link[i], dev);275if (!mlink)276continue;277278if (mt76_vif_link_phy(mlink) == phy)279return mlink;280}281282if (!dev->drv->vif_link_add)283return ERR_PTR(-EINVAL);284285mlink = mt76_alloc_mlink(dev, mvif);286if (!mlink)287return ERR_PTR(-ENOMEM);288289mlink->offchannel = true;290ret = dev->drv->vif_link_add(phy, vif, &vif->bss_conf, mlink);291if (ret) {292kfree(mlink);293return ERR_PTR(ret);294}295rcu_assign_pointer(mvif->offchannel_link, mlink);296297return mlink;298}299300void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif,301struct mt76_vif_link *mlink)302{303struct mt76_dev *dev = phy->dev;304struct mt76_vif_data *mvif;305306if (IS_ERR_OR_NULL(mlink) || !mlink->offchannel)307return;308309mvif = mlink->mvif;310311rcu_assign_pointer(mvif->offchannel_link, NULL);312dev->drv->vif_link_remove(phy, vif, &vif->bss_conf, mlink);313kfree(mlink);314}315316static void mt76_roc_complete(struct mt76_phy *phy)317{318struct mt76_vif_link *mlink = phy->roc_link;319320if (!phy->roc_vif)321return;322323if (mlink)324mlink->mvif->roc_phy = NULL;325if (phy->main_chandef.chan)326mt76_set_channel(phy, &phy->main_chandef, false);327mt76_put_vif_phy_link(phy, phy->roc_vif, phy->roc_link);328phy->roc_vif = NULL;329phy->roc_link = NULL;330ieee80211_remain_on_channel_expired(phy->hw);331}332333void mt76_roc_complete_work(struct work_struct *work)334{335struct mt76_phy *phy = container_of(work, struct mt76_phy, roc_work.work);336struct mt76_dev *dev = phy->dev;337338mutex_lock(&dev->mutex);339mt76_roc_complete(phy);340mutex_unlock(&dev->mutex);341}342343void mt76_abort_roc(struct mt76_phy *phy)344{345struct mt76_dev *dev = phy->dev;346347cancel_delayed_work_sync(&phy->roc_work);348349mutex_lock(&dev->mutex);350mt76_roc_complete(phy);351mutex_unlock(&dev->mutex);352}353354int mt76_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif,355struct ieee80211_channel *chan, int duration,356enum ieee80211_roc_type type)357{358struct cfg80211_chan_def chandef = {};359struct mt76_phy *phy = hw->priv;360struct mt76_dev *dev = phy->dev;361struct mt76_vif_link *mlink;362int ret = 0;363364phy = dev->band_phys[chan->band];365if (!phy)366return -EINVAL;367368mutex_lock(&dev->mutex);369370if (phy->roc_vif || dev->scan.phy == phy) {371ret = -EBUSY;372goto out;373}374375mlink = mt76_get_vif_phy_link(phy, vif);376if (IS_ERR(mlink)) {377ret = PTR_ERR(mlink);378goto out;379}380381mlink->mvif->roc_phy = phy;382phy->roc_vif = vif;383phy->roc_link = mlink;384cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);385mt76_set_channel(phy, &chandef, true);386ieee80211_ready_on_channel(hw);387ieee80211_queue_delayed_work(phy->hw, &phy->roc_work,388msecs_to_jiffies(duration));389390out:391mutex_unlock(&dev->mutex);392return ret;393}394EXPORT_SYMBOL_GPL(mt76_remain_on_channel);395396int mt76_cancel_remain_on_channel(struct ieee80211_hw *hw,397struct ieee80211_vif *vif)398{399struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;400struct mt76_vif_data *mvif = mlink->mvif;401struct mt76_phy *phy = mvif->roc_phy;402403if (!phy)404return 0;405406mt76_abort_roc(phy);407408return 0;409}410EXPORT_SYMBOL_GPL(mt76_cancel_remain_on_channel);411412413