File Index
Files and directories
Sign in
FILE

channel.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/mediatek/mt76/channel.c C
  1// SPDX-License-Identifier: BSD-3-Clause-Clear
  2/*
  3 * Copyright (C) 2024 Felix Fietkau <nbd@nbd.name>
  4 */
  5#include "mt76.h"
  6
  7static struct mt76_vif_link *
  8mt76_alloc_mlink(struct mt76_dev *dev, struct mt76_vif_data *mvif)
  9{
 10	struct mt76_vif_link *mlink;
 11
 12	mlink = kzalloc(dev->drv->link_data_size, GFP_KERNEL);
 13	if (!mlink)
 14		return NULL;
 15
 16	mlink->mvif = mvif;
 17
 18	return mlink;
 19}
 20
 21static int
 22mt76_phy_update_channel(struct mt76_phy *phy,
 23			struct ieee80211_chanctx_conf *conf)
 24{
 25	phy->radar_enabled = conf->radar_enabled;
 26	phy->main_chandef = conf->def;
 27	phy->chanctx = (struct mt76_chanctx *)conf->drv_priv;
 28
 29	return __mt76_set_channel(phy, &phy->main_chandef, false);
 30}
 31
 32int mt76_add_chanctx(struct ieee80211_hw *hw,
 33		     struct ieee80211_chanctx_conf *conf)
 34{
 35	struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
 36	struct mt76_phy *phy = hw->priv;
 37	struct mt76_dev *dev = phy->dev;
 38	int ret = -EINVAL;
 39
 40	phy = ctx->phy = dev->band_phys[conf->def.chan->band];
 41	if (WARN_ON_ONCE(!phy))
 42		return ret;
 43
 44	if (dev->scan.phy == phy)
 45		mt76_abort_scan(dev);
 46
 47	mutex_lock(&dev->mutex);
 48	if (!phy->chanctx)
 49		ret = mt76_phy_update_channel(phy, conf);
 50	else
 51		ret = 0;
 52	mutex_unlock(&dev->mutex);
 53
 54	return ret;
 55}
 56EXPORT_SYMBOL_GPL(mt76_add_chanctx);
 57
 58void mt76_remove_chanctx(struct ieee80211_hw *hw,
 59			 struct ieee80211_chanctx_conf *conf)
 60{
 61	struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
 62	struct mt76_phy *phy = hw->priv;
 63	struct mt76_dev *dev = phy->dev;
 64
 65	phy = ctx->phy;
 66	if (WARN_ON_ONCE(!phy))
 67		return;
 68
 69	if (dev->scan.phy == phy)
 70		mt76_abort_scan(dev);
 71
 72	mutex_lock(&dev->mutex);
 73	if (phy->chanctx == ctx)
 74		phy->chanctx = NULL;
 75	mutex_unlock(&dev->mutex);
 76}
 77EXPORT_SYMBOL_GPL(mt76_remove_chanctx);
 78
 79void mt76_change_chanctx(struct ieee80211_hw *hw,
 80			 struct ieee80211_chanctx_conf *conf,
 81			 u32 changed)
 82{
 83	struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
 84	struct mt76_phy *phy = ctx->phy;
 85	struct mt76_dev *dev = phy->dev;
 86
 87	if (!(changed & (IEEE80211_CHANCTX_CHANGE_WIDTH |
 88			 IEEE80211_CHANCTX_CHANGE_RADAR)))
 89		return;
 90
 91	cancel_delayed_work_sync(&phy->mac_work);
 92
 93	mutex_lock(&dev->mutex);
 94	mt76_phy_update_channel(phy, conf);
 95	mutex_unlock(&dev->mutex);
 96}
 97EXPORT_SYMBOL_GPL(mt76_change_chanctx);
 98
 99
100int mt76_assign_vif_chanctx(struct ieee80211_hw *hw,
101			    struct ieee80211_vif *vif,
102			    struct ieee80211_bss_conf *link_conf,
103			    struct ieee80211_chanctx_conf *conf)
104{
105	struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
106	struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
107	struct mt76_vif_data *mvif = mlink->mvif;
108	int link_id = link_conf->link_id;
109	struct mt76_phy *phy = ctx->phy;
110	struct mt76_dev *dev = phy->dev;
111	bool mlink_alloc = false;
112	int ret = 0;
113
114	if (dev->scan.vif == vif)
115		mt76_abort_scan(dev);
116
117	mutex_lock(&dev->mutex);
118
119	if (vif->type == NL80211_IFTYPE_MONITOR &&
120	    is_zero_ether_addr(vif->addr))
121		goto out;
122
123	mlink = mt76_vif_conf_link(dev, vif, link_conf);
124	if (!mlink) {
125		mlink = mt76_alloc_mlink(dev, mvif);
126		if (!mlink) {
127			ret = -ENOMEM;
128			goto out;
129		}
130		mlink_alloc = true;
131	}
132
133	mlink->ctx = conf;
134	ret = dev->drv->vif_link_add(phy, vif, link_conf, mlink);
135	if (ret) {
136		if (mlink_alloc)
137			kfree(mlink);
138		goto out;
139	}
140
141	if (link_conf != &vif->bss_conf)
142		rcu_assign_pointer(mvif->link[link_id], mlink);
143
144out:
145	mutex_unlock(&dev->mutex);
146
147	return ret;
148}
149EXPORT_SYMBOL_GPL(mt76_assign_vif_chanctx);
150
151void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw,
152			       struct ieee80211_vif *vif,
153			       struct ieee80211_bss_conf *link_conf,
154			       struct ieee80211_chanctx_conf *conf)
155{
156	struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
157	struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
158	struct mt76_vif_data *mvif = mlink->mvif;
159	int link_id = link_conf->link_id;
160	struct mt76_phy *phy = ctx->phy;
161	struct mt76_dev *dev = phy->dev;
162
163	if (dev->scan.vif == vif)
164		mt76_abort_scan(dev);
165
166	mutex_lock(&dev->mutex);
167
168	if (vif->type == NL80211_IFTYPE_MONITOR &&
169	    is_zero_ether_addr(vif->addr))
170		goto out;
171
172	mlink = mt76_vif_conf_link(dev, vif, link_conf);
173	if (!mlink)
174		goto out;
175
176	if (mlink != (struct mt76_vif_link *)vif->drv_priv)
177		rcu_assign_pointer(mvif->link[link_id], NULL);
178
179	dev->drv->vif_link_remove(phy, vif, link_conf, mlink);
180	mlink->ctx = NULL;
181
182	if (mlink != (struct mt76_vif_link *)vif->drv_priv)
183		kfree_rcu(mlink, rcu_head);
184
185out:
186	mutex_unlock(&dev->mutex);
187}
188EXPORT_SYMBOL_GPL(mt76_unassign_vif_chanctx);
189
190int mt76_switch_vif_chanctx(struct ieee80211_hw *hw,
191			    struct ieee80211_vif_chanctx_switch *vifs,
192			    int n_vifs,
193			    enum ieee80211_chanctx_switch_mode mode)
194{
195	struct mt76_chanctx *old_ctx = (struct mt76_chanctx *)vifs->old_ctx->drv_priv;
196	struct mt76_chanctx *new_ctx = (struct mt76_chanctx *)vifs->new_ctx->drv_priv;
197	struct ieee80211_chanctx_conf *conf = vifs->new_ctx;
198	struct mt76_phy *old_phy = old_ctx->phy;
199	struct mt76_phy *phy = hw->priv;
200	struct mt76_dev *dev = phy->dev;
201	struct mt76_vif_link *mlink;
202	bool update_chan;
203	int i, ret = 0;
204
205	if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS)
206		phy = new_ctx->phy = dev->band_phys[conf->def.chan->band];
207	else
208		phy = new_ctx->phy;
209	if (!phy)
210		return -EINVAL;
211
212	update_chan = phy->chanctx != new_ctx;
213	if (update_chan) {
214		if (dev->scan.phy == phy)
215			mt76_abort_scan(dev);
216
217		cancel_delayed_work_sync(&phy->mac_work);
218	}
219
220	mutex_lock(&dev->mutex);
221
222	if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&
223	    phy != old_phy && old_phy->chanctx == old_ctx)
224		old_phy->chanctx = NULL;
225
226	if (update_chan)
227		ret = mt76_phy_update_channel(phy, vifs->new_ctx);
228
229	if (ret)
230		goto out;
231
232	if (old_phy == phy)
233		goto skip_link_replace;
234
235	for (i = 0; i < n_vifs; i++) {
236		mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf);
237		if (!mlink)
238			continue;
239
240		dev->drv->vif_link_remove(old_phy, vifs[i].vif,
241					  vifs[i].link_conf, mlink);
242
243		ret = dev->drv->vif_link_add(phy, vifs[i].vif,
244					     vifs[i].link_conf, mlink);
245		if (ret)
246			goto out;
247
248	}
249
250skip_link_replace:
251	for (i = 0; i < n_vifs; i++) {
252		mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf);
253		if (!mlink)
254			continue;
255
256		mlink->ctx = vifs->new_ctx;
257	}
258
259out:
260	mutex_unlock(&dev->mutex);
261
262	return ret;
263}
264EXPORT_SYMBOL_GPL(mt76_switch_vif_chanctx);
265
266struct mt76_vif_link *mt76_get_vif_phy_link(struct mt76_phy *phy,
267					    struct ieee80211_vif *vif)
268{
269	struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
270	struct mt76_vif_data *mvif = mlink->mvif;
271	struct mt76_dev *dev = phy->dev;
272	int i, ret;
273
274	for (i = 0; i < ARRAY_SIZE(mvif->link); i++) {
275		mlink = mt76_dereference(mvif->link[i], dev);
276		if (!mlink)
277			continue;
278
279		if (mt76_vif_link_phy(mlink) == phy)
280			return mlink;
281	}
282
283	if (!dev->drv->vif_link_add)
284		return ERR_PTR(-EINVAL);
285
286	mlink = mt76_alloc_mlink(dev, mvif);
287	if (!mlink)
288		return ERR_PTR(-ENOMEM);
289
290	mlink->offchannel = true;
291	ret = dev->drv->vif_link_add(phy, vif, &vif->bss_conf, mlink);
292	if (ret) {
293		kfree(mlink);
294		return ERR_PTR(ret);
295	}
296	rcu_assign_pointer(mvif->offchannel_link, mlink);
297
298	return mlink;
299}
300
301void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif,
302			   struct mt76_vif_link *mlink)
303{
304	struct mt76_dev *dev = phy->dev;
305	struct mt76_vif_data *mvif;
306
307	if (IS_ERR_OR_NULL(mlink) || !mlink->offchannel)
308		return;
309
310	mvif = mlink->mvif;
311
312	rcu_assign_pointer(mvif->offchannel_link, NULL);
313	dev->drv->vif_link_remove(phy, vif, &vif->bss_conf, mlink);
314	kfree(mlink);
315}
316
317void mt76_roc_complete(struct mt76_phy *phy)
318{
319	struct mt76_vif_link *mlink = phy->roc_link;
320	struct mt76_dev *dev = phy->dev;
321
322	if (!phy->roc_vif)
323		return;
324
325	if (mlink)
326		mlink->mvif->roc_phy = NULL;
327	if (phy->main_chandef.chan &&
328	    !test_bit(MT76_MCU_RESET, &dev->phy.state))
329		mt76_set_channel(phy, &phy->main_chandef, false);
330	mt76_put_vif_phy_link(phy, phy->roc_vif, phy->roc_link);
331	phy->roc_vif = NULL;
332	phy->roc_link = NULL;
333	if (!test_bit(MT76_MCU_RESET, &dev->phy.state))
334		ieee80211_remain_on_channel_expired(phy->hw);
335}
336
337void mt76_roc_complete_work(struct work_struct *work)
338{
339	struct mt76_phy *phy = container_of(work, struct mt76_phy, roc_work.work);
340	struct mt76_dev *dev = phy->dev;
341
342	mutex_lock(&dev->mutex);
343	mt76_roc_complete(phy);
344	mutex_unlock(&dev->mutex);
345}
346
347void mt76_abort_roc(struct mt76_phy *phy)
348{
349	struct mt76_dev *dev = phy->dev;
350
351	cancel_delayed_work_sync(&phy->roc_work);
352
353	mutex_lock(&dev->mutex);
354	mt76_roc_complete(phy);
355	mutex_unlock(&dev->mutex);
356}
357EXPORT_SYMBOL_GPL(mt76_abort_roc);
358
359int mt76_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
360			   struct ieee80211_channel *chan, int duration,
361			   enum ieee80211_roc_type type)
362{
363	struct cfg80211_chan_def chandef = {};
364	struct mt76_phy *phy = hw->priv;
365	struct mt76_dev *dev = phy->dev;
366	struct mt76_vif_link *mlink;
367	int ret = 0;
368
369	phy = dev->band_phys[chan->band];
370	if (!phy)
371		return -EINVAL;
372
373	mutex_lock(&dev->mutex);
374
375	if (phy->roc_vif || dev->scan.phy == phy ||
376	    test_bit(MT76_MCU_RESET, &dev->phy.state)) {
377		ret = -EBUSY;
378		goto out;
379	}
380
381	mlink = mt76_get_vif_phy_link(phy, vif);
382	if (IS_ERR(mlink)) {
383		ret = PTR_ERR(mlink);
384		goto out;
385	}
386
387	mlink->mvif->roc_phy = phy;
388	phy->roc_vif = vif;
389	phy->roc_link = mlink;
390	cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
391	mt76_set_channel(phy, &chandef, true);
392	ieee80211_ready_on_channel(hw);
393	ieee80211_queue_delayed_work(phy->hw, &phy->roc_work,
394				     msecs_to_jiffies(duration));
395
396out:
397	mutex_unlock(&dev->mutex);
398	return ret;
399}
400EXPORT_SYMBOL_GPL(mt76_remain_on_channel);
401
402int mt76_cancel_remain_on_channel(struct ieee80211_hw *hw,
403				  struct ieee80211_vif *vif)
404{
405	struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
406	struct mt76_vif_data *mvif = mlink->mvif;
407	struct mt76_phy *phy = mvif->roc_phy;
408
409	if (!phy)
410		return 0;
411
412	mt76_abort_roc(phy);
413
414	return 0;
415}
416EXPORT_SYMBOL_GPL(mt76_cancel_remain_on_channel);
~/ Read-only file preview.