chan.c 12.4 KB
Newer Older
1
2
3
4
/*
 * mac80211 - channel management
 */

5
#include <linux/nl80211.h>
6
#include <linux/export.h>
7
#include <linux/rtnetlink.h>
8
#include <net/cfg80211.h>
9
#include "ieee80211_i.h"
10
#include "driver-ops.h"
11

12
static void ieee80211_change_chanctx(struct ieee80211_local *local,
13
14
				     struct ieee80211_chanctx *ctx,
				     const struct cfg80211_chan_def *chandef)
15
{
16
	if (cfg80211_chandef_identical(&ctx->conf.def, chandef))
17
		return;
18

19
20
21
22
	WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));

	ctx->conf.def = *chandef;
	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
Johannes Berg's avatar
Johannes Berg committed
23
24

	if (!local->use_chanctx) {
25
		local->_oper_channel_type = cfg80211_get_chandef_type(chandef);
Johannes Berg's avatar
Johannes Berg committed
26
27
		ieee80211_hw_config(local, 0);
	}
28
}
29
30
31

static struct ieee80211_chanctx *
ieee80211_find_chanctx(struct ieee80211_local *local,
32
		       const struct cfg80211_chan_def *chandef,
33
34
35
36
37
38
39
40
41
42
		       enum ieee80211_chanctx_mode mode)
{
	struct ieee80211_chanctx *ctx;

	lockdep_assert_held(&local->chanctx_mtx);

	if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
		return NULL;

	list_for_each_entry(ctx, &local->chanctx_list, list) {
43
		const struct cfg80211_chan_def *compat;
44

45
46
		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
			continue;
47
48
49

		compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
		if (!compat)
50
51
			continue;

52
		ieee80211_change_chanctx(local, ctx, compat);
53

54
55
56
57
58
59
60
61
		return ctx;
	}

	return NULL;
}

static struct ieee80211_chanctx *
ieee80211_new_chanctx(struct ieee80211_local *local,
62
		      const struct cfg80211_chan_def *chandef,
63
64
65
		      enum ieee80211_chanctx_mode mode)
{
	struct ieee80211_chanctx *ctx;
66
	int err;
67
68
69
70
71
72
73

	lockdep_assert_held(&local->chanctx_mtx);

	ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
	if (!ctx)
		return ERR_PTR(-ENOMEM);

74
	ctx->conf.def = *chandef;
75
76
	ctx->conf.rx_chains_static = 1;
	ctx->conf.rx_chains_dynamic = 1;
77
78
	ctx->mode = mode;

Johannes Berg's avatar
Johannes Berg committed
79
	if (!local->use_chanctx) {
80
81
82
		local->_oper_channel_type =
			cfg80211_get_chandef_type(chandef);
		local->_oper_channel = chandef->chan;
Johannes Berg's avatar
Johannes Berg committed
83
84
85
86
87
88
89
		ieee80211_hw_config(local, 0);
	} else {
		err = drv_add_chanctx(local, ctx);
		if (err) {
			kfree(ctx);
			return ERR_PTR(err);
		}
90
91
	}

92
	list_add_rcu(&ctx->list, &local->chanctx_list);
93

94
95
96
97
	mutex_lock(&local->mtx);
	ieee80211_recalc_idle(local);
	mutex_unlock(&local->mtx);

98
99
100
101
102
103
104
105
106
107
	return ctx;
}

static void ieee80211_free_chanctx(struct ieee80211_local *local,
				   struct ieee80211_chanctx *ctx)
{
	lockdep_assert_held(&local->chanctx_mtx);

	WARN_ON_ONCE(ctx->refcount != 0);

Johannes Berg's avatar
Johannes Berg committed
108
109
110
111
112
113
	if (!local->use_chanctx) {
		local->_oper_channel_type = NL80211_CHAN_NO_HT;
		ieee80211_hw_config(local, 0);
	} else {
		drv_remove_chanctx(local, ctx);
	}
114

115
	list_del_rcu(&ctx->list);
116
	kfree_rcu(ctx, rcu_head);
117
118
119
120

	mutex_lock(&local->mtx);
	ieee80211_recalc_idle(local);
	mutex_unlock(&local->mtx);
121
122
123
124
125
}

static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
					struct ieee80211_chanctx *ctx)
{
126
127
	struct ieee80211_local *local = sdata->local;
	int ret;
128
129
130

	lockdep_assert_held(&local->chanctx_mtx);

131
132
133
134
	ret = drv_assign_vif_chanctx(local, sdata, ctx);
	if (ret)
		return ret;

135
136
137
	rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
	ctx->refcount++;

138
	ieee80211_recalc_txpower(sdata);
139
	sdata->vif.bss_conf.idle = false;
140
141
142
143

	if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
	    sdata->vif.type != NL80211_IFTYPE_MONITOR)
		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
144

145
146
147
	return 0;
}

148
149
static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
					      struct ieee80211_chanctx *ctx)
150
151
152
{
	struct ieee80211_chanctx_conf *conf = &ctx->conf;
	struct ieee80211_sub_if_data *sdata;
153
	const struct cfg80211_chan_def *compat = NULL;
154
155
156
157
158

	lockdep_assert_held(&local->chanctx_mtx);

	rcu_read_lock();
	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
159

160
161
162
163
164
		if (!ieee80211_sdata_running(sdata))
			continue;
		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
			continue;

165
166
167
168
169
170
171
		if (!compat)
			compat = &sdata->vif.bss_conf.chandef;

		compat = cfg80211_chandef_compatible(
				&sdata->vif.bss_conf.chandef, compat);
		if (!compat)
			break;
172
173
174
	}
	rcu_read_unlock();

175
176
	if (WARN_ON_ONCE(!compat))
		return;
177

178
	ieee80211_change_chanctx(local, ctx, compat);
179
180
}

181
182
183
static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
					   struct ieee80211_chanctx *ctx)
{
184
	struct ieee80211_local *local = sdata->local;
185
186
187
188
189

	lockdep_assert_held(&local->chanctx_mtx);

	ctx->refcount--;
	rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
190

191
	sdata->vif.bss_conf.idle = true;
192
193
194
195

	if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
	    sdata->vif.type != NL80211_IFTYPE_MONITOR)
		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
196

197
	drv_unassign_vif_chanctx(local, sdata, ctx);
198

199
	if (ctx->refcount > 0) {
200
		ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
201
		ieee80211_recalc_smps_chanctx(local, ctx);
202
		ieee80211_recalc_radar_chanctx(local, ctx);
203
	}
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
}

static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
{
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_chanctx_conf *conf;
	struct ieee80211_chanctx *ctx;

	lockdep_assert_held(&local->chanctx_mtx);

	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
					 lockdep_is_held(&local->chanctx_mtx));
	if (!conf)
		return;

	ctx = container_of(conf, struct ieee80211_chanctx, conf);

	ieee80211_unassign_vif_chanctx(sdata, ctx);
	if (ctx->refcount == 0)
		ieee80211_free_chanctx(local, ctx);
}

226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
				    struct ieee80211_chanctx *chanctx)
{
	struct ieee80211_sub_if_data *sdata;
	bool radar_enabled = false;

	lockdep_assert_held(&local->chanctx_mtx);

	rcu_read_lock();
	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
		if (sdata->radar_required) {
			radar_enabled = true;
			break;
		}
	}
	rcu_read_unlock();

	if (radar_enabled == chanctx->conf.radar_enabled)
		return;

	chanctx->conf.radar_enabled = radar_enabled;
	local->radar_detect_enabled = chanctx->conf.radar_enabled;

	if (!local->use_chanctx) {
		local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
	}

	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
}

257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
				   struct ieee80211_chanctx *chanctx)
{
	struct ieee80211_sub_if_data *sdata;
	u8 rx_chains_static, rx_chains_dynamic;

	lockdep_assert_held(&local->chanctx_mtx);

	rx_chains_static = 1;
	rx_chains_dynamic = 1;

	rcu_read_lock();
	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
		u8 needed_static, needed_dynamic;

		if (!ieee80211_sdata_running(sdata))
			continue;

		if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
						&chanctx->conf)
			continue;

		switch (sdata->vif.type) {
		case NL80211_IFTYPE_P2P_DEVICE:
			continue;
		case NL80211_IFTYPE_STATION:
			if (!sdata->u.mgd.associated)
				continue;
			break;
		case NL80211_IFTYPE_AP_VLAN:
			continue;
		case NL80211_IFTYPE_AP:
		case NL80211_IFTYPE_ADHOC:
		case NL80211_IFTYPE_WDS:
		case NL80211_IFTYPE_MESH_POINT:
			break;
		default:
			WARN_ON_ONCE(1);
		}

		switch (sdata->smps_mode) {
		default:
			WARN_ONCE(1, "Invalid SMPS mode %d\n",
				  sdata->smps_mode);
			/* fall through */
		case IEEE80211_SMPS_OFF:
			needed_static = sdata->needed_rx_chains;
			needed_dynamic = sdata->needed_rx_chains;
			break;
		case IEEE80211_SMPS_DYNAMIC:
			needed_static = 1;
			needed_dynamic = sdata->needed_rx_chains;
			break;
		case IEEE80211_SMPS_STATIC:
			needed_static = 1;
			needed_dynamic = 1;
			break;
		}

		rx_chains_static = max(rx_chains_static, needed_static);
		rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
	}
	rcu_read_unlock();

	if (!local->use_chanctx) {
		if (rx_chains_static > 1)
			local->smps_mode = IEEE80211_SMPS_OFF;
		else if (rx_chains_dynamic > 1)
			local->smps_mode = IEEE80211_SMPS_DYNAMIC;
		else
			local->smps_mode = IEEE80211_SMPS_STATIC;
		ieee80211_hw_config(local, 0);
	}

	if (rx_chains_static == chanctx->conf.rx_chains_static &&
	    rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
		return;

	chanctx->conf.rx_chains_static = rx_chains_static;
	chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
}

340
int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
341
			      const struct cfg80211_chan_def *chandef,
342
343
344
345
346
347
			      enum ieee80211_chanctx_mode mode)
{
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_chanctx *ctx;
	int ret;

Johannes Berg's avatar
Johannes Berg committed
348
349
	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));

350
351
352
	mutex_lock(&local->chanctx_mtx);
	__ieee80211_vif_release_channel(sdata);

353
	ctx = ieee80211_find_chanctx(local, chandef, mode);
354
	if (!ctx)
355
		ctx = ieee80211_new_chanctx(local, chandef, mode);
356
357
358
359
360
	if (IS_ERR(ctx)) {
		ret = PTR_ERR(ctx);
		goto out;
	}

361
	sdata->vif.bss_conf.chandef = *chandef;
Johannes Berg's avatar
Johannes Berg committed
362

363
364
365
366
367
368
369
370
	ret = ieee80211_assign_vif_chanctx(sdata, ctx);
	if (ret) {
		/* if assign fails refcount stays the same */
		if (ctx->refcount == 0)
			ieee80211_free_chanctx(local, ctx);
		goto out;
	}

371
	ieee80211_recalc_smps_chanctx(local, ctx);
372
	ieee80211_recalc_radar_chanctx(local, ctx);
373
374
375
376
377
 out:
	mutex_unlock(&local->chanctx_mtx);
	return ret;
}

378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
				   const struct cfg80211_chan_def *chandef,
				   u32 *changed)
{
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_chanctx_conf *conf;
	struct ieee80211_chanctx *ctx;
	int ret;

	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
				     IEEE80211_CHAN_DISABLED))
		return -EINVAL;

	mutex_lock(&local->chanctx_mtx);
	if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) {
		ret = 0;
		goto out;
	}

	if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) {
		ret = -EINVAL;
		goto out;
	}

	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
					 lockdep_is_held(&local->chanctx_mtx));
	if (!conf) {
		ret = -EINVAL;
		goto out;
	}

	ctx = container_of(conf, struct ieee80211_chanctx, conf);
	if (!cfg80211_chandef_compatible(&conf->def, chandef)) {
		ret = -EINVAL;
		goto out;
	}

	sdata->vif.bss_conf.chandef = *chandef;

	ieee80211_recalc_chanctx_chantype(local, ctx);

	*changed |= BSS_CHANGED_BANDWIDTH;
	ret = 0;
 out:
	mutex_unlock(&local->chanctx_mtx);
	return ret;
}

427
428
void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
{
Johannes Berg's avatar
Johannes Berg committed
429
430
	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));

431
432
433
434
	mutex_lock(&sdata->local->chanctx_mtx);
	__ieee80211_vif_release_channel(sdata);
	mutex_unlock(&sdata->local->chanctx_mtx);
}
435

436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
{
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_sub_if_data *ap;
	struct ieee80211_chanctx_conf *conf;

	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
		return;

	ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);

	mutex_lock(&local->chanctx_mtx);

	conf = rcu_dereference_protected(ap->vif.chanctx_conf,
					 lockdep_is_held(&local->chanctx_mtx));
	rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
	mutex_unlock(&local->chanctx_mtx);
}

455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
					 bool clear)
{
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_sub_if_data *vlan;
	struct ieee80211_chanctx_conf *conf;

	ASSERT_RTNL();

	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
		return;

	mutex_lock(&local->chanctx_mtx);

	/*
	 * Check that conf exists, even when clearing this function
	 * must be called with the AP's channel context still there
	 * as it would otherwise cause VLANs to have an invalid
	 * channel context pointer for a while, possibly pointing
	 * to a channel context that has already been freed.
	 */
	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
				lockdep_is_held(&local->chanctx_mtx));
	WARN_ON(!conf);

	if (clear)
		conf = NULL;

	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
		rcu_assign_pointer(vlan->vif.chanctx_conf, conf);

	mutex_unlock(&local->chanctx_mtx);
}

489
490
491
492
493
494
495
496
497
498
499
500
void ieee80211_iter_chan_contexts_atomic(
	struct ieee80211_hw *hw,
	void (*iter)(struct ieee80211_hw *hw,
		     struct ieee80211_chanctx_conf *chanctx_conf,
		     void *data),
	void *iter_data)
{
	struct ieee80211_local *local = hw_to_local(hw);
	struct ieee80211_chanctx *ctx;

	rcu_read_lock();
	list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
501
502
		if (ctx->driver_present)
			iter(hw, &ctx->conf, iter_data);
503
504
505
	rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);