xref: /openbmc/linux/net/mac80211/chan.c (revision 164eb02d)
1f444de05SJohannes Berg /*
2f444de05SJohannes Berg  * mac80211 - channel management
3f444de05SJohannes Berg  */
4f444de05SJohannes Berg 
50aaffa9bSJohannes Berg #include <linux/nl80211.h>
63448c005SJohannes Berg #include <linux/export.h>
74d76d21bSJohannes Berg #include <linux/rtnetlink.h>
83117bbdbSPaul Stewart #include <net/cfg80211.h>
9f444de05SJohannes Berg #include "ieee80211_i.h"
1035f2fce9SMichal Kazior #include "driver-ops.h"
11f444de05SJohannes Berg 
124bf88530SJohannes Berg static void ieee80211_change_chandef(struct ieee80211_local *local,
13e89a96f5SMichal Kazior 				     struct ieee80211_chanctx *ctx,
144bf88530SJohannes Berg 				     const struct cfg80211_chan_def *chandef)
15e89a96f5SMichal Kazior {
164bf88530SJohannes Berg 	if (cfg80211_chandef_identical(&ctx->conf.def, chandef))
17e89a96f5SMichal Kazior 		return;
18e89a96f5SMichal Kazior 
194bf88530SJohannes Berg 	WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
204bf88530SJohannes Berg 
214bf88530SJohannes Berg 	ctx->conf.def = *chandef;
224bf88530SJohannes Berg 	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
2355de908aSJohannes Berg 
2455de908aSJohannes Berg 	if (!local->use_chanctx) {
254bf88530SJohannes Berg 		local->_oper_channel_type = cfg80211_get_chandef_type(chandef);
2655de908aSJohannes Berg 		ieee80211_hw_config(local, 0);
2755de908aSJohannes Berg 	}
280aaffa9bSJohannes Berg }
29d01a1e65SMichal Kazior 
30d01a1e65SMichal Kazior static struct ieee80211_chanctx *
31d01a1e65SMichal Kazior ieee80211_find_chanctx(struct ieee80211_local *local,
324bf88530SJohannes Berg 		       const struct cfg80211_chan_def *chandef,
33d01a1e65SMichal Kazior 		       enum ieee80211_chanctx_mode mode)
34d01a1e65SMichal Kazior {
35d01a1e65SMichal Kazior 	struct ieee80211_chanctx *ctx;
36d01a1e65SMichal Kazior 
37d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
38d01a1e65SMichal Kazior 
39d01a1e65SMichal Kazior 	if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
40d01a1e65SMichal Kazior 		return NULL;
41d01a1e65SMichal Kazior 
42d01a1e65SMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
434bf88530SJohannes Berg 		const struct cfg80211_chan_def *compat;
44e89a96f5SMichal Kazior 
45d01a1e65SMichal Kazior 		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
46d01a1e65SMichal Kazior 			continue;
474bf88530SJohannes Berg 
484bf88530SJohannes Berg 		compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
494bf88530SJohannes Berg 		if (!compat)
50d01a1e65SMichal Kazior 			continue;
51d01a1e65SMichal Kazior 
524bf88530SJohannes Berg 		ieee80211_change_chandef(local, ctx, compat);
53e89a96f5SMichal Kazior 
54d01a1e65SMichal Kazior 		return ctx;
55d01a1e65SMichal Kazior 	}
56d01a1e65SMichal Kazior 
57d01a1e65SMichal Kazior 	return NULL;
58d01a1e65SMichal Kazior }
59d01a1e65SMichal Kazior 
60d01a1e65SMichal Kazior static struct ieee80211_chanctx *
61d01a1e65SMichal Kazior ieee80211_new_chanctx(struct ieee80211_local *local,
624bf88530SJohannes Berg 		      const struct cfg80211_chan_def *chandef,
63d01a1e65SMichal Kazior 		      enum ieee80211_chanctx_mode mode)
64d01a1e65SMichal Kazior {
65d01a1e65SMichal Kazior 	struct ieee80211_chanctx *ctx;
6635f2fce9SMichal Kazior 	int err;
67d01a1e65SMichal Kazior 
68d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
69d01a1e65SMichal Kazior 
70d01a1e65SMichal Kazior 	ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
71d01a1e65SMichal Kazior 	if (!ctx)
72d01a1e65SMichal Kazior 		return ERR_PTR(-ENOMEM);
73d01a1e65SMichal Kazior 
744bf88530SJohannes Berg 	ctx->conf.def = *chandef;
7504ecd257SJohannes Berg 	ctx->conf.rx_chains_static = 1;
7604ecd257SJohannes Berg 	ctx->conf.rx_chains_dynamic = 1;
77d01a1e65SMichal Kazior 	ctx->mode = mode;
78d01a1e65SMichal Kazior 
7955de908aSJohannes Berg 	if (!local->use_chanctx) {
804bf88530SJohannes Berg 		local->_oper_channel_type =
814bf88530SJohannes Berg 			cfg80211_get_chandef_type(chandef);
824bf88530SJohannes Berg 		local->_oper_channel = chandef->chan;
8355de908aSJohannes Berg 		ieee80211_hw_config(local, 0);
8455de908aSJohannes Berg 	} else {
8535f2fce9SMichal Kazior 		err = drv_add_chanctx(local, ctx);
8635f2fce9SMichal Kazior 		if (err) {
8735f2fce9SMichal Kazior 			kfree(ctx);
8835f2fce9SMichal Kazior 			return ERR_PTR(err);
8935f2fce9SMichal Kazior 		}
9055de908aSJohannes Berg 	}
9135f2fce9SMichal Kazior 
923448c005SJohannes Berg 	list_add_rcu(&ctx->list, &local->chanctx_list);
93d01a1e65SMichal Kazior 
94fd0f979aSJohannes Berg 	mutex_lock(&local->mtx);
95fd0f979aSJohannes Berg 	ieee80211_recalc_idle(local);
96fd0f979aSJohannes Berg 	mutex_unlock(&local->mtx);
97fd0f979aSJohannes Berg 
98d01a1e65SMichal Kazior 	return ctx;
99d01a1e65SMichal Kazior }
100d01a1e65SMichal Kazior 
101d01a1e65SMichal Kazior static void ieee80211_free_chanctx(struct ieee80211_local *local,
102d01a1e65SMichal Kazior 				   struct ieee80211_chanctx *ctx)
103d01a1e65SMichal Kazior {
104d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
105d01a1e65SMichal Kazior 
106d01a1e65SMichal Kazior 	WARN_ON_ONCE(ctx->refcount != 0);
107d01a1e65SMichal Kazior 
10855de908aSJohannes Berg 	if (!local->use_chanctx) {
10955de908aSJohannes Berg 		local->_oper_channel_type = NL80211_CHAN_NO_HT;
11055de908aSJohannes Berg 		ieee80211_hw_config(local, 0);
11155de908aSJohannes Berg 	} else {
11235f2fce9SMichal Kazior 		drv_remove_chanctx(local, ctx);
11355de908aSJohannes Berg 	}
11435f2fce9SMichal Kazior 
1153448c005SJohannes Berg 	list_del_rcu(&ctx->list);
116d01a1e65SMichal Kazior 	kfree_rcu(ctx, rcu_head);
117fd0f979aSJohannes Berg 
118fd0f979aSJohannes Berg 	mutex_lock(&local->mtx);
119fd0f979aSJohannes Berg 	ieee80211_recalc_idle(local);
120fd0f979aSJohannes Berg 	mutex_unlock(&local->mtx);
121d01a1e65SMichal Kazior }
122d01a1e65SMichal Kazior 
123d01a1e65SMichal Kazior static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
124d01a1e65SMichal Kazior 					struct ieee80211_chanctx *ctx)
125d01a1e65SMichal Kazior {
12635f2fce9SMichal Kazior 	struct ieee80211_local *local = sdata->local;
12735f2fce9SMichal Kazior 	int ret;
128d01a1e65SMichal Kazior 
129d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
130d01a1e65SMichal Kazior 
13135f2fce9SMichal Kazior 	ret = drv_assign_vif_chanctx(local, sdata, ctx);
13235f2fce9SMichal Kazior 	if (ret)
13335f2fce9SMichal Kazior 		return ret;
13435f2fce9SMichal Kazior 
135d01a1e65SMichal Kazior 	rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
136d01a1e65SMichal Kazior 	ctx->refcount++;
137d01a1e65SMichal Kazior 
1381ea6f9c0SJohannes Berg 	ieee80211_recalc_txpower(sdata);
139fd0f979aSJohannes Berg 	sdata->vif.bss_conf.idle = false;
140fd0f979aSJohannes Berg 	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
1411ea6f9c0SJohannes Berg 
142d01a1e65SMichal Kazior 	return 0;
143d01a1e65SMichal Kazior }
144d01a1e65SMichal Kazior 
1454bf88530SJohannes Berg static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
146e89a96f5SMichal Kazior 					      struct ieee80211_chanctx *ctx)
147e89a96f5SMichal Kazior {
148e89a96f5SMichal Kazior 	struct ieee80211_chanctx_conf *conf = &ctx->conf;
149e89a96f5SMichal Kazior 	struct ieee80211_sub_if_data *sdata;
1504bf88530SJohannes Berg 	const struct cfg80211_chan_def *compat = NULL;
151e89a96f5SMichal Kazior 
152e89a96f5SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
153e89a96f5SMichal Kazior 
154e89a96f5SMichal Kazior 	rcu_read_lock();
155e89a96f5SMichal Kazior 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
1564bf88530SJohannes Berg 
157e89a96f5SMichal Kazior 		if (!ieee80211_sdata_running(sdata))
158e89a96f5SMichal Kazior 			continue;
159e89a96f5SMichal Kazior 		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
160e89a96f5SMichal Kazior 			continue;
161e89a96f5SMichal Kazior 
1624bf88530SJohannes Berg 		if (!compat)
1634bf88530SJohannes Berg 			compat = &sdata->vif.bss_conf.chandef;
1644bf88530SJohannes Berg 
1654bf88530SJohannes Berg 		compat = cfg80211_chandef_compatible(
1664bf88530SJohannes Berg 				&sdata->vif.bss_conf.chandef, compat);
1674bf88530SJohannes Berg 		if (!compat)
1684bf88530SJohannes Berg 			break;
169e89a96f5SMichal Kazior 	}
170e89a96f5SMichal Kazior 	rcu_read_unlock();
171e89a96f5SMichal Kazior 
1724bf88530SJohannes Berg 	if (WARN_ON_ONCE(!compat))
1734bf88530SJohannes Berg 		return;
174e89a96f5SMichal Kazior 
1754bf88530SJohannes Berg 	ieee80211_change_chandef(local, ctx, compat);
176e89a96f5SMichal Kazior }
177e89a96f5SMichal Kazior 
178d01a1e65SMichal Kazior static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
179d01a1e65SMichal Kazior 					   struct ieee80211_chanctx *ctx)
180d01a1e65SMichal Kazior {
18135f2fce9SMichal Kazior 	struct ieee80211_local *local = sdata->local;
182d01a1e65SMichal Kazior 
183d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
184d01a1e65SMichal Kazior 
185d01a1e65SMichal Kazior 	ctx->refcount--;
186d01a1e65SMichal Kazior 	rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
18735f2fce9SMichal Kazior 
188fd0f979aSJohannes Berg 	sdata->vif.bss_conf.idle = true;
189fd0f979aSJohannes Berg 	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
190fd0f979aSJohannes Berg 
19135f2fce9SMichal Kazior 	drv_unassign_vif_chanctx(local, sdata, ctx);
192e89a96f5SMichal Kazior 
19304ecd257SJohannes Berg 	if (ctx->refcount > 0) {
194e89a96f5SMichal Kazior 		ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
19504ecd257SJohannes Berg 		ieee80211_recalc_smps_chanctx(local, ctx);
196164eb02dSSimon Wunderlich 		ieee80211_recalc_radar_chanctx(local, ctx);
19704ecd257SJohannes Berg 	}
198d01a1e65SMichal Kazior }
199d01a1e65SMichal Kazior 
200d01a1e65SMichal Kazior static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
201d01a1e65SMichal Kazior {
202d01a1e65SMichal Kazior 	struct ieee80211_local *local = sdata->local;
203d01a1e65SMichal Kazior 	struct ieee80211_chanctx_conf *conf;
204d01a1e65SMichal Kazior 	struct ieee80211_chanctx *ctx;
205d01a1e65SMichal Kazior 
206d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
207d01a1e65SMichal Kazior 
208d01a1e65SMichal Kazior 	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
209d01a1e65SMichal Kazior 					 lockdep_is_held(&local->chanctx_mtx));
210d01a1e65SMichal Kazior 	if (!conf)
211d01a1e65SMichal Kazior 		return;
212d01a1e65SMichal Kazior 
213d01a1e65SMichal Kazior 	ctx = container_of(conf, struct ieee80211_chanctx, conf);
214d01a1e65SMichal Kazior 
215d01a1e65SMichal Kazior 	ieee80211_unassign_vif_chanctx(sdata, ctx);
216d01a1e65SMichal Kazior 	if (ctx->refcount == 0)
217d01a1e65SMichal Kazior 		ieee80211_free_chanctx(local, ctx);
218d01a1e65SMichal Kazior }
219d01a1e65SMichal Kazior 
220164eb02dSSimon Wunderlich void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
221164eb02dSSimon Wunderlich 				    struct ieee80211_chanctx *chanctx)
222164eb02dSSimon Wunderlich {
223164eb02dSSimon Wunderlich 	struct ieee80211_sub_if_data *sdata;
224164eb02dSSimon Wunderlich 	bool radar_enabled = false;
225164eb02dSSimon Wunderlich 
226164eb02dSSimon Wunderlich 	lockdep_assert_held(&local->chanctx_mtx);
227164eb02dSSimon Wunderlich 
228164eb02dSSimon Wunderlich 	rcu_read_lock();
229164eb02dSSimon Wunderlich 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
230164eb02dSSimon Wunderlich 		if (sdata->radar_required) {
231164eb02dSSimon Wunderlich 			radar_enabled = true;
232164eb02dSSimon Wunderlich 			break;
233164eb02dSSimon Wunderlich 		}
234164eb02dSSimon Wunderlich 	}
235164eb02dSSimon Wunderlich 	rcu_read_unlock();
236164eb02dSSimon Wunderlich 
237164eb02dSSimon Wunderlich 	if (radar_enabled == chanctx->conf.radar_enabled)
238164eb02dSSimon Wunderlich 		return;
239164eb02dSSimon Wunderlich 
240164eb02dSSimon Wunderlich 	chanctx->conf.radar_enabled = radar_enabled;
241164eb02dSSimon Wunderlich 	local->radar_detect_enabled = chanctx->conf.radar_enabled;
242164eb02dSSimon Wunderlich 
243164eb02dSSimon Wunderlich 	if (!local->use_chanctx) {
244164eb02dSSimon Wunderlich 		local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
245164eb02dSSimon Wunderlich 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
246164eb02dSSimon Wunderlich 	}
247164eb02dSSimon Wunderlich 
248164eb02dSSimon Wunderlich 	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
249164eb02dSSimon Wunderlich }
250164eb02dSSimon Wunderlich 
25104ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
25204ecd257SJohannes Berg 				   struct ieee80211_chanctx *chanctx)
25304ecd257SJohannes Berg {
25404ecd257SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
25504ecd257SJohannes Berg 	u8 rx_chains_static, rx_chains_dynamic;
25604ecd257SJohannes Berg 
25704ecd257SJohannes Berg 	lockdep_assert_held(&local->chanctx_mtx);
25804ecd257SJohannes Berg 
25904ecd257SJohannes Berg 	rx_chains_static = 1;
26004ecd257SJohannes Berg 	rx_chains_dynamic = 1;
26104ecd257SJohannes Berg 
26204ecd257SJohannes Berg 	rcu_read_lock();
26304ecd257SJohannes Berg 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
26404ecd257SJohannes Berg 		u8 needed_static, needed_dynamic;
26504ecd257SJohannes Berg 
26604ecd257SJohannes Berg 		if (!ieee80211_sdata_running(sdata))
26704ecd257SJohannes Berg 			continue;
26804ecd257SJohannes Berg 
26904ecd257SJohannes Berg 		if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
27004ecd257SJohannes Berg 						&chanctx->conf)
27104ecd257SJohannes Berg 			continue;
27204ecd257SJohannes Berg 
27304ecd257SJohannes Berg 		switch (sdata->vif.type) {
27404ecd257SJohannes Berg 		case NL80211_IFTYPE_P2P_DEVICE:
27504ecd257SJohannes Berg 			continue;
27604ecd257SJohannes Berg 		case NL80211_IFTYPE_STATION:
27704ecd257SJohannes Berg 			if (!sdata->u.mgd.associated)
27804ecd257SJohannes Berg 				continue;
27904ecd257SJohannes Berg 			break;
28004ecd257SJohannes Berg 		case NL80211_IFTYPE_AP_VLAN:
28104ecd257SJohannes Berg 			continue;
28204ecd257SJohannes Berg 		case NL80211_IFTYPE_AP:
28304ecd257SJohannes Berg 		case NL80211_IFTYPE_ADHOC:
28404ecd257SJohannes Berg 		case NL80211_IFTYPE_WDS:
28504ecd257SJohannes Berg 		case NL80211_IFTYPE_MESH_POINT:
28604ecd257SJohannes Berg 			break;
28704ecd257SJohannes Berg 		default:
28804ecd257SJohannes Berg 			WARN_ON_ONCE(1);
28904ecd257SJohannes Berg 		}
29004ecd257SJohannes Berg 
29104ecd257SJohannes Berg 		switch (sdata->smps_mode) {
29204ecd257SJohannes Berg 		default:
29304ecd257SJohannes Berg 			WARN_ONCE(1, "Invalid SMPS mode %d\n",
29404ecd257SJohannes Berg 				  sdata->smps_mode);
29504ecd257SJohannes Berg 			/* fall through */
29604ecd257SJohannes Berg 		case IEEE80211_SMPS_OFF:
29704ecd257SJohannes Berg 			needed_static = sdata->needed_rx_chains;
29804ecd257SJohannes Berg 			needed_dynamic = sdata->needed_rx_chains;
29904ecd257SJohannes Berg 			break;
30004ecd257SJohannes Berg 		case IEEE80211_SMPS_DYNAMIC:
30104ecd257SJohannes Berg 			needed_static = 1;
30204ecd257SJohannes Berg 			needed_dynamic = sdata->needed_rx_chains;
30304ecd257SJohannes Berg 			break;
30404ecd257SJohannes Berg 		case IEEE80211_SMPS_STATIC:
30504ecd257SJohannes Berg 			needed_static = 1;
30604ecd257SJohannes Berg 			needed_dynamic = 1;
30704ecd257SJohannes Berg 			break;
30804ecd257SJohannes Berg 		}
30904ecd257SJohannes Berg 
31004ecd257SJohannes Berg 		rx_chains_static = max(rx_chains_static, needed_static);
31104ecd257SJohannes Berg 		rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
31204ecd257SJohannes Berg 	}
31304ecd257SJohannes Berg 	rcu_read_unlock();
31404ecd257SJohannes Berg 
31504ecd257SJohannes Berg 	if (!local->use_chanctx) {
31604ecd257SJohannes Berg 		if (rx_chains_static > 1)
31704ecd257SJohannes Berg 			local->smps_mode = IEEE80211_SMPS_OFF;
31804ecd257SJohannes Berg 		else if (rx_chains_dynamic > 1)
31904ecd257SJohannes Berg 			local->smps_mode = IEEE80211_SMPS_DYNAMIC;
32004ecd257SJohannes Berg 		else
32104ecd257SJohannes Berg 			local->smps_mode = IEEE80211_SMPS_STATIC;
32204ecd257SJohannes Berg 		ieee80211_hw_config(local, 0);
32304ecd257SJohannes Berg 	}
32404ecd257SJohannes Berg 
32504ecd257SJohannes Berg 	if (rx_chains_static == chanctx->conf.rx_chains_static &&
32604ecd257SJohannes Berg 	    rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
32704ecd257SJohannes Berg 		return;
32804ecd257SJohannes Berg 
32904ecd257SJohannes Berg 	chanctx->conf.rx_chains_static = rx_chains_static;
33004ecd257SJohannes Berg 	chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
33104ecd257SJohannes Berg 	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
33204ecd257SJohannes Berg }
33304ecd257SJohannes Berg 
334d01a1e65SMichal Kazior int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
3354bf88530SJohannes Berg 			      const struct cfg80211_chan_def *chandef,
336d01a1e65SMichal Kazior 			      enum ieee80211_chanctx_mode mode)
337d01a1e65SMichal Kazior {
338d01a1e65SMichal Kazior 	struct ieee80211_local *local = sdata->local;
339d01a1e65SMichal Kazior 	struct ieee80211_chanctx *ctx;
340d01a1e65SMichal Kazior 	int ret;
341d01a1e65SMichal Kazior 
34255de908aSJohannes Berg 	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
34355de908aSJohannes Berg 
344d01a1e65SMichal Kazior 	mutex_lock(&local->chanctx_mtx);
345d01a1e65SMichal Kazior 	__ieee80211_vif_release_channel(sdata);
346d01a1e65SMichal Kazior 
3474bf88530SJohannes Berg 	ctx = ieee80211_find_chanctx(local, chandef, mode);
348d01a1e65SMichal Kazior 	if (!ctx)
3494bf88530SJohannes Berg 		ctx = ieee80211_new_chanctx(local, chandef, mode);
350d01a1e65SMichal Kazior 	if (IS_ERR(ctx)) {
351d01a1e65SMichal Kazior 		ret = PTR_ERR(ctx);
352d01a1e65SMichal Kazior 		goto out;
353d01a1e65SMichal Kazior 	}
354d01a1e65SMichal Kazior 
3554bf88530SJohannes Berg 	sdata->vif.bss_conf.chandef = *chandef;
35655de908aSJohannes Berg 
357d01a1e65SMichal Kazior 	ret = ieee80211_assign_vif_chanctx(sdata, ctx);
358d01a1e65SMichal Kazior 	if (ret) {
359d01a1e65SMichal Kazior 		/* if assign fails refcount stays the same */
360d01a1e65SMichal Kazior 		if (ctx->refcount == 0)
361d01a1e65SMichal Kazior 			ieee80211_free_chanctx(local, ctx);
362d01a1e65SMichal Kazior 		goto out;
363d01a1e65SMichal Kazior 	}
364d01a1e65SMichal Kazior 
36504ecd257SJohannes Berg 	ieee80211_recalc_smps_chanctx(local, ctx);
366164eb02dSSimon Wunderlich 	ieee80211_recalc_radar_chanctx(local, ctx);
367d01a1e65SMichal Kazior  out:
368d01a1e65SMichal Kazior 	mutex_unlock(&local->chanctx_mtx);
369d01a1e65SMichal Kazior 	return ret;
370d01a1e65SMichal Kazior }
371d01a1e65SMichal Kazior 
372d01a1e65SMichal Kazior void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
373d01a1e65SMichal Kazior {
37455de908aSJohannes Berg 	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
37555de908aSJohannes Berg 
376d01a1e65SMichal Kazior 	mutex_lock(&sdata->local->chanctx_mtx);
377d01a1e65SMichal Kazior 	__ieee80211_vif_release_channel(sdata);
378d01a1e65SMichal Kazior 	mutex_unlock(&sdata->local->chanctx_mtx);
379d01a1e65SMichal Kazior }
3803448c005SJohannes Berg 
3814d76d21bSJohannes Berg void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
3824d76d21bSJohannes Berg {
3834d76d21bSJohannes Berg 	struct ieee80211_local *local = sdata->local;
3844d76d21bSJohannes Berg 	struct ieee80211_sub_if_data *ap;
3854d76d21bSJohannes Berg 	struct ieee80211_chanctx_conf *conf;
3864d76d21bSJohannes Berg 
3874d76d21bSJohannes Berg 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
3884d76d21bSJohannes Berg 		return;
3894d76d21bSJohannes Berg 
3904d76d21bSJohannes Berg 	ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
3914d76d21bSJohannes Berg 
3924d76d21bSJohannes Berg 	mutex_lock(&local->chanctx_mtx);
3934d76d21bSJohannes Berg 
3944d76d21bSJohannes Berg 	conf = rcu_dereference_protected(ap->vif.chanctx_conf,
3954d76d21bSJohannes Berg 					 lockdep_is_held(&local->chanctx_mtx));
3964d76d21bSJohannes Berg 	rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
3974d76d21bSJohannes Berg 	mutex_unlock(&local->chanctx_mtx);
3984d76d21bSJohannes Berg }
3994d76d21bSJohannes Berg 
4001f4ac5a6SJohannes Berg void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
4011f4ac5a6SJohannes Berg 					 bool clear)
4021f4ac5a6SJohannes Berg {
4031f4ac5a6SJohannes Berg 	struct ieee80211_local *local = sdata->local;
4041f4ac5a6SJohannes Berg 	struct ieee80211_sub_if_data *vlan;
4051f4ac5a6SJohannes Berg 	struct ieee80211_chanctx_conf *conf;
4061f4ac5a6SJohannes Berg 
4071f4ac5a6SJohannes Berg 	ASSERT_RTNL();
4081f4ac5a6SJohannes Berg 
4091f4ac5a6SJohannes Berg 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
4101f4ac5a6SJohannes Berg 		return;
4111f4ac5a6SJohannes Berg 
4121f4ac5a6SJohannes Berg 	mutex_lock(&local->chanctx_mtx);
4131f4ac5a6SJohannes Berg 
4141f4ac5a6SJohannes Berg 	/*
4151f4ac5a6SJohannes Berg 	 * Check that conf exists, even when clearing this function
4161f4ac5a6SJohannes Berg 	 * must be called with the AP's channel context still there
4171f4ac5a6SJohannes Berg 	 * as it would otherwise cause VLANs to have an invalid
4181f4ac5a6SJohannes Berg 	 * channel context pointer for a while, possibly pointing
4191f4ac5a6SJohannes Berg 	 * to a channel context that has already been freed.
4201f4ac5a6SJohannes Berg 	 */
4211f4ac5a6SJohannes Berg 	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
4221f4ac5a6SJohannes Berg 				lockdep_is_held(&local->chanctx_mtx));
4231f4ac5a6SJohannes Berg 	WARN_ON(!conf);
4241f4ac5a6SJohannes Berg 
4251f4ac5a6SJohannes Berg 	if (clear)
4261f4ac5a6SJohannes Berg 		conf = NULL;
4271f4ac5a6SJohannes Berg 
4281f4ac5a6SJohannes Berg 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
4291f4ac5a6SJohannes Berg 		rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
4301f4ac5a6SJohannes Berg 
4311f4ac5a6SJohannes Berg 	mutex_unlock(&local->chanctx_mtx);
4321f4ac5a6SJohannes Berg }
4331f4ac5a6SJohannes Berg 
4343448c005SJohannes Berg void ieee80211_iter_chan_contexts_atomic(
4353448c005SJohannes Berg 	struct ieee80211_hw *hw,
4363448c005SJohannes Berg 	void (*iter)(struct ieee80211_hw *hw,
4373448c005SJohannes Berg 		     struct ieee80211_chanctx_conf *chanctx_conf,
4383448c005SJohannes Berg 		     void *data),
4393448c005SJohannes Berg 	void *iter_data)
4403448c005SJohannes Berg {
4413448c005SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
4423448c005SJohannes Berg 	struct ieee80211_chanctx *ctx;
4433448c005SJohannes Berg 
4443448c005SJohannes Berg 	rcu_read_lock();
4453448c005SJohannes Berg 	list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
4468a61af65SJohannes Berg 		if (ctx->driver_present)
4473448c005SJohannes Berg 			iter(hw, &ctx->conf, iter_data);
4483448c005SJohannes Berg 	rcu_read_unlock();
4493448c005SJohannes Berg }
4503448c005SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);
451