Commit a8f424c1 authored by Felix Fietkau's avatar Felix Fietkau
Browse files

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: default avatarFelix Fietkau <nbd@nbd.name>
parent e411b819
Loading
Loading
Loading
Loading
+96 −0
Original line number Diff line number Diff line
@@ -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);
+3 −0
Original line number Diff line number Diff line
@@ -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);
+12 −0
Original line number Diff line number Diff line
@@ -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,
+1 −1
Original line number Diff line number Diff line
@@ -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;
	}