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

wifi: mt76: add chanctx functions for multi-channel phy support

This adds an implementation for the chanctx functions, which can be used
by multi-channel capable drivers. Preparation for adding MLO support.

Link: https://patch.msgid.link/20250102163508.52945-7-nbd@nbd.name


Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
parent cbf5e61d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@ obj-$(CONFIG_MT792x_USB) += mt792x-usb.o

mt76-y := \
	mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o \
	tx.o agg-rx.o mcu.o wed.o scan.o
	tx.o agg-rx.o mcu.o wed.o scan.o channel.o

mt76-$(CONFIG_PCI) += pci.o
mt76-$(CONFIG_NL80211_TESTMODE) += testmode.o
+250 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: ISC
/*
 * Copyright (C) 2024 Felix Fietkau <nbd@nbd.name>
 */
#include "mt76.h"

static int
mt76_phy_update_channel(struct mt76_phy *phy,
			struct ieee80211_chanctx_conf *conf)
{
	phy->radar_enabled = conf->radar_enabled;
	phy->main_chandef = conf->def;
	phy->chanctx = (struct mt76_chanctx *)conf->drv_priv;

	return __mt76_set_channel(phy, &phy->main_chandef, false);
}

int mt76_add_chanctx(struct ieee80211_hw *hw,
		     struct ieee80211_chanctx_conf *conf)
{
	struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
	struct mt76_phy *phy = hw->priv;
	struct mt76_dev *dev = phy->dev;
	int ret = -EINVAL;

	phy = ctx->phy = dev->band_phys[conf->def.chan->band];
	if (WARN_ON_ONCE(!phy))
		return ret;

	if (dev->scan.phy == phy)
		mt76_abort_scan(dev);

	mutex_lock(&dev->mutex);
	if (!phy->chanctx)
		ret = mt76_phy_update_channel(phy, conf);
	else
		ret = 0;
	mutex_unlock(&dev->mutex);

	return ret;
}
EXPORT_SYMBOL_GPL(mt76_add_chanctx);

void mt76_remove_chanctx(struct ieee80211_hw *hw,
			 struct ieee80211_chanctx_conf *conf)
{
	struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
	struct mt76_phy *phy = hw->priv;
	struct mt76_dev *dev = phy->dev;

	phy = ctx->phy;
	if (WARN_ON_ONCE(!phy))
		return;

	if (dev->scan.phy == phy)
		mt76_abort_scan(dev);

	mutex_lock(&dev->mutex);
	if (phy->chanctx == ctx)
		phy->chanctx = NULL;
	mutex_unlock(&dev->mutex);
}
EXPORT_SYMBOL_GPL(mt76_remove_chanctx);

void mt76_change_chanctx(struct ieee80211_hw *hw,
			 struct ieee80211_chanctx_conf *conf,
			 u32 changed)
{
	struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
	struct mt76_phy *phy = ctx->phy;
	struct mt76_dev *dev = phy->dev;

	if (!(changed & (IEEE80211_CHANCTX_CHANGE_WIDTH |
			 IEEE80211_CHANCTX_CHANGE_RADAR)))
		return;

	cancel_delayed_work_sync(&phy->mac_work);

	mutex_lock(&dev->mutex);
	mt76_phy_update_channel(phy, conf);
	mutex_unlock(&dev->mutex);
}
EXPORT_SYMBOL_GPL(mt76_change_chanctx);


int mt76_assign_vif_chanctx(struct ieee80211_hw *hw,
			    struct ieee80211_vif *vif,
			    struct ieee80211_bss_conf *link_conf,
			    struct ieee80211_chanctx_conf *conf)
{
	struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
	struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
	struct mt76_vif_data *mvif = mlink->mvif;
	int link_id = link_conf->link_id;
	struct mt76_phy *phy = ctx->phy;
	struct mt76_dev *dev = phy->dev;
	bool mlink_alloc = false;
	int ret = 0;

	if (dev->scan.vif == vif)
		mt76_abort_scan(dev);

	mutex_lock(&dev->mutex);

	if (vif->type == NL80211_IFTYPE_MONITOR &&
	    is_zero_ether_addr(vif->addr))
		goto out;

	mlink = mt76_vif_conf_link(dev, vif, link_conf);
	if (!mlink) {
		mlink = kzalloc(dev->drv->link_data_size, GFP_KERNEL);
		if (!mlink) {
			ret = -ENOMEM;
			goto out;
		}
		mlink_alloc = true;
	}

	mlink->ctx = conf;
	ret = dev->drv->vif_link_add(phy, vif, link_conf, mlink);
	if (ret) {
		if (mlink_alloc)
			kfree(mlink);
		goto out;
	}

	if (link_conf != &vif->bss_conf)
		rcu_assign_pointer(mvif->link[link_id], mlink);

out:
	mutex_unlock(&dev->mutex);

	return ret;
}
EXPORT_SYMBOL_GPL(mt76_assign_vif_chanctx);

void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw,
			       struct ieee80211_vif *vif,
			       struct ieee80211_bss_conf *link_conf,
			       struct ieee80211_chanctx_conf *conf)
{
	struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
	struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
	struct mt76_vif_data *mvif = mlink->mvif;
	int link_id = link_conf->link_id;
	struct mt76_phy *phy = ctx->phy;
	struct mt76_dev *dev = phy->dev;

	if (dev->scan.vif == vif)
		mt76_abort_scan(dev);

	mutex_lock(&dev->mutex);

	if (vif->type == NL80211_IFTYPE_MONITOR &&
	    is_zero_ether_addr(vif->addr))
		goto out;

	mlink = mt76_vif_conf_link(dev, vif, link_conf);
	if (!mlink)
		goto out;

	if (link_conf != &vif->bss_conf)
		rcu_assign_pointer(mvif->link[link_id], NULL);

	dev->drv->vif_link_remove(phy, vif, link_conf, mlink);
	mlink->ctx = NULL;

	if (link_conf != &vif->bss_conf)
		kfree_rcu(mlink, rcu_head);

out:
	mutex_unlock(&dev->mutex);
}
EXPORT_SYMBOL_GPL(mt76_unassign_vif_chanctx);

int mt76_switch_vif_chanctx(struct ieee80211_hw *hw,
			    struct ieee80211_vif_chanctx_switch *vifs,
			    int n_vifs,
			    enum ieee80211_chanctx_switch_mode mode)
{
	struct mt76_chanctx *old_ctx = (struct mt76_chanctx *)vifs->old_ctx->drv_priv;
	struct mt76_chanctx *new_ctx = (struct mt76_chanctx *)vifs->new_ctx->drv_priv;
	struct ieee80211_chanctx_conf *conf = vifs->new_ctx;
	struct mt76_phy *old_phy = old_ctx->phy;
	struct mt76_phy *phy = hw->priv;
	struct mt76_dev *dev = phy->dev;
	struct mt76_vif_link *mlink;
	bool update_chan;
	int i, ret = 0;

	if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS)
		phy = new_ctx->phy = dev->band_phys[conf->def.chan->band];
	else
		phy = new_ctx->phy;
	if (!phy)
		return -EINVAL;

	update_chan = phy->chanctx != new_ctx;
	if (update_chan) {
		if (dev->scan.phy == phy)
			mt76_abort_scan(dev);

		cancel_delayed_work_sync(&phy->mac_work);
	}

	mutex_lock(&dev->mutex);

	if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&
	    phy != old_phy && old_phy->chanctx == old_ctx)
		old_phy->chanctx = NULL;

	if (update_chan)
		ret = mt76_phy_update_channel(phy, vifs->new_ctx);

	if (ret)
		goto out;

	if (old_phy == phy)
		goto skip_link_replace;

	for (i = 0; i < n_vifs; i++) {
		mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf);
		if (!mlink)
			continue;

		dev->drv->vif_link_remove(old_phy, vifs[i].vif,
					  vifs[i].link_conf, mlink);

		ret = dev->drv->vif_link_add(phy, vifs[i].vif,
					     vifs[i].link_conf, mlink);
		if (ret)
			goto out;

	}

skip_link_replace:
	for (i = 0; i < n_vifs; i++) {
		mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf);
		if (!mlink)
			continue;

		mlink->ctx = vifs->new_ctx;
	}

out:
	mutex_unlock(&dev->mutex);

	return ret;
}
EXPORT_SYMBOL_GPL(mt76_switch_vif_chanctx);
+29 −6
Original line number Diff line number Diff line
@@ -414,6 +414,7 @@ mt76_check_sband(struct mt76_phy *phy, struct mt76_sband *msband,
		cfg80211_chandef_create(&phy->chandef, &sband->channels[0],
					NL80211_CHAN_HT20);
		phy->chan_state = &msband->chan[0];
		phy->dev->band_phys[band] = phy;
		return;
	}

@@ -959,16 +960,13 @@ void mt76_update_survey(struct mt76_phy *phy)
}
EXPORT_SYMBOL_GPL(mt76_update_survey);

int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
		       bool offchannel)
{
	struct mt76_dev *dev = phy->dev;
	int timeout = HZ / 5;
	int ret;

	cancel_delayed_work_sync(&phy->mac_work);

	mutex_lock(&dev->mutex);
	set_bit(MT76_RESET, &phy->state);

	mt76_worker_disable(&dev->tx_worker);
@@ -995,6 +993,19 @@ int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
	mt76_worker_enable(&dev->tx_worker);
	mt76_worker_schedule(&dev->tx_worker);

	return ret;
}

int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
		     bool offchannel)
{
	struct mt76_dev *dev = phy->dev;
	int ret;

	cancel_delayed_work_sync(&phy->mac_work);

	mutex_lock(&dev->mutex);
	ret = __mt76_set_channel(phy, chandef, offchannel);
	mutex_unlock(&dev->mutex);

	return ret;
@@ -1006,6 +1017,8 @@ int mt76_update_channel(struct mt76_phy *phy)
	struct cfg80211_chan_def *chandef = &hw->conf.chandef;
	bool offchannel = hw->conf.flags & IEEE80211_CONF_OFFCHANNEL;

	phy->radar_enabled = hw->conf.radar_enabled;

	return mt76_set_channel(phy, chandef, offchannel);
}
EXPORT_SYMBOL_GPL(mt76_update_channel);
@@ -1919,7 +1932,7 @@ enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy)
	    test_bit(MT76_SCANNING, &phy->state))
		return MT_DFS_STATE_DISABLED;

	if (!hw->conf.radar_enabled) {
	if (!phy->radar_enabled) {
		if ((hw->conf.flags & IEEE80211_CONF_MONITOR) &&
		    (phy->chandef.chan->flags & IEEE80211_CHAN_RADAR))
			return MT_DFS_STATE_ACTIVE;
@@ -1933,3 +1946,13 @@ enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy)
	return MT_DFS_STATE_ACTIVE;
}
EXPORT_SYMBOL_GPL(mt76_phy_dfs_state);

void mt76_vif_cleanup(struct mt76_dev *dev, struct ieee80211_vif *vif)
{
	struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
	struct mt76_vif_data *mvif = mlink->mvif;

	rcu_assign_pointer(mvif->link[0], NULL);
	mt76_abort_scan(dev);
}
EXPORT_SYMBOL_GPL(mt76_vif_cleanup);
+56 −8
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@ struct mt76_dev;
struct mt76_phy;
struct mt76_wcid;
struct mt76s_intr;
struct mt76_chanctx;
struct mt76_vif_link;

struct mt76_reg_pair {
	u32 reg;
@@ -497,6 +499,8 @@ struct mt76_driver_ops {
	u16 token_size;
	u8 mcs_rates;

	unsigned int link_data_size;

	void (*update_survey)(struct mt76_phy *phy);
	int (*set_channel)(struct mt76_phy *phy);

@@ -528,6 +532,15 @@ struct mt76_driver_ops {

	void (*sta_remove)(struct mt76_dev *dev, struct ieee80211_vif *vif,
			   struct ieee80211_sta *sta);

	int (*vif_link_add)(struct mt76_phy *phy, struct ieee80211_vif *vif,
			    struct ieee80211_bss_conf *link_conf,
			    struct mt76_vif_link *mlink);

	void (*vif_link_remove)(struct mt76_phy *phy,
				struct ieee80211_vif *vif,
				struct ieee80211_bss_conf *link_conf,
				struct mt76_vif_link *mlink);
};

struct mt76_channel_state {
@@ -793,6 +806,9 @@ struct mt76_phy {
	struct cfg80211_chan_def chandef;
	struct cfg80211_chan_def main_chandef;
	bool offchannel;
	bool radar_enabled;

	struct mt76_chanctx *chanctx;

	struct mt76_channel_state *chan_state;
	enum mt76_dfs_state dfs_state;
@@ -837,6 +853,7 @@ struct mt76_phy {
struct mt76_dev {
	struct mt76_phy phy; /* must be first */
	struct mt76_phy *phys[__MT_MAX_BAND];
	struct mt76_phy *band_phys[NUM_NL80211_BANDS];

	struct ieee80211_hw *hw;

@@ -1057,6 +1074,10 @@ struct mt76_ethtool_worker_info {
	int sta_count;
};

struct mt76_chanctx {
	struct mt76_phy *phy;
};

#define CCK_RATE(_idx, _rate) {					\
	.bitrate = _rate,					\
	.flags = IEEE80211_RATE_SHORT_PREAMBLE,			\
@@ -1480,6 +1501,25 @@ void mt76_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void mt76_sw_scan_complete(struct ieee80211_hw *hw,
			   struct ieee80211_vif *vif);
enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy);
int mt76_add_chanctx(struct ieee80211_hw *hw,
		     struct ieee80211_chanctx_conf *conf);
void mt76_remove_chanctx(struct ieee80211_hw *hw,
			 struct ieee80211_chanctx_conf *conf);
void mt76_change_chanctx(struct ieee80211_hw *hw,
			 struct ieee80211_chanctx_conf *conf,
			 u32 changed);
int mt76_assign_vif_chanctx(struct ieee80211_hw *hw,
			    struct ieee80211_vif *vif,
			    struct ieee80211_bss_conf *link_conf,
			    struct ieee80211_chanctx_conf *conf);
void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw,
			       struct ieee80211_vif *vif,
			       struct ieee80211_bss_conf *link_conf,
			       struct ieee80211_chanctx_conf *conf);
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_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,
@@ -1525,6 +1565,8 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames);
void mt76_testmode_tx_pending(struct mt76_phy *phy);
void mt76_queue_tx_complete(struct mt76_dev *dev, struct mt76_queue *q,
			    struct mt76_queue_entry *e);
int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
		       bool offchannel);
int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
		     bool offchannel);
void mt76_scan_work(struct work_struct *work);
@@ -1777,14 +1819,7 @@ mt76_vif_init(struct ieee80211_vif *vif, struct mt76_vif_data *mvif)
	rcu_assign_pointer(mvif->link[0], mlink);
}

static inline void
mt76_vif_cleanup(struct mt76_dev *dev, struct ieee80211_vif *vif)
{
	struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
	struct mt76_vif_data *mvif = mlink->mvif;

	rcu_assign_pointer(mvif->link[0], NULL);
}
void mt76_vif_cleanup(struct mt76_dev *dev, struct ieee80211_vif *vif);

static inline struct mt76_vif_link *
mt76_vif_link(struct mt76_dev *dev, struct ieee80211_vif *vif, int link_id)
@@ -1808,4 +1843,17 @@ mt76_vif_conf_link(struct mt76_dev *dev, struct ieee80211_vif *vif,
	return mt76_dereference(mvif->link[link_conf->link_id], dev);
}

static inline struct mt76_phy *
mt76_vif_link_phy(struct mt76_vif_link *mlink)
{
	struct mt76_chanctx *ctx;

	if (!mlink->ctx)
		return NULL;

	ctx = (struct mt76_chanctx *)mlink->ctx->drv_priv;

	return ctx->phy;
}

#endif
+6 −0
Original line number Diff line number Diff line
@@ -124,6 +124,12 @@ int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
	struct mt76_dev *dev = phy->dev;
	int ret = 0;

	if (hw->wiphy->n_radio > 1) {
		phy = dev->band_phys[req->req.channels[0]->band];
		if (!phy)
			return -EINVAL;
	}

	mutex_lock(&dev->mutex);

	if (dev->scan.req) {