mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 07:27:12 +08:00
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:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user