xref: /openbmc/linux/net/mac80211/chan.c (revision d0b73b48)
1 /*
2  * mac80211 - channel management
3  */
4 
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_chandef(struct ieee80211_local *local,
13 				     struct ieee80211_chanctx *ctx,
14 				     const struct cfg80211_chan_def *chandef)
15 {
16 	if (cfg80211_chandef_identical(&ctx->conf.def, chandef))
17 		return;
18 
19 	WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
20 
21 	ctx->conf.def = *chandef;
22 	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
23 
24 	if (!local->use_chanctx) {
25 		local->_oper_channel_type = cfg80211_get_chandef_type(chandef);
26 		ieee80211_hw_config(local, 0);
27 	}
28 }
29 
30 static struct ieee80211_chanctx *
31 ieee80211_find_chanctx(struct ieee80211_local *local,
32 		       const struct cfg80211_chan_def *chandef,
33 		       enum ieee80211_chanctx_mode mode)
34 {
35 	struct ieee80211_chanctx *ctx;
36 
37 	lockdep_assert_held(&local->chanctx_mtx);
38 
39 	if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
40 		return NULL;
41 
42 	list_for_each_entry(ctx, &local->chanctx_list, list) {
43 		const struct cfg80211_chan_def *compat;
44 
45 		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
46 			continue;
47 
48 		compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
49 		if (!compat)
50 			continue;
51 
52 		ieee80211_change_chandef(local, ctx, compat);
53 
54 		return ctx;
55 	}
56 
57 	return NULL;
58 }
59 
60 static struct ieee80211_chanctx *
61 ieee80211_new_chanctx(struct ieee80211_local *local,
62 		      const struct cfg80211_chan_def *chandef,
63 		      enum ieee80211_chanctx_mode mode)
64 {
65 	struct ieee80211_chanctx *ctx;
66 	int err;
67 
68 	lockdep_assert_held(&local->chanctx_mtx);
69 
70 	ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
71 	if (!ctx)
72 		return ERR_PTR(-ENOMEM);
73 
74 	ctx->conf.def = *chandef;
75 	ctx->conf.rx_chains_static = 1;
76 	ctx->conf.rx_chains_dynamic = 1;
77 	ctx->mode = mode;
78 
79 	if (!local->use_chanctx) {
80 		local->_oper_channel_type =
81 			cfg80211_get_chandef_type(chandef);
82 		local->_oper_channel = chandef->chan;
83 		ieee80211_hw_config(local, 0);
84 	} else {
85 		err = drv_add_chanctx(local, ctx);
86 		if (err) {
87 			kfree(ctx);
88 			return ERR_PTR(err);
89 		}
90 	}
91 
92 	list_add_rcu(&ctx->list, &local->chanctx_list);
93 
94 	return ctx;
95 }
96 
97 static void ieee80211_free_chanctx(struct ieee80211_local *local,
98 				   struct ieee80211_chanctx *ctx)
99 {
100 	lockdep_assert_held(&local->chanctx_mtx);
101 
102 	WARN_ON_ONCE(ctx->refcount != 0);
103 
104 	if (!local->use_chanctx) {
105 		local->_oper_channel_type = NL80211_CHAN_NO_HT;
106 		ieee80211_hw_config(local, 0);
107 	} else {
108 		drv_remove_chanctx(local, ctx);
109 	}
110 
111 	list_del_rcu(&ctx->list);
112 	kfree_rcu(ctx, rcu_head);
113 }
114 
115 static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
116 					struct ieee80211_chanctx *ctx)
117 {
118 	struct ieee80211_local *local = sdata->local;
119 	int ret;
120 
121 	lockdep_assert_held(&local->chanctx_mtx);
122 
123 	ret = drv_assign_vif_chanctx(local, sdata, ctx);
124 	if (ret)
125 		return ret;
126 
127 	rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
128 	ctx->refcount++;
129 
130 	ieee80211_recalc_txpower(sdata);
131 
132 	return 0;
133 }
134 
135 static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
136 					      struct ieee80211_chanctx *ctx)
137 {
138 	struct ieee80211_chanctx_conf *conf = &ctx->conf;
139 	struct ieee80211_sub_if_data *sdata;
140 	const struct cfg80211_chan_def *compat = NULL;
141 
142 	lockdep_assert_held(&local->chanctx_mtx);
143 
144 	rcu_read_lock();
145 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
146 
147 		if (!ieee80211_sdata_running(sdata))
148 			continue;
149 		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
150 			continue;
151 
152 		if (!compat)
153 			compat = &sdata->vif.bss_conf.chandef;
154 
155 		compat = cfg80211_chandef_compatible(
156 				&sdata->vif.bss_conf.chandef, compat);
157 		if (!compat)
158 			break;
159 	}
160 	rcu_read_unlock();
161 
162 	if (WARN_ON_ONCE(!compat))
163 		return;
164 
165 	ieee80211_change_chandef(local, ctx, compat);
166 }
167 
168 static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
169 					   struct ieee80211_chanctx *ctx)
170 {
171 	struct ieee80211_local *local = sdata->local;
172 
173 	lockdep_assert_held(&local->chanctx_mtx);
174 
175 	ctx->refcount--;
176 	rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
177 
178 	drv_unassign_vif_chanctx(local, sdata, ctx);
179 
180 	if (ctx->refcount > 0) {
181 		ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
182 		ieee80211_recalc_smps_chanctx(local, ctx);
183 	}
184 }
185 
186 static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
187 {
188 	struct ieee80211_local *local = sdata->local;
189 	struct ieee80211_chanctx_conf *conf;
190 	struct ieee80211_chanctx *ctx;
191 
192 	lockdep_assert_held(&local->chanctx_mtx);
193 
194 	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
195 					 lockdep_is_held(&local->chanctx_mtx));
196 	if (!conf)
197 		return;
198 
199 	ctx = container_of(conf, struct ieee80211_chanctx, conf);
200 
201 	if (sdata->vif.type == NL80211_IFTYPE_AP) {
202 		struct ieee80211_sub_if_data *vlan;
203 
204 		/* for the VLAN list */
205 		ASSERT_RTNL();
206 		list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
207 			rcu_assign_pointer(vlan->vif.chanctx_conf, NULL);
208 	}
209 
210 	ieee80211_unassign_vif_chanctx(sdata, ctx);
211 	if (ctx->refcount == 0)
212 		ieee80211_free_chanctx(local, ctx);
213 }
214 
215 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
216 				   struct ieee80211_chanctx *chanctx)
217 {
218 	struct ieee80211_sub_if_data *sdata;
219 	u8 rx_chains_static, rx_chains_dynamic;
220 
221 	lockdep_assert_held(&local->chanctx_mtx);
222 
223 	rx_chains_static = 1;
224 	rx_chains_dynamic = 1;
225 
226 	rcu_read_lock();
227 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
228 		u8 needed_static, needed_dynamic;
229 
230 		if (!ieee80211_sdata_running(sdata))
231 			continue;
232 
233 		if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
234 						&chanctx->conf)
235 			continue;
236 
237 		switch (sdata->vif.type) {
238 		case NL80211_IFTYPE_P2P_DEVICE:
239 			continue;
240 		case NL80211_IFTYPE_STATION:
241 			if (!sdata->u.mgd.associated)
242 				continue;
243 			break;
244 		case NL80211_IFTYPE_AP_VLAN:
245 			continue;
246 		case NL80211_IFTYPE_AP:
247 		case NL80211_IFTYPE_ADHOC:
248 		case NL80211_IFTYPE_WDS:
249 		case NL80211_IFTYPE_MESH_POINT:
250 			break;
251 		default:
252 			WARN_ON_ONCE(1);
253 		}
254 
255 		switch (sdata->smps_mode) {
256 		default:
257 			WARN_ONCE(1, "Invalid SMPS mode %d\n",
258 				  sdata->smps_mode);
259 			/* fall through */
260 		case IEEE80211_SMPS_OFF:
261 			needed_static = sdata->needed_rx_chains;
262 			needed_dynamic = sdata->needed_rx_chains;
263 			break;
264 		case IEEE80211_SMPS_DYNAMIC:
265 			needed_static = 1;
266 			needed_dynamic = sdata->needed_rx_chains;
267 			break;
268 		case IEEE80211_SMPS_STATIC:
269 			needed_static = 1;
270 			needed_dynamic = 1;
271 			break;
272 		}
273 
274 		rx_chains_static = max(rx_chains_static, needed_static);
275 		rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
276 	}
277 	rcu_read_unlock();
278 
279 	if (!local->use_chanctx) {
280 		if (rx_chains_static > 1)
281 			local->smps_mode = IEEE80211_SMPS_OFF;
282 		else if (rx_chains_dynamic > 1)
283 			local->smps_mode = IEEE80211_SMPS_DYNAMIC;
284 		else
285 			local->smps_mode = IEEE80211_SMPS_STATIC;
286 		ieee80211_hw_config(local, 0);
287 	}
288 
289 	if (rx_chains_static == chanctx->conf.rx_chains_static &&
290 	    rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
291 		return;
292 
293 	chanctx->conf.rx_chains_static = rx_chains_static;
294 	chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
295 	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
296 }
297 
298 int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
299 			      const struct cfg80211_chan_def *chandef,
300 			      enum ieee80211_chanctx_mode mode)
301 {
302 	struct ieee80211_local *local = sdata->local;
303 	struct ieee80211_chanctx *ctx;
304 	int ret;
305 
306 	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
307 
308 	mutex_lock(&local->chanctx_mtx);
309 	__ieee80211_vif_release_channel(sdata);
310 
311 	ctx = ieee80211_find_chanctx(local, chandef, mode);
312 	if (!ctx)
313 		ctx = ieee80211_new_chanctx(local, chandef, mode);
314 	if (IS_ERR(ctx)) {
315 		ret = PTR_ERR(ctx);
316 		goto out;
317 	}
318 
319 	sdata->vif.bss_conf.chandef = *chandef;
320 
321 	ret = ieee80211_assign_vif_chanctx(sdata, ctx);
322 	if (ret) {
323 		/* if assign fails refcount stays the same */
324 		if (ctx->refcount == 0)
325 			ieee80211_free_chanctx(local, ctx);
326 		goto out;
327 	}
328 
329 	if (sdata->vif.type == NL80211_IFTYPE_AP) {
330 		struct ieee80211_sub_if_data *vlan;
331 
332 		/* for the VLAN list */
333 		ASSERT_RTNL();
334 		list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
335 			rcu_assign_pointer(vlan->vif.chanctx_conf, &ctx->conf);
336 	}
337 
338 	ieee80211_recalc_smps_chanctx(local, ctx);
339  out:
340 	mutex_unlock(&local->chanctx_mtx);
341 	return ret;
342 }
343 
344 void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
345 {
346 	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
347 
348 	mutex_lock(&sdata->local->chanctx_mtx);
349 	__ieee80211_vif_release_channel(sdata);
350 	mutex_unlock(&sdata->local->chanctx_mtx);
351 }
352 
353 void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
354 {
355 	struct ieee80211_local *local = sdata->local;
356 	struct ieee80211_sub_if_data *ap;
357 	struct ieee80211_chanctx_conf *conf;
358 
359 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
360 		return;
361 
362 	ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
363 
364 	mutex_lock(&local->chanctx_mtx);
365 
366 	conf = rcu_dereference_protected(ap->vif.chanctx_conf,
367 					 lockdep_is_held(&local->chanctx_mtx));
368 	rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
369 	mutex_unlock(&local->chanctx_mtx);
370 }
371 
372 void ieee80211_iter_chan_contexts_atomic(
373 	struct ieee80211_hw *hw,
374 	void (*iter)(struct ieee80211_hw *hw,
375 		     struct ieee80211_chanctx_conf *chanctx_conf,
376 		     void *data),
377 	void *iter_data)
378 {
379 	struct ieee80211_local *local = hw_to_local(hw);
380 	struct ieee80211_chanctx *ctx;
381 
382 	rcu_read_lock();
383 	list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
384 		iter(hw, &ctx->conf, iter_data);
385 	rcu_read_unlock();
386 }
387 EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);
388