wifi: mt76: add multi-radio remain_on_channel functions

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>
This commit is contained in:
Felix Fietkau
2025-01-02 17:34:56 +01:00
parent e411b8190f
commit a8f424c128
4 changed files with 112 additions and 1 deletions

View 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);

View 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);

View 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,

View 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;
}