xref: /OK3568_Linux_fs/kernel/net/mac80211/chan.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * mac80211 - channel management
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <linux/nl80211.h>
7*4882a593Smuzhiyun #include <linux/export.h>
8*4882a593Smuzhiyun #include <linux/rtnetlink.h>
9*4882a593Smuzhiyun #include <net/cfg80211.h>
10*4882a593Smuzhiyun #include "ieee80211_i.h"
11*4882a593Smuzhiyun #include "driver-ops.h"
12*4882a593Smuzhiyun 
ieee80211_chanctx_num_assigned(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)13*4882a593Smuzhiyun static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local,
14*4882a593Smuzhiyun 					  struct ieee80211_chanctx *ctx)
15*4882a593Smuzhiyun {
16*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *sdata;
17*4882a593Smuzhiyun 	int num = 0;
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun 	list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list)
22*4882a593Smuzhiyun 		num++;
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun 	return num;
25*4882a593Smuzhiyun }
26*4882a593Smuzhiyun 
ieee80211_chanctx_num_reserved(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)27*4882a593Smuzhiyun static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local,
28*4882a593Smuzhiyun 					  struct ieee80211_chanctx *ctx)
29*4882a593Smuzhiyun {
30*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *sdata;
31*4882a593Smuzhiyun 	int num = 0;
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun 	list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list)
36*4882a593Smuzhiyun 		num++;
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	return num;
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun 
ieee80211_chanctx_refcount(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)41*4882a593Smuzhiyun int ieee80211_chanctx_refcount(struct ieee80211_local *local,
42*4882a593Smuzhiyun 			       struct ieee80211_chanctx *ctx)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun 	return ieee80211_chanctx_num_assigned(local, ctx) +
45*4882a593Smuzhiyun 	       ieee80211_chanctx_num_reserved(local, ctx);
46*4882a593Smuzhiyun }
47*4882a593Smuzhiyun 
ieee80211_num_chanctx(struct ieee80211_local * local)48*4882a593Smuzhiyun static int ieee80211_num_chanctx(struct ieee80211_local *local)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun 	struct ieee80211_chanctx *ctx;
51*4882a593Smuzhiyun 	int num = 0;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	list_for_each_entry(ctx, &local->chanctx_list, list)
56*4882a593Smuzhiyun 		num++;
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	return num;
59*4882a593Smuzhiyun }
60*4882a593Smuzhiyun 
ieee80211_can_create_new_chanctx(struct ieee80211_local * local)61*4882a593Smuzhiyun static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
64*4882a593Smuzhiyun 	return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local);
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun static struct ieee80211_chanctx *
ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data * sdata)68*4882a593Smuzhiyun ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	struct ieee80211_local *local __maybe_unused = sdata->local;
71*4882a593Smuzhiyun 	struct ieee80211_chanctx_conf *conf;
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
74*4882a593Smuzhiyun 					 lockdep_is_held(&local->chanctx_mtx));
75*4882a593Smuzhiyun 	if (!conf)
76*4882a593Smuzhiyun 		return NULL;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	return container_of(conf, struct ieee80211_chanctx, conf);
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun static const struct cfg80211_chan_def *
ieee80211_chanctx_reserved_chandef(struct ieee80211_local * local,struct ieee80211_chanctx * ctx,const struct cfg80211_chan_def * compat)82*4882a593Smuzhiyun ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local,
83*4882a593Smuzhiyun 				   struct ieee80211_chanctx *ctx,
84*4882a593Smuzhiyun 				   const struct cfg80211_chan_def *compat)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *sdata;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	list_for_each_entry(sdata, &ctx->reserved_vifs,
91*4882a593Smuzhiyun 			    reserved_chanctx_list) {
92*4882a593Smuzhiyun 		if (!compat)
93*4882a593Smuzhiyun 			compat = &sdata->reserved_chandef;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 		compat = cfg80211_chandef_compatible(&sdata->reserved_chandef,
96*4882a593Smuzhiyun 						     compat);
97*4882a593Smuzhiyun 		if (!compat)
98*4882a593Smuzhiyun 			break;
99*4882a593Smuzhiyun 	}
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	return compat;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun static const struct cfg80211_chan_def *
ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local * local,struct ieee80211_chanctx * ctx,const struct cfg80211_chan_def * compat)105*4882a593Smuzhiyun ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local,
106*4882a593Smuzhiyun 				       struct ieee80211_chanctx *ctx,
107*4882a593Smuzhiyun 				       const struct cfg80211_chan_def *compat)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *sdata;
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	list_for_each_entry(sdata, &ctx->assigned_vifs,
114*4882a593Smuzhiyun 			    assigned_chanctx_list) {
115*4882a593Smuzhiyun 		if (sdata->reserved_chanctx != NULL)
116*4882a593Smuzhiyun 			continue;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 		if (!compat)
119*4882a593Smuzhiyun 			compat = &sdata->vif.bss_conf.chandef;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 		compat = cfg80211_chandef_compatible(
122*4882a593Smuzhiyun 				&sdata->vif.bss_conf.chandef, compat);
123*4882a593Smuzhiyun 		if (!compat)
124*4882a593Smuzhiyun 			break;
125*4882a593Smuzhiyun 	}
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	return compat;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun static const struct cfg80211_chan_def *
ieee80211_chanctx_combined_chandef(struct ieee80211_local * local,struct ieee80211_chanctx * ctx,const struct cfg80211_chan_def * compat)131*4882a593Smuzhiyun ieee80211_chanctx_combined_chandef(struct ieee80211_local *local,
132*4882a593Smuzhiyun 				   struct ieee80211_chanctx *ctx,
133*4882a593Smuzhiyun 				   const struct cfg80211_chan_def *compat)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat);
138*4882a593Smuzhiyun 	if (!compat)
139*4882a593Smuzhiyun 		return NULL;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat);
142*4882a593Smuzhiyun 	if (!compat)
143*4882a593Smuzhiyun 		return NULL;
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	return compat;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun static bool
ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local * local,struct ieee80211_chanctx * ctx,const struct cfg80211_chan_def * def)149*4882a593Smuzhiyun ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local,
150*4882a593Smuzhiyun 				      struct ieee80211_chanctx *ctx,
151*4882a593Smuzhiyun 				      const struct cfg80211_chan_def *def)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	if (ieee80211_chanctx_combined_chandef(local, ctx, def))
156*4882a593Smuzhiyun 		return true;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	if (!list_empty(&ctx->reserved_vifs) &&
159*4882a593Smuzhiyun 	    ieee80211_chanctx_reserved_chandef(local, ctx, def))
160*4882a593Smuzhiyun 		return true;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	return false;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun static struct ieee80211_chanctx *
ieee80211_find_reservation_chanctx(struct ieee80211_local * local,const struct cfg80211_chan_def * chandef,enum ieee80211_chanctx_mode mode)166*4882a593Smuzhiyun ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
167*4882a593Smuzhiyun 				   const struct cfg80211_chan_def *chandef,
168*4882a593Smuzhiyun 				   enum ieee80211_chanctx_mode mode)
169*4882a593Smuzhiyun {
170*4882a593Smuzhiyun 	struct ieee80211_chanctx *ctx;
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 	if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
175*4882a593Smuzhiyun 		return NULL;
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	list_for_each_entry(ctx, &local->chanctx_list, list) {
178*4882a593Smuzhiyun 		if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
179*4882a593Smuzhiyun 			continue;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
182*4882a593Smuzhiyun 			continue;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 		if (!ieee80211_chanctx_can_reserve_chandef(local, ctx,
185*4882a593Smuzhiyun 							   chandef))
186*4882a593Smuzhiyun 			continue;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 		return ctx;
189*4882a593Smuzhiyun 	}
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	return NULL;
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun 
ieee80211_get_sta_bw(struct ieee80211_sta * sta)194*4882a593Smuzhiyun enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun 	switch (sta->bandwidth) {
197*4882a593Smuzhiyun 	case IEEE80211_STA_RX_BW_20:
198*4882a593Smuzhiyun 		if (sta->ht_cap.ht_supported)
199*4882a593Smuzhiyun 			return NL80211_CHAN_WIDTH_20;
200*4882a593Smuzhiyun 		else
201*4882a593Smuzhiyun 			return NL80211_CHAN_WIDTH_20_NOHT;
202*4882a593Smuzhiyun 	case IEEE80211_STA_RX_BW_40:
203*4882a593Smuzhiyun 		return NL80211_CHAN_WIDTH_40;
204*4882a593Smuzhiyun 	case IEEE80211_STA_RX_BW_80:
205*4882a593Smuzhiyun 		return NL80211_CHAN_WIDTH_80;
206*4882a593Smuzhiyun 	case IEEE80211_STA_RX_BW_160:
207*4882a593Smuzhiyun 		/*
208*4882a593Smuzhiyun 		 * This applied for both 160 and 80+80. since we use
209*4882a593Smuzhiyun 		 * the returned value to consider degradation of
210*4882a593Smuzhiyun 		 * ctx->conf.min_def, we have to make sure to take
211*4882a593Smuzhiyun 		 * the bigger one (NL80211_CHAN_WIDTH_160).
212*4882a593Smuzhiyun 		 * Otherwise we might try degrading even when not
213*4882a593Smuzhiyun 		 * needed, as the max required sta_bw returned (80+80)
214*4882a593Smuzhiyun 		 * might be smaller than the configured bw (160).
215*4882a593Smuzhiyun 		 */
216*4882a593Smuzhiyun 		return NL80211_CHAN_WIDTH_160;
217*4882a593Smuzhiyun 	default:
218*4882a593Smuzhiyun 		WARN_ON(1);
219*4882a593Smuzhiyun 		return NL80211_CHAN_WIDTH_20;
220*4882a593Smuzhiyun 	}
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun static enum nl80211_chan_width
ieee80211_get_max_required_bw(struct ieee80211_sub_if_data * sdata)224*4882a593Smuzhiyun ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun 	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
227*4882a593Smuzhiyun 	struct sta_info *sta;
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	rcu_read_lock();
230*4882a593Smuzhiyun 	list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
231*4882a593Smuzhiyun 		if (sdata != sta->sdata &&
232*4882a593Smuzhiyun 		    !(sta->sdata->bss && sta->sdata->bss == sdata->bss))
233*4882a593Smuzhiyun 			continue;
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 		max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta));
236*4882a593Smuzhiyun 	}
237*4882a593Smuzhiyun 	rcu_read_unlock();
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	return max_bw;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun static enum nl80211_chan_width
ieee80211_get_chanctx_max_required_bw(struct ieee80211_local * local,struct ieee80211_chanctx_conf * conf)243*4882a593Smuzhiyun ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
244*4882a593Smuzhiyun 				      struct ieee80211_chanctx_conf *conf)
245*4882a593Smuzhiyun {
246*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *sdata;
247*4882a593Smuzhiyun 	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	rcu_read_lock();
250*4882a593Smuzhiyun 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
251*4882a593Smuzhiyun 		struct ieee80211_vif *vif = &sdata->vif;
252*4882a593Smuzhiyun 		enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 		if (!ieee80211_sdata_running(sdata))
255*4882a593Smuzhiyun 			continue;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
258*4882a593Smuzhiyun 			continue;
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 		switch (vif->type) {
261*4882a593Smuzhiyun 		case NL80211_IFTYPE_AP:
262*4882a593Smuzhiyun 		case NL80211_IFTYPE_AP_VLAN:
263*4882a593Smuzhiyun 			width = ieee80211_get_max_required_bw(sdata);
264*4882a593Smuzhiyun 			break;
265*4882a593Smuzhiyun 		case NL80211_IFTYPE_STATION:
266*4882a593Smuzhiyun 			/*
267*4882a593Smuzhiyun 			 * The ap's sta->bandwidth is not set yet at this
268*4882a593Smuzhiyun 			 * point, so take the width from the chandef, but
269*4882a593Smuzhiyun 			 * account also for TDLS peers
270*4882a593Smuzhiyun 			 */
271*4882a593Smuzhiyun 			width = max(vif->bss_conf.chandef.width,
272*4882a593Smuzhiyun 				    ieee80211_get_max_required_bw(sdata));
273*4882a593Smuzhiyun 			break;
274*4882a593Smuzhiyun 		case NL80211_IFTYPE_P2P_DEVICE:
275*4882a593Smuzhiyun 		case NL80211_IFTYPE_NAN:
276*4882a593Smuzhiyun 			continue;
277*4882a593Smuzhiyun 		case NL80211_IFTYPE_ADHOC:
278*4882a593Smuzhiyun 		case NL80211_IFTYPE_WDS:
279*4882a593Smuzhiyun 		case NL80211_IFTYPE_MESH_POINT:
280*4882a593Smuzhiyun 		case NL80211_IFTYPE_OCB:
281*4882a593Smuzhiyun 			width = vif->bss_conf.chandef.width;
282*4882a593Smuzhiyun 			break;
283*4882a593Smuzhiyun 		case NL80211_IFTYPE_UNSPECIFIED:
284*4882a593Smuzhiyun 		case NUM_NL80211_IFTYPES:
285*4882a593Smuzhiyun 		case NL80211_IFTYPE_MONITOR:
286*4882a593Smuzhiyun 		case NL80211_IFTYPE_P2P_CLIENT:
287*4882a593Smuzhiyun 		case NL80211_IFTYPE_P2P_GO:
288*4882a593Smuzhiyun 			WARN_ON_ONCE(1);
289*4882a593Smuzhiyun 		}
290*4882a593Smuzhiyun 		max_bw = max(max_bw, width);
291*4882a593Smuzhiyun 	}
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	/* use the configured bandwidth in case of monitor interface */
294*4882a593Smuzhiyun 	sdata = rcu_dereference(local->monitor_sdata);
295*4882a593Smuzhiyun 	if (sdata && rcu_access_pointer(sdata->vif.chanctx_conf) == conf)
296*4882a593Smuzhiyun 		max_bw = max(max_bw, conf->def.width);
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	rcu_read_unlock();
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	return max_bw;
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun /*
304*4882a593Smuzhiyun  * recalc the min required chan width of the channel context, which is
305*4882a593Smuzhiyun  * the max of min required widths of all the interfaces bound to this
306*4882a593Smuzhiyun  * channel context.
307*4882a593Smuzhiyun  */
ieee80211_recalc_chanctx_min_def(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)308*4882a593Smuzhiyun void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
309*4882a593Smuzhiyun 				      struct ieee80211_chanctx *ctx)
310*4882a593Smuzhiyun {
311*4882a593Smuzhiyun 	enum nl80211_chan_width max_bw;
312*4882a593Smuzhiyun 	struct cfg80211_chan_def min_def;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	/* don't optimize non-20MHz based and radar_enabled confs */
317*4882a593Smuzhiyun 	if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 ||
318*4882a593Smuzhiyun 	    ctx->conf.def.width == NL80211_CHAN_WIDTH_10 ||
319*4882a593Smuzhiyun 	    ctx->conf.def.width == NL80211_CHAN_WIDTH_1 ||
320*4882a593Smuzhiyun 	    ctx->conf.def.width == NL80211_CHAN_WIDTH_2 ||
321*4882a593Smuzhiyun 	    ctx->conf.def.width == NL80211_CHAN_WIDTH_4 ||
322*4882a593Smuzhiyun 	    ctx->conf.def.width == NL80211_CHAN_WIDTH_8 ||
323*4882a593Smuzhiyun 	    ctx->conf.def.width == NL80211_CHAN_WIDTH_16 ||
324*4882a593Smuzhiyun 	    ctx->conf.radar_enabled) {
325*4882a593Smuzhiyun 		ctx->conf.min_def = ctx->conf.def;
326*4882a593Smuzhiyun 		return;
327*4882a593Smuzhiyun 	}
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf);
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	/* downgrade chandef up to max_bw */
332*4882a593Smuzhiyun 	min_def = ctx->conf.def;
333*4882a593Smuzhiyun 	while (min_def.width > max_bw)
334*4882a593Smuzhiyun 		ieee80211_chandef_downgrade(&min_def);
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def))
337*4882a593Smuzhiyun 		return;
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	ctx->conf.min_def = min_def;
340*4882a593Smuzhiyun 	if (!ctx->driver_present)
341*4882a593Smuzhiyun 		return;
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH);
344*4882a593Smuzhiyun }
345*4882a593Smuzhiyun 
ieee80211_change_chanctx(struct ieee80211_local * local,struct ieee80211_chanctx * ctx,const struct cfg80211_chan_def * chandef)346*4882a593Smuzhiyun static void ieee80211_change_chanctx(struct ieee80211_local *local,
347*4882a593Smuzhiyun 				     struct ieee80211_chanctx *ctx,
348*4882a593Smuzhiyun 				     const struct cfg80211_chan_def *chandef)
349*4882a593Smuzhiyun {
350*4882a593Smuzhiyun 	if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) {
351*4882a593Smuzhiyun 		ieee80211_recalc_chanctx_min_def(local, ctx);
352*4882a593Smuzhiyun 		return;
353*4882a593Smuzhiyun 	}
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	ctx->conf.def = *chandef;
358*4882a593Smuzhiyun 	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
359*4882a593Smuzhiyun 	ieee80211_recalc_chanctx_min_def(local, ctx);
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 	if (!local->use_chanctx) {
362*4882a593Smuzhiyun 		local->_oper_chandef = *chandef;
363*4882a593Smuzhiyun 		ieee80211_hw_config(local, 0);
364*4882a593Smuzhiyun 	}
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun static struct ieee80211_chanctx *
ieee80211_find_chanctx(struct ieee80211_local * local,const struct cfg80211_chan_def * chandef,enum ieee80211_chanctx_mode mode)368*4882a593Smuzhiyun ieee80211_find_chanctx(struct ieee80211_local *local,
369*4882a593Smuzhiyun 		       const struct cfg80211_chan_def *chandef,
370*4882a593Smuzhiyun 		       enum ieee80211_chanctx_mode mode)
371*4882a593Smuzhiyun {
372*4882a593Smuzhiyun 	struct ieee80211_chanctx *ctx;
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
377*4882a593Smuzhiyun 		return NULL;
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	list_for_each_entry(ctx, &local->chanctx_list, list) {
380*4882a593Smuzhiyun 		const struct cfg80211_chan_def *compat;
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE)
383*4882a593Smuzhiyun 			continue;
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
386*4882a593Smuzhiyun 			continue;
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 		compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
389*4882a593Smuzhiyun 		if (!compat)
390*4882a593Smuzhiyun 			continue;
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 		compat = ieee80211_chanctx_reserved_chandef(local, ctx,
393*4882a593Smuzhiyun 							    compat);
394*4882a593Smuzhiyun 		if (!compat)
395*4882a593Smuzhiyun 			continue;
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 		ieee80211_change_chanctx(local, ctx, compat);
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 		return ctx;
400*4882a593Smuzhiyun 	}
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	return NULL;
403*4882a593Smuzhiyun }
404*4882a593Smuzhiyun 
ieee80211_is_radar_required(struct ieee80211_local * local)405*4882a593Smuzhiyun bool ieee80211_is_radar_required(struct ieee80211_local *local)
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *sdata;
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	lockdep_assert_held(&local->mtx);
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	rcu_read_lock();
412*4882a593Smuzhiyun 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
413*4882a593Smuzhiyun 		if (sdata->radar_required) {
414*4882a593Smuzhiyun 			rcu_read_unlock();
415*4882a593Smuzhiyun 			return true;
416*4882a593Smuzhiyun 		}
417*4882a593Smuzhiyun 	}
418*4882a593Smuzhiyun 	rcu_read_unlock();
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	return false;
421*4882a593Smuzhiyun }
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun static bool
ieee80211_chanctx_radar_required(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)424*4882a593Smuzhiyun ieee80211_chanctx_radar_required(struct ieee80211_local *local,
425*4882a593Smuzhiyun 				 struct ieee80211_chanctx *ctx)
426*4882a593Smuzhiyun {
427*4882a593Smuzhiyun 	struct ieee80211_chanctx_conf *conf = &ctx->conf;
428*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *sdata;
429*4882a593Smuzhiyun 	bool required = false;
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
432*4882a593Smuzhiyun 	lockdep_assert_held(&local->mtx);
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 	rcu_read_lock();
435*4882a593Smuzhiyun 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
436*4882a593Smuzhiyun 		if (!ieee80211_sdata_running(sdata))
437*4882a593Smuzhiyun 			continue;
438*4882a593Smuzhiyun 		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
439*4882a593Smuzhiyun 			continue;
440*4882a593Smuzhiyun 		if (!sdata->radar_required)
441*4882a593Smuzhiyun 			continue;
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 		required = true;
444*4882a593Smuzhiyun 		break;
445*4882a593Smuzhiyun 	}
446*4882a593Smuzhiyun 	rcu_read_unlock();
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	return required;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun static struct ieee80211_chanctx *
ieee80211_alloc_chanctx(struct ieee80211_local * local,const struct cfg80211_chan_def * chandef,enum ieee80211_chanctx_mode mode)452*4882a593Smuzhiyun ieee80211_alloc_chanctx(struct ieee80211_local *local,
453*4882a593Smuzhiyun 			const struct cfg80211_chan_def *chandef,
454*4882a593Smuzhiyun 			enum ieee80211_chanctx_mode mode)
455*4882a593Smuzhiyun {
456*4882a593Smuzhiyun 	struct ieee80211_chanctx *ctx;
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
461*4882a593Smuzhiyun 	if (!ctx)
462*4882a593Smuzhiyun 		return NULL;
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun 	INIT_LIST_HEAD(&ctx->assigned_vifs);
465*4882a593Smuzhiyun 	INIT_LIST_HEAD(&ctx->reserved_vifs);
466*4882a593Smuzhiyun 	ctx->conf.def = *chandef;
467*4882a593Smuzhiyun 	ctx->conf.rx_chains_static = 1;
468*4882a593Smuzhiyun 	ctx->conf.rx_chains_dynamic = 1;
469*4882a593Smuzhiyun 	ctx->mode = mode;
470*4882a593Smuzhiyun 	ctx->conf.radar_enabled = false;
471*4882a593Smuzhiyun 	ieee80211_recalc_chanctx_min_def(local, ctx);
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 	return ctx;
474*4882a593Smuzhiyun }
475*4882a593Smuzhiyun 
ieee80211_add_chanctx(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)476*4882a593Smuzhiyun static int ieee80211_add_chanctx(struct ieee80211_local *local,
477*4882a593Smuzhiyun 				 struct ieee80211_chanctx *ctx)
478*4882a593Smuzhiyun {
479*4882a593Smuzhiyun 	u32 changed;
480*4882a593Smuzhiyun 	int err;
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	lockdep_assert_held(&local->mtx);
483*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	if (!local->use_chanctx)
486*4882a593Smuzhiyun 		local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 	/* turn idle off *before* setting channel -- some drivers need that */
489*4882a593Smuzhiyun 	changed = ieee80211_idle_off(local);
490*4882a593Smuzhiyun 	if (changed)
491*4882a593Smuzhiyun 		ieee80211_hw_config(local, changed);
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 	if (!local->use_chanctx) {
494*4882a593Smuzhiyun 		local->_oper_chandef = ctx->conf.def;
495*4882a593Smuzhiyun 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
496*4882a593Smuzhiyun 	} else {
497*4882a593Smuzhiyun 		err = drv_add_chanctx(local, ctx);
498*4882a593Smuzhiyun 		if (err) {
499*4882a593Smuzhiyun 			ieee80211_recalc_idle(local);
500*4882a593Smuzhiyun 			return err;
501*4882a593Smuzhiyun 		}
502*4882a593Smuzhiyun 	}
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	return 0;
505*4882a593Smuzhiyun }
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun static struct ieee80211_chanctx *
ieee80211_new_chanctx(struct ieee80211_local * local,const struct cfg80211_chan_def * chandef,enum ieee80211_chanctx_mode mode)508*4882a593Smuzhiyun ieee80211_new_chanctx(struct ieee80211_local *local,
509*4882a593Smuzhiyun 		      const struct cfg80211_chan_def *chandef,
510*4882a593Smuzhiyun 		      enum ieee80211_chanctx_mode mode)
511*4882a593Smuzhiyun {
512*4882a593Smuzhiyun 	struct ieee80211_chanctx *ctx;
513*4882a593Smuzhiyun 	int err;
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 	lockdep_assert_held(&local->mtx);
516*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun 	ctx = ieee80211_alloc_chanctx(local, chandef, mode);
519*4882a593Smuzhiyun 	if (!ctx)
520*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun 	err = ieee80211_add_chanctx(local, ctx);
523*4882a593Smuzhiyun 	if (err) {
524*4882a593Smuzhiyun 		kfree(ctx);
525*4882a593Smuzhiyun 		return ERR_PTR(err);
526*4882a593Smuzhiyun 	}
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	list_add_rcu(&ctx->list, &local->chanctx_list);
529*4882a593Smuzhiyun 	return ctx;
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun 
ieee80211_del_chanctx(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)532*4882a593Smuzhiyun static void ieee80211_del_chanctx(struct ieee80211_local *local,
533*4882a593Smuzhiyun 				  struct ieee80211_chanctx *ctx)
534*4882a593Smuzhiyun {
535*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun 	if (!local->use_chanctx) {
538*4882a593Smuzhiyun 		struct cfg80211_chan_def *chandef = &local->_oper_chandef;
539*4882a593Smuzhiyun 		/* S1G doesn't have 20MHz, so get the correct width for the
540*4882a593Smuzhiyun 		 * current channel.
541*4882a593Smuzhiyun 		 */
542*4882a593Smuzhiyun 		if (chandef->chan->band == NL80211_BAND_S1GHZ)
543*4882a593Smuzhiyun 			chandef->width =
544*4882a593Smuzhiyun 				ieee80211_s1g_channel_width(chandef->chan);
545*4882a593Smuzhiyun 		else
546*4882a593Smuzhiyun 			chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
547*4882a593Smuzhiyun 		chandef->center_freq1 = chandef->chan->center_freq;
548*4882a593Smuzhiyun 		chandef->freq1_offset = chandef->chan->freq_offset;
549*4882a593Smuzhiyun 		chandef->center_freq2 = 0;
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 		/* NOTE: Disabling radar is only valid here for
552*4882a593Smuzhiyun 		 * single channel context. To be sure, check it ...
553*4882a593Smuzhiyun 		 */
554*4882a593Smuzhiyun 		WARN_ON(local->hw.conf.radar_enabled &&
555*4882a593Smuzhiyun 			!list_empty(&local->chanctx_list));
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 		local->hw.conf.radar_enabled = false;
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
560*4882a593Smuzhiyun 	} else {
561*4882a593Smuzhiyun 		drv_remove_chanctx(local, ctx);
562*4882a593Smuzhiyun 	}
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	ieee80211_recalc_idle(local);
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun 
ieee80211_free_chanctx(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)567*4882a593Smuzhiyun static void ieee80211_free_chanctx(struct ieee80211_local *local,
568*4882a593Smuzhiyun 				   struct ieee80211_chanctx *ctx)
569*4882a593Smuzhiyun {
570*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0);
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	list_del_rcu(&ctx->list);
575*4882a593Smuzhiyun 	ieee80211_del_chanctx(local, ctx);
576*4882a593Smuzhiyun 	kfree_rcu(ctx, rcu_head);
577*4882a593Smuzhiyun }
578*4882a593Smuzhiyun 
ieee80211_recalc_chanctx_chantype(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)579*4882a593Smuzhiyun void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
580*4882a593Smuzhiyun 				       struct ieee80211_chanctx *ctx)
581*4882a593Smuzhiyun {
582*4882a593Smuzhiyun 	struct ieee80211_chanctx_conf *conf = &ctx->conf;
583*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *sdata;
584*4882a593Smuzhiyun 	const struct cfg80211_chan_def *compat = NULL;
585*4882a593Smuzhiyun 	struct sta_info *sta;
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	rcu_read_lock();
590*4882a593Smuzhiyun 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 		if (!ieee80211_sdata_running(sdata))
593*4882a593Smuzhiyun 			continue;
594*4882a593Smuzhiyun 		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
595*4882a593Smuzhiyun 			continue;
596*4882a593Smuzhiyun 		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
597*4882a593Smuzhiyun 			continue;
598*4882a593Smuzhiyun 
599*4882a593Smuzhiyun 		if (!compat)
600*4882a593Smuzhiyun 			compat = &sdata->vif.bss_conf.chandef;
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 		compat = cfg80211_chandef_compatible(
603*4882a593Smuzhiyun 				&sdata->vif.bss_conf.chandef, compat);
604*4882a593Smuzhiyun 		if (WARN_ON_ONCE(!compat))
605*4882a593Smuzhiyun 			break;
606*4882a593Smuzhiyun 	}
607*4882a593Smuzhiyun 
608*4882a593Smuzhiyun 	/* TDLS peers can sometimes affect the chandef width */
609*4882a593Smuzhiyun 	list_for_each_entry_rcu(sta, &local->sta_list, list) {
610*4882a593Smuzhiyun 		if (!sta->uploaded ||
611*4882a593Smuzhiyun 		    !test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) ||
612*4882a593Smuzhiyun 		    !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
613*4882a593Smuzhiyun 		    !sta->tdls_chandef.chan)
614*4882a593Smuzhiyun 			continue;
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 		compat = cfg80211_chandef_compatible(&sta->tdls_chandef,
617*4882a593Smuzhiyun 						     compat);
618*4882a593Smuzhiyun 		if (WARN_ON_ONCE(!compat))
619*4882a593Smuzhiyun 			break;
620*4882a593Smuzhiyun 	}
621*4882a593Smuzhiyun 	rcu_read_unlock();
622*4882a593Smuzhiyun 
623*4882a593Smuzhiyun 	if (!compat)
624*4882a593Smuzhiyun 		return;
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	ieee80211_change_chanctx(local, ctx, compat);
627*4882a593Smuzhiyun }
628*4882a593Smuzhiyun 
ieee80211_recalc_radar_chanctx(struct ieee80211_local * local,struct ieee80211_chanctx * chanctx)629*4882a593Smuzhiyun static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
630*4882a593Smuzhiyun 					   struct ieee80211_chanctx *chanctx)
631*4882a593Smuzhiyun {
632*4882a593Smuzhiyun 	bool radar_enabled;
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
635*4882a593Smuzhiyun 	/* for ieee80211_is_radar_required */
636*4882a593Smuzhiyun 	lockdep_assert_held(&local->mtx);
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun 	radar_enabled = ieee80211_chanctx_radar_required(local, chanctx);
639*4882a593Smuzhiyun 
640*4882a593Smuzhiyun 	if (radar_enabled == chanctx->conf.radar_enabled)
641*4882a593Smuzhiyun 		return;
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun 	chanctx->conf.radar_enabled = radar_enabled;
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun 	if (!local->use_chanctx) {
646*4882a593Smuzhiyun 		local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
647*4882a593Smuzhiyun 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
648*4882a593Smuzhiyun 	}
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
651*4882a593Smuzhiyun }
652*4882a593Smuzhiyun 
ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data * sdata,struct ieee80211_chanctx * new_ctx)653*4882a593Smuzhiyun static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
654*4882a593Smuzhiyun 					struct ieee80211_chanctx *new_ctx)
655*4882a593Smuzhiyun {
656*4882a593Smuzhiyun 	struct ieee80211_local *local = sdata->local;
657*4882a593Smuzhiyun 	struct ieee80211_chanctx_conf *conf;
658*4882a593Smuzhiyun 	struct ieee80211_chanctx *curr_ctx = NULL;
659*4882a593Smuzhiyun 	int ret = 0;
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun 	if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_NAN))
662*4882a593Smuzhiyun 		return -ENOTSUPP;
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun 	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
665*4882a593Smuzhiyun 					 lockdep_is_held(&local->chanctx_mtx));
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun 	if (conf) {
668*4882a593Smuzhiyun 		curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
669*4882a593Smuzhiyun 
670*4882a593Smuzhiyun 		drv_unassign_vif_chanctx(local, sdata, curr_ctx);
671*4882a593Smuzhiyun 		conf = NULL;
672*4882a593Smuzhiyun 		list_del(&sdata->assigned_chanctx_list);
673*4882a593Smuzhiyun 	}
674*4882a593Smuzhiyun 
675*4882a593Smuzhiyun 	if (new_ctx) {
676*4882a593Smuzhiyun 		ret = drv_assign_vif_chanctx(local, sdata, new_ctx);
677*4882a593Smuzhiyun 		if (ret)
678*4882a593Smuzhiyun 			goto out;
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun 		conf = &new_ctx->conf;
681*4882a593Smuzhiyun 		list_add(&sdata->assigned_chanctx_list,
682*4882a593Smuzhiyun 			 &new_ctx->assigned_vifs);
683*4882a593Smuzhiyun 	}
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun out:
686*4882a593Smuzhiyun 	rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 	sdata->vif.bss_conf.idle = !conf;
689*4882a593Smuzhiyun 
690*4882a593Smuzhiyun 	if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) {
691*4882a593Smuzhiyun 		ieee80211_recalc_chanctx_chantype(local, curr_ctx);
692*4882a593Smuzhiyun 		ieee80211_recalc_smps_chanctx(local, curr_ctx);
693*4882a593Smuzhiyun 		ieee80211_recalc_radar_chanctx(local, curr_ctx);
694*4882a593Smuzhiyun 		ieee80211_recalc_chanctx_min_def(local, curr_ctx);
695*4882a593Smuzhiyun 	}
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
698*4882a593Smuzhiyun 		ieee80211_recalc_txpower(sdata, false);
699*4882a593Smuzhiyun 		ieee80211_recalc_chanctx_min_def(local, new_ctx);
700*4882a593Smuzhiyun 	}
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun 	if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
703*4882a593Smuzhiyun 	    sdata->vif.type != NL80211_IFTYPE_MONITOR)
704*4882a593Smuzhiyun 		ieee80211_bss_info_change_notify(sdata,
705*4882a593Smuzhiyun 						 BSS_CHANGED_IDLE);
706*4882a593Smuzhiyun 
707*4882a593Smuzhiyun 	ieee80211_check_fast_xmit_iface(sdata);
708*4882a593Smuzhiyun 
709*4882a593Smuzhiyun 	return ret;
710*4882a593Smuzhiyun }
711*4882a593Smuzhiyun 
ieee80211_recalc_smps_chanctx(struct ieee80211_local * local,struct ieee80211_chanctx * chanctx)712*4882a593Smuzhiyun void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
713*4882a593Smuzhiyun 				   struct ieee80211_chanctx *chanctx)
714*4882a593Smuzhiyun {
715*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *sdata;
716*4882a593Smuzhiyun 	u8 rx_chains_static, rx_chains_dynamic;
717*4882a593Smuzhiyun 
718*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun 	rx_chains_static = 1;
721*4882a593Smuzhiyun 	rx_chains_dynamic = 1;
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun 	rcu_read_lock();
724*4882a593Smuzhiyun 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
725*4882a593Smuzhiyun 		u8 needed_static, needed_dynamic;
726*4882a593Smuzhiyun 
727*4882a593Smuzhiyun 		if (!ieee80211_sdata_running(sdata))
728*4882a593Smuzhiyun 			continue;
729*4882a593Smuzhiyun 
730*4882a593Smuzhiyun 		if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
731*4882a593Smuzhiyun 						&chanctx->conf)
732*4882a593Smuzhiyun 			continue;
733*4882a593Smuzhiyun 
734*4882a593Smuzhiyun 		switch (sdata->vif.type) {
735*4882a593Smuzhiyun 		case NL80211_IFTYPE_P2P_DEVICE:
736*4882a593Smuzhiyun 		case NL80211_IFTYPE_NAN:
737*4882a593Smuzhiyun 			continue;
738*4882a593Smuzhiyun 		case NL80211_IFTYPE_STATION:
739*4882a593Smuzhiyun 			if (!sdata->u.mgd.associated)
740*4882a593Smuzhiyun 				continue;
741*4882a593Smuzhiyun 			break;
742*4882a593Smuzhiyun 		case NL80211_IFTYPE_AP_VLAN:
743*4882a593Smuzhiyun 			continue;
744*4882a593Smuzhiyun 		case NL80211_IFTYPE_AP:
745*4882a593Smuzhiyun 		case NL80211_IFTYPE_ADHOC:
746*4882a593Smuzhiyun 		case NL80211_IFTYPE_WDS:
747*4882a593Smuzhiyun 		case NL80211_IFTYPE_MESH_POINT:
748*4882a593Smuzhiyun 		case NL80211_IFTYPE_OCB:
749*4882a593Smuzhiyun 			break;
750*4882a593Smuzhiyun 		default:
751*4882a593Smuzhiyun 			WARN_ON_ONCE(1);
752*4882a593Smuzhiyun 		}
753*4882a593Smuzhiyun 
754*4882a593Smuzhiyun 		switch (sdata->smps_mode) {
755*4882a593Smuzhiyun 		default:
756*4882a593Smuzhiyun 			WARN_ONCE(1, "Invalid SMPS mode %d\n",
757*4882a593Smuzhiyun 				  sdata->smps_mode);
758*4882a593Smuzhiyun 			fallthrough;
759*4882a593Smuzhiyun 		case IEEE80211_SMPS_OFF:
760*4882a593Smuzhiyun 			needed_static = sdata->needed_rx_chains;
761*4882a593Smuzhiyun 			needed_dynamic = sdata->needed_rx_chains;
762*4882a593Smuzhiyun 			break;
763*4882a593Smuzhiyun 		case IEEE80211_SMPS_DYNAMIC:
764*4882a593Smuzhiyun 			needed_static = 1;
765*4882a593Smuzhiyun 			needed_dynamic = sdata->needed_rx_chains;
766*4882a593Smuzhiyun 			break;
767*4882a593Smuzhiyun 		case IEEE80211_SMPS_STATIC:
768*4882a593Smuzhiyun 			needed_static = 1;
769*4882a593Smuzhiyun 			needed_dynamic = 1;
770*4882a593Smuzhiyun 			break;
771*4882a593Smuzhiyun 		}
772*4882a593Smuzhiyun 
773*4882a593Smuzhiyun 		rx_chains_static = max(rx_chains_static, needed_static);
774*4882a593Smuzhiyun 		rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
775*4882a593Smuzhiyun 	}
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun 	/* Disable SMPS for the monitor interface */
778*4882a593Smuzhiyun 	sdata = rcu_dereference(local->monitor_sdata);
779*4882a593Smuzhiyun 	if (sdata &&
780*4882a593Smuzhiyun 	    rcu_access_pointer(sdata->vif.chanctx_conf) == &chanctx->conf)
781*4882a593Smuzhiyun 		rx_chains_dynamic = rx_chains_static = local->rx_chains;
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	rcu_read_unlock();
784*4882a593Smuzhiyun 
785*4882a593Smuzhiyun 	if (!local->use_chanctx) {
786*4882a593Smuzhiyun 		if (rx_chains_static > 1)
787*4882a593Smuzhiyun 			local->smps_mode = IEEE80211_SMPS_OFF;
788*4882a593Smuzhiyun 		else if (rx_chains_dynamic > 1)
789*4882a593Smuzhiyun 			local->smps_mode = IEEE80211_SMPS_DYNAMIC;
790*4882a593Smuzhiyun 		else
791*4882a593Smuzhiyun 			local->smps_mode = IEEE80211_SMPS_STATIC;
792*4882a593Smuzhiyun 		ieee80211_hw_config(local, 0);
793*4882a593Smuzhiyun 	}
794*4882a593Smuzhiyun 
795*4882a593Smuzhiyun 	if (rx_chains_static == chanctx->conf.rx_chains_static &&
796*4882a593Smuzhiyun 	    rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
797*4882a593Smuzhiyun 		return;
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun 	chanctx->conf.rx_chains_static = rx_chains_static;
800*4882a593Smuzhiyun 	chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
801*4882a593Smuzhiyun 	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
802*4882a593Smuzhiyun }
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun static void
__ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data * sdata,bool clear)805*4882a593Smuzhiyun __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
806*4882a593Smuzhiyun 				      bool clear)
807*4882a593Smuzhiyun {
808*4882a593Smuzhiyun 	struct ieee80211_local *local __maybe_unused = sdata->local;
809*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *vlan;
810*4882a593Smuzhiyun 	struct ieee80211_chanctx_conf *conf;
811*4882a593Smuzhiyun 
812*4882a593Smuzhiyun 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
813*4882a593Smuzhiyun 		return;
814*4882a593Smuzhiyun 
815*4882a593Smuzhiyun 	lockdep_assert_held(&local->mtx);
816*4882a593Smuzhiyun 
817*4882a593Smuzhiyun 	/* Check that conf exists, even when clearing this function
818*4882a593Smuzhiyun 	 * must be called with the AP's channel context still there
819*4882a593Smuzhiyun 	 * as it would otherwise cause VLANs to have an invalid
820*4882a593Smuzhiyun 	 * channel context pointer for a while, possibly pointing
821*4882a593Smuzhiyun 	 * to a channel context that has already been freed.
822*4882a593Smuzhiyun 	 */
823*4882a593Smuzhiyun 	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
824*4882a593Smuzhiyun 					 lockdep_is_held(&local->chanctx_mtx));
825*4882a593Smuzhiyun 	WARN_ON(!conf);
826*4882a593Smuzhiyun 
827*4882a593Smuzhiyun 	if (clear)
828*4882a593Smuzhiyun 		conf = NULL;
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
831*4882a593Smuzhiyun 		rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
832*4882a593Smuzhiyun }
833*4882a593Smuzhiyun 
ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data * sdata,bool clear)834*4882a593Smuzhiyun void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
835*4882a593Smuzhiyun 					 bool clear)
836*4882a593Smuzhiyun {
837*4882a593Smuzhiyun 	struct ieee80211_local *local = sdata->local;
838*4882a593Smuzhiyun 
839*4882a593Smuzhiyun 	mutex_lock(&local->chanctx_mtx);
840*4882a593Smuzhiyun 
841*4882a593Smuzhiyun 	__ieee80211_vif_copy_chanctx_to_vlans(sdata, clear);
842*4882a593Smuzhiyun 
843*4882a593Smuzhiyun 	mutex_unlock(&local->chanctx_mtx);
844*4882a593Smuzhiyun }
845*4882a593Smuzhiyun 
ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data * sdata)846*4882a593Smuzhiyun int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata)
847*4882a593Smuzhiyun {
848*4882a593Smuzhiyun 	struct ieee80211_chanctx *ctx = sdata->reserved_chanctx;
849*4882a593Smuzhiyun 
850*4882a593Smuzhiyun 	lockdep_assert_held(&sdata->local->chanctx_mtx);
851*4882a593Smuzhiyun 
852*4882a593Smuzhiyun 	if (WARN_ON(!ctx))
853*4882a593Smuzhiyun 		return -EINVAL;
854*4882a593Smuzhiyun 
855*4882a593Smuzhiyun 	list_del(&sdata->reserved_chanctx_list);
856*4882a593Smuzhiyun 	sdata->reserved_chanctx = NULL;
857*4882a593Smuzhiyun 
858*4882a593Smuzhiyun 	if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) {
859*4882a593Smuzhiyun 		if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
860*4882a593Smuzhiyun 			if (WARN_ON(!ctx->replace_ctx))
861*4882a593Smuzhiyun 				return -EINVAL;
862*4882a593Smuzhiyun 
863*4882a593Smuzhiyun 			WARN_ON(ctx->replace_ctx->replace_state !=
864*4882a593Smuzhiyun 			        IEEE80211_CHANCTX_WILL_BE_REPLACED);
865*4882a593Smuzhiyun 			WARN_ON(ctx->replace_ctx->replace_ctx != ctx);
866*4882a593Smuzhiyun 
867*4882a593Smuzhiyun 			ctx->replace_ctx->replace_ctx = NULL;
868*4882a593Smuzhiyun 			ctx->replace_ctx->replace_state =
869*4882a593Smuzhiyun 					IEEE80211_CHANCTX_REPLACE_NONE;
870*4882a593Smuzhiyun 
871*4882a593Smuzhiyun 			list_del_rcu(&ctx->list);
872*4882a593Smuzhiyun 			kfree_rcu(ctx, rcu_head);
873*4882a593Smuzhiyun 		} else {
874*4882a593Smuzhiyun 			ieee80211_free_chanctx(sdata->local, ctx);
875*4882a593Smuzhiyun 		}
876*4882a593Smuzhiyun 	}
877*4882a593Smuzhiyun 
878*4882a593Smuzhiyun 	return 0;
879*4882a593Smuzhiyun }
880*4882a593Smuzhiyun 
ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data * sdata,const struct cfg80211_chan_def * chandef,enum ieee80211_chanctx_mode mode,bool radar_required)881*4882a593Smuzhiyun int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
882*4882a593Smuzhiyun 				  const struct cfg80211_chan_def *chandef,
883*4882a593Smuzhiyun 				  enum ieee80211_chanctx_mode mode,
884*4882a593Smuzhiyun 				  bool radar_required)
885*4882a593Smuzhiyun {
886*4882a593Smuzhiyun 	struct ieee80211_local *local = sdata->local;
887*4882a593Smuzhiyun 	struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx;
888*4882a593Smuzhiyun 
889*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
890*4882a593Smuzhiyun 
891*4882a593Smuzhiyun 	curr_ctx = ieee80211_vif_get_chanctx(sdata);
892*4882a593Smuzhiyun 	if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx)
893*4882a593Smuzhiyun 		return -ENOTSUPP;
894*4882a593Smuzhiyun 
895*4882a593Smuzhiyun 	new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
896*4882a593Smuzhiyun 	if (!new_ctx) {
897*4882a593Smuzhiyun 		if (ieee80211_can_create_new_chanctx(local)) {
898*4882a593Smuzhiyun 			new_ctx = ieee80211_new_chanctx(local, chandef, mode);
899*4882a593Smuzhiyun 			if (IS_ERR(new_ctx))
900*4882a593Smuzhiyun 				return PTR_ERR(new_ctx);
901*4882a593Smuzhiyun 		} else {
902*4882a593Smuzhiyun 			if (!curr_ctx ||
903*4882a593Smuzhiyun 			    (curr_ctx->replace_state ==
904*4882a593Smuzhiyun 			     IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
905*4882a593Smuzhiyun 			    !list_empty(&curr_ctx->reserved_vifs)) {
906*4882a593Smuzhiyun 				/*
907*4882a593Smuzhiyun 				 * Another vif already requested this context
908*4882a593Smuzhiyun 				 * for a reservation. Find another one hoping
909*4882a593Smuzhiyun 				 * all vifs assigned to it will also switch
910*4882a593Smuzhiyun 				 * soon enough.
911*4882a593Smuzhiyun 				 *
912*4882a593Smuzhiyun 				 * TODO: This needs a little more work as some
913*4882a593Smuzhiyun 				 * cases (more than 2 chanctx capable devices)
914*4882a593Smuzhiyun 				 * may fail which could otherwise succeed
915*4882a593Smuzhiyun 				 * provided some channel context juggling was
916*4882a593Smuzhiyun 				 * performed.
917*4882a593Smuzhiyun 				 *
918*4882a593Smuzhiyun 				 * Consider ctx1..3, vif1..6, each ctx has 2
919*4882a593Smuzhiyun 				 * vifs. vif1 and vif2 from ctx1 request new
920*4882a593Smuzhiyun 				 * different chandefs starting 2 in-place
921*4882a593Smuzhiyun 				 * reserations with ctx4 and ctx5 replacing
922*4882a593Smuzhiyun 				 * ctx1 and ctx2 respectively. Next vif5 and
923*4882a593Smuzhiyun 				 * vif6 from ctx3 reserve ctx4. If vif3 and
924*4882a593Smuzhiyun 				 * vif4 remain on ctx2 as they are then this
925*4882a593Smuzhiyun 				 * fails unless `replace_ctx` from ctx5 is
926*4882a593Smuzhiyun 				 * replaced with ctx3.
927*4882a593Smuzhiyun 				 */
928*4882a593Smuzhiyun 				list_for_each_entry(ctx, &local->chanctx_list,
929*4882a593Smuzhiyun 						    list) {
930*4882a593Smuzhiyun 					if (ctx->replace_state !=
931*4882a593Smuzhiyun 					    IEEE80211_CHANCTX_REPLACE_NONE)
932*4882a593Smuzhiyun 						continue;
933*4882a593Smuzhiyun 
934*4882a593Smuzhiyun 					if (!list_empty(&ctx->reserved_vifs))
935*4882a593Smuzhiyun 						continue;
936*4882a593Smuzhiyun 
937*4882a593Smuzhiyun 					curr_ctx = ctx;
938*4882a593Smuzhiyun 					break;
939*4882a593Smuzhiyun 				}
940*4882a593Smuzhiyun 			}
941*4882a593Smuzhiyun 
942*4882a593Smuzhiyun 			/*
943*4882a593Smuzhiyun 			 * If that's true then all available contexts already
944*4882a593Smuzhiyun 			 * have reservations and cannot be used.
945*4882a593Smuzhiyun 			 */
946*4882a593Smuzhiyun 			if (!curr_ctx ||
947*4882a593Smuzhiyun 			    (curr_ctx->replace_state ==
948*4882a593Smuzhiyun 			     IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
949*4882a593Smuzhiyun 			    !list_empty(&curr_ctx->reserved_vifs))
950*4882a593Smuzhiyun 				return -EBUSY;
951*4882a593Smuzhiyun 
952*4882a593Smuzhiyun 			new_ctx = ieee80211_alloc_chanctx(local, chandef, mode);
953*4882a593Smuzhiyun 			if (!new_ctx)
954*4882a593Smuzhiyun 				return -ENOMEM;
955*4882a593Smuzhiyun 
956*4882a593Smuzhiyun 			new_ctx->replace_ctx = curr_ctx;
957*4882a593Smuzhiyun 			new_ctx->replace_state =
958*4882a593Smuzhiyun 					IEEE80211_CHANCTX_REPLACES_OTHER;
959*4882a593Smuzhiyun 
960*4882a593Smuzhiyun 			curr_ctx->replace_ctx = new_ctx;
961*4882a593Smuzhiyun 			curr_ctx->replace_state =
962*4882a593Smuzhiyun 					IEEE80211_CHANCTX_WILL_BE_REPLACED;
963*4882a593Smuzhiyun 
964*4882a593Smuzhiyun 			list_add_rcu(&new_ctx->list, &local->chanctx_list);
965*4882a593Smuzhiyun 		}
966*4882a593Smuzhiyun 	}
967*4882a593Smuzhiyun 
968*4882a593Smuzhiyun 	list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs);
969*4882a593Smuzhiyun 	sdata->reserved_chanctx = new_ctx;
970*4882a593Smuzhiyun 	sdata->reserved_chandef = *chandef;
971*4882a593Smuzhiyun 	sdata->reserved_radar_required = radar_required;
972*4882a593Smuzhiyun 	sdata->reserved_ready = false;
973*4882a593Smuzhiyun 
974*4882a593Smuzhiyun 	return 0;
975*4882a593Smuzhiyun }
976*4882a593Smuzhiyun 
977*4882a593Smuzhiyun static void
ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data * sdata)978*4882a593Smuzhiyun ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata)
979*4882a593Smuzhiyun {
980*4882a593Smuzhiyun 	switch (sdata->vif.type) {
981*4882a593Smuzhiyun 	case NL80211_IFTYPE_ADHOC:
982*4882a593Smuzhiyun 	case NL80211_IFTYPE_AP:
983*4882a593Smuzhiyun 	case NL80211_IFTYPE_MESH_POINT:
984*4882a593Smuzhiyun 	case NL80211_IFTYPE_OCB:
985*4882a593Smuzhiyun 		ieee80211_queue_work(&sdata->local->hw,
986*4882a593Smuzhiyun 				     &sdata->csa_finalize_work);
987*4882a593Smuzhiyun 		break;
988*4882a593Smuzhiyun 	case NL80211_IFTYPE_STATION:
989*4882a593Smuzhiyun 		ieee80211_queue_work(&sdata->local->hw,
990*4882a593Smuzhiyun 				     &sdata->u.mgd.chswitch_work);
991*4882a593Smuzhiyun 		break;
992*4882a593Smuzhiyun 	case NL80211_IFTYPE_UNSPECIFIED:
993*4882a593Smuzhiyun 	case NL80211_IFTYPE_AP_VLAN:
994*4882a593Smuzhiyun 	case NL80211_IFTYPE_WDS:
995*4882a593Smuzhiyun 	case NL80211_IFTYPE_MONITOR:
996*4882a593Smuzhiyun 	case NL80211_IFTYPE_P2P_CLIENT:
997*4882a593Smuzhiyun 	case NL80211_IFTYPE_P2P_GO:
998*4882a593Smuzhiyun 	case NL80211_IFTYPE_P2P_DEVICE:
999*4882a593Smuzhiyun 	case NL80211_IFTYPE_NAN:
1000*4882a593Smuzhiyun 	case NUM_NL80211_IFTYPES:
1001*4882a593Smuzhiyun 		WARN_ON(1);
1002*4882a593Smuzhiyun 		break;
1003*4882a593Smuzhiyun 	}
1004*4882a593Smuzhiyun }
1005*4882a593Smuzhiyun 
1006*4882a593Smuzhiyun static void
ieee80211_vif_update_chandef(struct ieee80211_sub_if_data * sdata,const struct cfg80211_chan_def * chandef)1007*4882a593Smuzhiyun ieee80211_vif_update_chandef(struct ieee80211_sub_if_data *sdata,
1008*4882a593Smuzhiyun 			     const struct cfg80211_chan_def *chandef)
1009*4882a593Smuzhiyun {
1010*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *vlan;
1011*4882a593Smuzhiyun 
1012*4882a593Smuzhiyun 	sdata->vif.bss_conf.chandef = *chandef;
1013*4882a593Smuzhiyun 
1014*4882a593Smuzhiyun 	if (sdata->vif.type != NL80211_IFTYPE_AP)
1015*4882a593Smuzhiyun 		return;
1016*4882a593Smuzhiyun 
1017*4882a593Smuzhiyun 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
1018*4882a593Smuzhiyun 		vlan->vif.bss_conf.chandef = *chandef;
1019*4882a593Smuzhiyun }
1020*4882a593Smuzhiyun 
1021*4882a593Smuzhiyun static int
ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data * sdata)1022*4882a593Smuzhiyun ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
1023*4882a593Smuzhiyun {
1024*4882a593Smuzhiyun 	struct ieee80211_local *local = sdata->local;
1025*4882a593Smuzhiyun 	struct ieee80211_vif_chanctx_switch vif_chsw[1] = {};
1026*4882a593Smuzhiyun 	struct ieee80211_chanctx *old_ctx, *new_ctx;
1027*4882a593Smuzhiyun 	const struct cfg80211_chan_def *chandef;
1028*4882a593Smuzhiyun 	u32 changed = 0;
1029*4882a593Smuzhiyun 	int err;
1030*4882a593Smuzhiyun 
1031*4882a593Smuzhiyun 	lockdep_assert_held(&local->mtx);
1032*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
1033*4882a593Smuzhiyun 
1034*4882a593Smuzhiyun 	new_ctx = sdata->reserved_chanctx;
1035*4882a593Smuzhiyun 	old_ctx = ieee80211_vif_get_chanctx(sdata);
1036*4882a593Smuzhiyun 
1037*4882a593Smuzhiyun 	if (WARN_ON(!sdata->reserved_ready))
1038*4882a593Smuzhiyun 		return -EBUSY;
1039*4882a593Smuzhiyun 
1040*4882a593Smuzhiyun 	if (WARN_ON(!new_ctx))
1041*4882a593Smuzhiyun 		return -EINVAL;
1042*4882a593Smuzhiyun 
1043*4882a593Smuzhiyun 	if (WARN_ON(!old_ctx))
1044*4882a593Smuzhiyun 		return -EINVAL;
1045*4882a593Smuzhiyun 
1046*4882a593Smuzhiyun 	if (WARN_ON(new_ctx->replace_state ==
1047*4882a593Smuzhiyun 		    IEEE80211_CHANCTX_REPLACES_OTHER))
1048*4882a593Smuzhiyun 		return -EINVAL;
1049*4882a593Smuzhiyun 
1050*4882a593Smuzhiyun 	chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
1051*4882a593Smuzhiyun 				&sdata->reserved_chandef);
1052*4882a593Smuzhiyun 	if (WARN_ON(!chandef))
1053*4882a593Smuzhiyun 		return -EINVAL;
1054*4882a593Smuzhiyun 
1055*4882a593Smuzhiyun 	ieee80211_change_chanctx(local, new_ctx, chandef);
1056*4882a593Smuzhiyun 
1057*4882a593Smuzhiyun 	vif_chsw[0].vif = &sdata->vif;
1058*4882a593Smuzhiyun 	vif_chsw[0].old_ctx = &old_ctx->conf;
1059*4882a593Smuzhiyun 	vif_chsw[0].new_ctx = &new_ctx->conf;
1060*4882a593Smuzhiyun 
1061*4882a593Smuzhiyun 	list_del(&sdata->reserved_chanctx_list);
1062*4882a593Smuzhiyun 	sdata->reserved_chanctx = NULL;
1063*4882a593Smuzhiyun 
1064*4882a593Smuzhiyun 	err = drv_switch_vif_chanctx(local, vif_chsw, 1,
1065*4882a593Smuzhiyun 				     CHANCTX_SWMODE_REASSIGN_VIF);
1066*4882a593Smuzhiyun 	if (err) {
1067*4882a593Smuzhiyun 		if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
1068*4882a593Smuzhiyun 			ieee80211_free_chanctx(local, new_ctx);
1069*4882a593Smuzhiyun 
1070*4882a593Smuzhiyun 		goto out;
1071*4882a593Smuzhiyun 	}
1072*4882a593Smuzhiyun 
1073*4882a593Smuzhiyun 	list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs);
1074*4882a593Smuzhiyun 	rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf);
1075*4882a593Smuzhiyun 
1076*4882a593Smuzhiyun 	if (sdata->vif.type == NL80211_IFTYPE_AP)
1077*4882a593Smuzhiyun 		__ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
1078*4882a593Smuzhiyun 
1079*4882a593Smuzhiyun 	ieee80211_check_fast_xmit_iface(sdata);
1080*4882a593Smuzhiyun 
1081*4882a593Smuzhiyun 	if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
1082*4882a593Smuzhiyun 		ieee80211_free_chanctx(local, old_ctx);
1083*4882a593Smuzhiyun 
1084*4882a593Smuzhiyun 	if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
1085*4882a593Smuzhiyun 		changed = BSS_CHANGED_BANDWIDTH;
1086*4882a593Smuzhiyun 
1087*4882a593Smuzhiyun 	ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
1088*4882a593Smuzhiyun 
1089*4882a593Smuzhiyun 	ieee80211_recalc_smps_chanctx(local, new_ctx);
1090*4882a593Smuzhiyun 	ieee80211_recalc_radar_chanctx(local, new_ctx);
1091*4882a593Smuzhiyun 	ieee80211_recalc_chanctx_min_def(local, new_ctx);
1092*4882a593Smuzhiyun 
1093*4882a593Smuzhiyun 	if (changed)
1094*4882a593Smuzhiyun 		ieee80211_bss_info_change_notify(sdata, changed);
1095*4882a593Smuzhiyun 
1096*4882a593Smuzhiyun out:
1097*4882a593Smuzhiyun 	ieee80211_vif_chanctx_reservation_complete(sdata);
1098*4882a593Smuzhiyun 	return err;
1099*4882a593Smuzhiyun }
1100*4882a593Smuzhiyun 
1101*4882a593Smuzhiyun static int
ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data * sdata)1102*4882a593Smuzhiyun ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata)
1103*4882a593Smuzhiyun {
1104*4882a593Smuzhiyun 	struct ieee80211_local *local = sdata->local;
1105*4882a593Smuzhiyun 	struct ieee80211_chanctx *old_ctx, *new_ctx;
1106*4882a593Smuzhiyun 	const struct cfg80211_chan_def *chandef;
1107*4882a593Smuzhiyun 	int err;
1108*4882a593Smuzhiyun 
1109*4882a593Smuzhiyun 	old_ctx = ieee80211_vif_get_chanctx(sdata);
1110*4882a593Smuzhiyun 	new_ctx = sdata->reserved_chanctx;
1111*4882a593Smuzhiyun 
1112*4882a593Smuzhiyun 	if (WARN_ON(!sdata->reserved_ready))
1113*4882a593Smuzhiyun 		return -EINVAL;
1114*4882a593Smuzhiyun 
1115*4882a593Smuzhiyun 	if (WARN_ON(old_ctx))
1116*4882a593Smuzhiyun 		return -EINVAL;
1117*4882a593Smuzhiyun 
1118*4882a593Smuzhiyun 	if (WARN_ON(!new_ctx))
1119*4882a593Smuzhiyun 		return -EINVAL;
1120*4882a593Smuzhiyun 
1121*4882a593Smuzhiyun 	if (WARN_ON(new_ctx->replace_state ==
1122*4882a593Smuzhiyun 		    IEEE80211_CHANCTX_REPLACES_OTHER))
1123*4882a593Smuzhiyun 		return -EINVAL;
1124*4882a593Smuzhiyun 
1125*4882a593Smuzhiyun 	chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
1126*4882a593Smuzhiyun 				&sdata->reserved_chandef);
1127*4882a593Smuzhiyun 	if (WARN_ON(!chandef))
1128*4882a593Smuzhiyun 		return -EINVAL;
1129*4882a593Smuzhiyun 
1130*4882a593Smuzhiyun 	ieee80211_change_chanctx(local, new_ctx, chandef);
1131*4882a593Smuzhiyun 
1132*4882a593Smuzhiyun 	list_del(&sdata->reserved_chanctx_list);
1133*4882a593Smuzhiyun 	sdata->reserved_chanctx = NULL;
1134*4882a593Smuzhiyun 
1135*4882a593Smuzhiyun 	err = ieee80211_assign_vif_chanctx(sdata, new_ctx);
1136*4882a593Smuzhiyun 	if (err) {
1137*4882a593Smuzhiyun 		if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
1138*4882a593Smuzhiyun 			ieee80211_free_chanctx(local, new_ctx);
1139*4882a593Smuzhiyun 
1140*4882a593Smuzhiyun 		goto out;
1141*4882a593Smuzhiyun 	}
1142*4882a593Smuzhiyun 
1143*4882a593Smuzhiyun out:
1144*4882a593Smuzhiyun 	ieee80211_vif_chanctx_reservation_complete(sdata);
1145*4882a593Smuzhiyun 	return err;
1146*4882a593Smuzhiyun }
1147*4882a593Smuzhiyun 
1148*4882a593Smuzhiyun static bool
ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data * sdata)1149*4882a593Smuzhiyun ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata)
1150*4882a593Smuzhiyun {
1151*4882a593Smuzhiyun 	struct ieee80211_chanctx *old_ctx, *new_ctx;
1152*4882a593Smuzhiyun 
1153*4882a593Smuzhiyun 	lockdep_assert_held(&sdata->local->chanctx_mtx);
1154*4882a593Smuzhiyun 
1155*4882a593Smuzhiyun 	new_ctx = sdata->reserved_chanctx;
1156*4882a593Smuzhiyun 	old_ctx = ieee80211_vif_get_chanctx(sdata);
1157*4882a593Smuzhiyun 
1158*4882a593Smuzhiyun 	if (!old_ctx)
1159*4882a593Smuzhiyun 		return false;
1160*4882a593Smuzhiyun 
1161*4882a593Smuzhiyun 	if (WARN_ON(!new_ctx))
1162*4882a593Smuzhiyun 		return false;
1163*4882a593Smuzhiyun 
1164*4882a593Smuzhiyun 	if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
1165*4882a593Smuzhiyun 		return false;
1166*4882a593Smuzhiyun 
1167*4882a593Smuzhiyun 	if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1168*4882a593Smuzhiyun 		return false;
1169*4882a593Smuzhiyun 
1170*4882a593Smuzhiyun 	return true;
1171*4882a593Smuzhiyun }
1172*4882a593Smuzhiyun 
ieee80211_chsw_switch_hwconf(struct ieee80211_local * local,struct ieee80211_chanctx * new_ctx)1173*4882a593Smuzhiyun static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local,
1174*4882a593Smuzhiyun 					struct ieee80211_chanctx *new_ctx)
1175*4882a593Smuzhiyun {
1176*4882a593Smuzhiyun 	const struct cfg80211_chan_def *chandef;
1177*4882a593Smuzhiyun 
1178*4882a593Smuzhiyun 	lockdep_assert_held(&local->mtx);
1179*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
1180*4882a593Smuzhiyun 
1181*4882a593Smuzhiyun 	chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL);
1182*4882a593Smuzhiyun 	if (WARN_ON(!chandef))
1183*4882a593Smuzhiyun 		return -EINVAL;
1184*4882a593Smuzhiyun 
1185*4882a593Smuzhiyun 	local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled;
1186*4882a593Smuzhiyun 	local->_oper_chandef = *chandef;
1187*4882a593Smuzhiyun 	ieee80211_hw_config(local, 0);
1188*4882a593Smuzhiyun 
1189*4882a593Smuzhiyun 	return 0;
1190*4882a593Smuzhiyun }
1191*4882a593Smuzhiyun 
ieee80211_chsw_switch_vifs(struct ieee80211_local * local,int n_vifs)1192*4882a593Smuzhiyun static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
1193*4882a593Smuzhiyun 				      int n_vifs)
1194*4882a593Smuzhiyun {
1195*4882a593Smuzhiyun 	struct ieee80211_vif_chanctx_switch *vif_chsw;
1196*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *sdata;
1197*4882a593Smuzhiyun 	struct ieee80211_chanctx *ctx, *old_ctx;
1198*4882a593Smuzhiyun 	int i, err;
1199*4882a593Smuzhiyun 
1200*4882a593Smuzhiyun 	lockdep_assert_held(&local->mtx);
1201*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
1202*4882a593Smuzhiyun 
1203*4882a593Smuzhiyun 	vif_chsw = kcalloc(n_vifs, sizeof(vif_chsw[0]), GFP_KERNEL);
1204*4882a593Smuzhiyun 	if (!vif_chsw)
1205*4882a593Smuzhiyun 		return -ENOMEM;
1206*4882a593Smuzhiyun 
1207*4882a593Smuzhiyun 	i = 0;
1208*4882a593Smuzhiyun 	list_for_each_entry(ctx, &local->chanctx_list, list) {
1209*4882a593Smuzhiyun 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1210*4882a593Smuzhiyun 			continue;
1211*4882a593Smuzhiyun 
1212*4882a593Smuzhiyun 		if (WARN_ON(!ctx->replace_ctx)) {
1213*4882a593Smuzhiyun 			err = -EINVAL;
1214*4882a593Smuzhiyun 			goto out;
1215*4882a593Smuzhiyun 		}
1216*4882a593Smuzhiyun 
1217*4882a593Smuzhiyun 		list_for_each_entry(sdata, &ctx->reserved_vifs,
1218*4882a593Smuzhiyun 				    reserved_chanctx_list) {
1219*4882a593Smuzhiyun 			if (!ieee80211_vif_has_in_place_reservation(
1220*4882a593Smuzhiyun 					sdata))
1221*4882a593Smuzhiyun 				continue;
1222*4882a593Smuzhiyun 
1223*4882a593Smuzhiyun 			old_ctx = ieee80211_vif_get_chanctx(sdata);
1224*4882a593Smuzhiyun 			vif_chsw[i].vif = &sdata->vif;
1225*4882a593Smuzhiyun 			vif_chsw[i].old_ctx = &old_ctx->conf;
1226*4882a593Smuzhiyun 			vif_chsw[i].new_ctx = &ctx->conf;
1227*4882a593Smuzhiyun 
1228*4882a593Smuzhiyun 			i++;
1229*4882a593Smuzhiyun 		}
1230*4882a593Smuzhiyun 	}
1231*4882a593Smuzhiyun 
1232*4882a593Smuzhiyun 	err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs,
1233*4882a593Smuzhiyun 				     CHANCTX_SWMODE_SWAP_CONTEXTS);
1234*4882a593Smuzhiyun 
1235*4882a593Smuzhiyun out:
1236*4882a593Smuzhiyun 	kfree(vif_chsw);
1237*4882a593Smuzhiyun 	return err;
1238*4882a593Smuzhiyun }
1239*4882a593Smuzhiyun 
ieee80211_chsw_switch_ctxs(struct ieee80211_local * local)1240*4882a593Smuzhiyun static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local)
1241*4882a593Smuzhiyun {
1242*4882a593Smuzhiyun 	struct ieee80211_chanctx *ctx;
1243*4882a593Smuzhiyun 	int err;
1244*4882a593Smuzhiyun 
1245*4882a593Smuzhiyun 	lockdep_assert_held(&local->mtx);
1246*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
1247*4882a593Smuzhiyun 
1248*4882a593Smuzhiyun 	list_for_each_entry(ctx, &local->chanctx_list, list) {
1249*4882a593Smuzhiyun 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1250*4882a593Smuzhiyun 			continue;
1251*4882a593Smuzhiyun 
1252*4882a593Smuzhiyun 		if (!list_empty(&ctx->replace_ctx->assigned_vifs))
1253*4882a593Smuzhiyun 			continue;
1254*4882a593Smuzhiyun 
1255*4882a593Smuzhiyun 		ieee80211_del_chanctx(local, ctx->replace_ctx);
1256*4882a593Smuzhiyun 		err = ieee80211_add_chanctx(local, ctx);
1257*4882a593Smuzhiyun 		if (err)
1258*4882a593Smuzhiyun 			goto err;
1259*4882a593Smuzhiyun 	}
1260*4882a593Smuzhiyun 
1261*4882a593Smuzhiyun 	return 0;
1262*4882a593Smuzhiyun 
1263*4882a593Smuzhiyun err:
1264*4882a593Smuzhiyun 	WARN_ON(ieee80211_add_chanctx(local, ctx));
1265*4882a593Smuzhiyun 	list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) {
1266*4882a593Smuzhiyun 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1267*4882a593Smuzhiyun 			continue;
1268*4882a593Smuzhiyun 
1269*4882a593Smuzhiyun 		if (!list_empty(&ctx->replace_ctx->assigned_vifs))
1270*4882a593Smuzhiyun 			continue;
1271*4882a593Smuzhiyun 
1272*4882a593Smuzhiyun 		ieee80211_del_chanctx(local, ctx);
1273*4882a593Smuzhiyun 		WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx));
1274*4882a593Smuzhiyun 	}
1275*4882a593Smuzhiyun 
1276*4882a593Smuzhiyun 	return err;
1277*4882a593Smuzhiyun }
1278*4882a593Smuzhiyun 
ieee80211_vif_use_reserved_switch(struct ieee80211_local * local)1279*4882a593Smuzhiyun static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
1280*4882a593Smuzhiyun {
1281*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *sdata, *sdata_tmp;
1282*4882a593Smuzhiyun 	struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx;
1283*4882a593Smuzhiyun 	struct ieee80211_chanctx *new_ctx = NULL;
1284*4882a593Smuzhiyun 	int err, n_assigned, n_reserved, n_ready;
1285*4882a593Smuzhiyun 	int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0;
1286*4882a593Smuzhiyun 
1287*4882a593Smuzhiyun 	lockdep_assert_held(&local->mtx);
1288*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
1289*4882a593Smuzhiyun 
1290*4882a593Smuzhiyun 	/*
1291*4882a593Smuzhiyun 	 * If there are 2 independent pairs of channel contexts performing
1292*4882a593Smuzhiyun 	 * cross-switch of their vifs this code will still wait until both are
1293*4882a593Smuzhiyun 	 * ready even though it could be possible to switch one before the
1294*4882a593Smuzhiyun 	 * other is ready.
1295*4882a593Smuzhiyun 	 *
1296*4882a593Smuzhiyun 	 * For practical reasons and code simplicity just do a single huge
1297*4882a593Smuzhiyun 	 * switch.
1298*4882a593Smuzhiyun 	 */
1299*4882a593Smuzhiyun 
1300*4882a593Smuzhiyun 	/*
1301*4882a593Smuzhiyun 	 * Verify if the reservation is still feasible.
1302*4882a593Smuzhiyun 	 *  - if it's not then disconnect
1303*4882a593Smuzhiyun 	 *  - if it is but not all vifs necessary are ready then defer
1304*4882a593Smuzhiyun 	 */
1305*4882a593Smuzhiyun 
1306*4882a593Smuzhiyun 	list_for_each_entry(ctx, &local->chanctx_list, list) {
1307*4882a593Smuzhiyun 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1308*4882a593Smuzhiyun 			continue;
1309*4882a593Smuzhiyun 
1310*4882a593Smuzhiyun 		if (WARN_ON(!ctx->replace_ctx)) {
1311*4882a593Smuzhiyun 			err = -EINVAL;
1312*4882a593Smuzhiyun 			goto err;
1313*4882a593Smuzhiyun 		}
1314*4882a593Smuzhiyun 
1315*4882a593Smuzhiyun 		if (!local->use_chanctx)
1316*4882a593Smuzhiyun 			new_ctx = ctx;
1317*4882a593Smuzhiyun 
1318*4882a593Smuzhiyun 		n_ctx++;
1319*4882a593Smuzhiyun 
1320*4882a593Smuzhiyun 		n_assigned = 0;
1321*4882a593Smuzhiyun 		n_reserved = 0;
1322*4882a593Smuzhiyun 		n_ready = 0;
1323*4882a593Smuzhiyun 
1324*4882a593Smuzhiyun 		list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs,
1325*4882a593Smuzhiyun 				    assigned_chanctx_list) {
1326*4882a593Smuzhiyun 			n_assigned++;
1327*4882a593Smuzhiyun 			if (sdata->reserved_chanctx) {
1328*4882a593Smuzhiyun 				n_reserved++;
1329*4882a593Smuzhiyun 				if (sdata->reserved_ready)
1330*4882a593Smuzhiyun 					n_ready++;
1331*4882a593Smuzhiyun 			}
1332*4882a593Smuzhiyun 		}
1333*4882a593Smuzhiyun 
1334*4882a593Smuzhiyun 		if (n_assigned != n_reserved) {
1335*4882a593Smuzhiyun 			if (n_ready == n_reserved) {
1336*4882a593Smuzhiyun 				wiphy_info(local->hw.wiphy,
1337*4882a593Smuzhiyun 					   "channel context reservation cannot be finalized because some interfaces aren't switching\n");
1338*4882a593Smuzhiyun 				err = -EBUSY;
1339*4882a593Smuzhiyun 				goto err;
1340*4882a593Smuzhiyun 			}
1341*4882a593Smuzhiyun 
1342*4882a593Smuzhiyun 			return -EAGAIN;
1343*4882a593Smuzhiyun 		}
1344*4882a593Smuzhiyun 
1345*4882a593Smuzhiyun 		ctx->conf.radar_enabled = false;
1346*4882a593Smuzhiyun 		list_for_each_entry(sdata, &ctx->reserved_vifs,
1347*4882a593Smuzhiyun 				    reserved_chanctx_list) {
1348*4882a593Smuzhiyun 			if (ieee80211_vif_has_in_place_reservation(sdata) &&
1349*4882a593Smuzhiyun 			    !sdata->reserved_ready)
1350*4882a593Smuzhiyun 				return -EAGAIN;
1351*4882a593Smuzhiyun 
1352*4882a593Smuzhiyun 			old_ctx = ieee80211_vif_get_chanctx(sdata);
1353*4882a593Smuzhiyun 			if (old_ctx) {
1354*4882a593Smuzhiyun 				if (old_ctx->replace_state ==
1355*4882a593Smuzhiyun 				    IEEE80211_CHANCTX_WILL_BE_REPLACED)
1356*4882a593Smuzhiyun 					n_vifs_switch++;
1357*4882a593Smuzhiyun 				else
1358*4882a593Smuzhiyun 					n_vifs_assign++;
1359*4882a593Smuzhiyun 			} else {
1360*4882a593Smuzhiyun 				n_vifs_ctxless++;
1361*4882a593Smuzhiyun 			}
1362*4882a593Smuzhiyun 
1363*4882a593Smuzhiyun 			if (sdata->reserved_radar_required)
1364*4882a593Smuzhiyun 				ctx->conf.radar_enabled = true;
1365*4882a593Smuzhiyun 		}
1366*4882a593Smuzhiyun 	}
1367*4882a593Smuzhiyun 
1368*4882a593Smuzhiyun 	if (WARN_ON(n_ctx == 0) ||
1369*4882a593Smuzhiyun 	    WARN_ON(n_vifs_switch == 0 &&
1370*4882a593Smuzhiyun 		    n_vifs_assign == 0 &&
1371*4882a593Smuzhiyun 		    n_vifs_ctxless == 0) ||
1372*4882a593Smuzhiyun 	    WARN_ON(n_ctx > 1 && !local->use_chanctx) ||
1373*4882a593Smuzhiyun 	    WARN_ON(!new_ctx && !local->use_chanctx)) {
1374*4882a593Smuzhiyun 		err = -EINVAL;
1375*4882a593Smuzhiyun 		goto err;
1376*4882a593Smuzhiyun 	}
1377*4882a593Smuzhiyun 
1378*4882a593Smuzhiyun 	/*
1379*4882a593Smuzhiyun 	 * All necessary vifs are ready. Perform the switch now depending on
1380*4882a593Smuzhiyun 	 * reservations and driver capabilities.
1381*4882a593Smuzhiyun 	 */
1382*4882a593Smuzhiyun 
1383*4882a593Smuzhiyun 	if (local->use_chanctx) {
1384*4882a593Smuzhiyun 		if (n_vifs_switch > 0) {
1385*4882a593Smuzhiyun 			err = ieee80211_chsw_switch_vifs(local, n_vifs_switch);
1386*4882a593Smuzhiyun 			if (err)
1387*4882a593Smuzhiyun 				goto err;
1388*4882a593Smuzhiyun 		}
1389*4882a593Smuzhiyun 
1390*4882a593Smuzhiyun 		if (n_vifs_assign > 0 || n_vifs_ctxless > 0) {
1391*4882a593Smuzhiyun 			err = ieee80211_chsw_switch_ctxs(local);
1392*4882a593Smuzhiyun 			if (err)
1393*4882a593Smuzhiyun 				goto err;
1394*4882a593Smuzhiyun 		}
1395*4882a593Smuzhiyun 	} else {
1396*4882a593Smuzhiyun 		err = ieee80211_chsw_switch_hwconf(local, new_ctx);
1397*4882a593Smuzhiyun 		if (err)
1398*4882a593Smuzhiyun 			goto err;
1399*4882a593Smuzhiyun 	}
1400*4882a593Smuzhiyun 
1401*4882a593Smuzhiyun 	/*
1402*4882a593Smuzhiyun 	 * Update all structures, values and pointers to point to new channel
1403*4882a593Smuzhiyun 	 * context(s).
1404*4882a593Smuzhiyun 	 */
1405*4882a593Smuzhiyun 	list_for_each_entry(ctx, &local->chanctx_list, list) {
1406*4882a593Smuzhiyun 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1407*4882a593Smuzhiyun 			continue;
1408*4882a593Smuzhiyun 
1409*4882a593Smuzhiyun 		if (WARN_ON(!ctx->replace_ctx)) {
1410*4882a593Smuzhiyun 			err = -EINVAL;
1411*4882a593Smuzhiyun 			goto err;
1412*4882a593Smuzhiyun 		}
1413*4882a593Smuzhiyun 
1414*4882a593Smuzhiyun 		list_for_each_entry(sdata, &ctx->reserved_vifs,
1415*4882a593Smuzhiyun 				    reserved_chanctx_list) {
1416*4882a593Smuzhiyun 			u32 changed = 0;
1417*4882a593Smuzhiyun 
1418*4882a593Smuzhiyun 			if (!ieee80211_vif_has_in_place_reservation(sdata))
1419*4882a593Smuzhiyun 				continue;
1420*4882a593Smuzhiyun 
1421*4882a593Smuzhiyun 			rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
1422*4882a593Smuzhiyun 
1423*4882a593Smuzhiyun 			if (sdata->vif.type == NL80211_IFTYPE_AP)
1424*4882a593Smuzhiyun 				__ieee80211_vif_copy_chanctx_to_vlans(sdata,
1425*4882a593Smuzhiyun 								      false);
1426*4882a593Smuzhiyun 
1427*4882a593Smuzhiyun 			ieee80211_check_fast_xmit_iface(sdata);
1428*4882a593Smuzhiyun 
1429*4882a593Smuzhiyun 			sdata->radar_required = sdata->reserved_radar_required;
1430*4882a593Smuzhiyun 
1431*4882a593Smuzhiyun 			if (sdata->vif.bss_conf.chandef.width !=
1432*4882a593Smuzhiyun 			    sdata->reserved_chandef.width)
1433*4882a593Smuzhiyun 				changed = BSS_CHANGED_BANDWIDTH;
1434*4882a593Smuzhiyun 
1435*4882a593Smuzhiyun 			ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
1436*4882a593Smuzhiyun 			if (changed)
1437*4882a593Smuzhiyun 				ieee80211_bss_info_change_notify(sdata,
1438*4882a593Smuzhiyun 								 changed);
1439*4882a593Smuzhiyun 
1440*4882a593Smuzhiyun 			ieee80211_recalc_txpower(sdata, false);
1441*4882a593Smuzhiyun 		}
1442*4882a593Smuzhiyun 
1443*4882a593Smuzhiyun 		ieee80211_recalc_chanctx_chantype(local, ctx);
1444*4882a593Smuzhiyun 		ieee80211_recalc_smps_chanctx(local, ctx);
1445*4882a593Smuzhiyun 		ieee80211_recalc_radar_chanctx(local, ctx);
1446*4882a593Smuzhiyun 		ieee80211_recalc_chanctx_min_def(local, ctx);
1447*4882a593Smuzhiyun 
1448*4882a593Smuzhiyun 		list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
1449*4882a593Smuzhiyun 					 reserved_chanctx_list) {
1450*4882a593Smuzhiyun 			if (ieee80211_vif_get_chanctx(sdata) != ctx)
1451*4882a593Smuzhiyun 				continue;
1452*4882a593Smuzhiyun 
1453*4882a593Smuzhiyun 			list_del(&sdata->reserved_chanctx_list);
1454*4882a593Smuzhiyun 			list_move(&sdata->assigned_chanctx_list,
1455*4882a593Smuzhiyun 				  &ctx->assigned_vifs);
1456*4882a593Smuzhiyun 			sdata->reserved_chanctx = NULL;
1457*4882a593Smuzhiyun 
1458*4882a593Smuzhiyun 			ieee80211_vif_chanctx_reservation_complete(sdata);
1459*4882a593Smuzhiyun 		}
1460*4882a593Smuzhiyun 
1461*4882a593Smuzhiyun 		/*
1462*4882a593Smuzhiyun 		 * This context might have been a dependency for an already
1463*4882a593Smuzhiyun 		 * ready re-assign reservation interface that was deferred. Do
1464*4882a593Smuzhiyun 		 * not propagate error to the caller though. The in-place
1465*4882a593Smuzhiyun 		 * reservation for originally requested interface has already
1466*4882a593Smuzhiyun 		 * succeeded at this point.
1467*4882a593Smuzhiyun 		 */
1468*4882a593Smuzhiyun 		list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
1469*4882a593Smuzhiyun 					 reserved_chanctx_list) {
1470*4882a593Smuzhiyun 			if (WARN_ON(ieee80211_vif_has_in_place_reservation(
1471*4882a593Smuzhiyun 					sdata)))
1472*4882a593Smuzhiyun 				continue;
1473*4882a593Smuzhiyun 
1474*4882a593Smuzhiyun 			if (WARN_ON(sdata->reserved_chanctx != ctx))
1475*4882a593Smuzhiyun 				continue;
1476*4882a593Smuzhiyun 
1477*4882a593Smuzhiyun 			if (!sdata->reserved_ready)
1478*4882a593Smuzhiyun 				continue;
1479*4882a593Smuzhiyun 
1480*4882a593Smuzhiyun 			if (ieee80211_vif_get_chanctx(sdata))
1481*4882a593Smuzhiyun 				err = ieee80211_vif_use_reserved_reassign(
1482*4882a593Smuzhiyun 						sdata);
1483*4882a593Smuzhiyun 			else
1484*4882a593Smuzhiyun 				err = ieee80211_vif_use_reserved_assign(sdata);
1485*4882a593Smuzhiyun 
1486*4882a593Smuzhiyun 			if (err) {
1487*4882a593Smuzhiyun 				sdata_info(sdata,
1488*4882a593Smuzhiyun 					   "failed to finalize (re-)assign reservation (err=%d)\n",
1489*4882a593Smuzhiyun 					   err);
1490*4882a593Smuzhiyun 				ieee80211_vif_unreserve_chanctx(sdata);
1491*4882a593Smuzhiyun 				cfg80211_stop_iface(local->hw.wiphy,
1492*4882a593Smuzhiyun 						    &sdata->wdev,
1493*4882a593Smuzhiyun 						    GFP_KERNEL);
1494*4882a593Smuzhiyun 			}
1495*4882a593Smuzhiyun 		}
1496*4882a593Smuzhiyun 	}
1497*4882a593Smuzhiyun 
1498*4882a593Smuzhiyun 	/*
1499*4882a593Smuzhiyun 	 * Finally free old contexts
1500*4882a593Smuzhiyun 	 */
1501*4882a593Smuzhiyun 
1502*4882a593Smuzhiyun 	list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) {
1503*4882a593Smuzhiyun 		if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
1504*4882a593Smuzhiyun 			continue;
1505*4882a593Smuzhiyun 
1506*4882a593Smuzhiyun 		ctx->replace_ctx->replace_ctx = NULL;
1507*4882a593Smuzhiyun 		ctx->replace_ctx->replace_state =
1508*4882a593Smuzhiyun 				IEEE80211_CHANCTX_REPLACE_NONE;
1509*4882a593Smuzhiyun 
1510*4882a593Smuzhiyun 		list_del_rcu(&ctx->list);
1511*4882a593Smuzhiyun 		kfree_rcu(ctx, rcu_head);
1512*4882a593Smuzhiyun 	}
1513*4882a593Smuzhiyun 
1514*4882a593Smuzhiyun 	return 0;
1515*4882a593Smuzhiyun 
1516*4882a593Smuzhiyun err:
1517*4882a593Smuzhiyun 	list_for_each_entry(ctx, &local->chanctx_list, list) {
1518*4882a593Smuzhiyun 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1519*4882a593Smuzhiyun 			continue;
1520*4882a593Smuzhiyun 
1521*4882a593Smuzhiyun 		list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
1522*4882a593Smuzhiyun 					 reserved_chanctx_list) {
1523*4882a593Smuzhiyun 			ieee80211_vif_unreserve_chanctx(sdata);
1524*4882a593Smuzhiyun 			ieee80211_vif_chanctx_reservation_complete(sdata);
1525*4882a593Smuzhiyun 		}
1526*4882a593Smuzhiyun 	}
1527*4882a593Smuzhiyun 
1528*4882a593Smuzhiyun 	return err;
1529*4882a593Smuzhiyun }
1530*4882a593Smuzhiyun 
__ieee80211_vif_release_channel(struct ieee80211_sub_if_data * sdata)1531*4882a593Smuzhiyun static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
1532*4882a593Smuzhiyun {
1533*4882a593Smuzhiyun 	struct ieee80211_local *local = sdata->local;
1534*4882a593Smuzhiyun 	struct ieee80211_chanctx_conf *conf;
1535*4882a593Smuzhiyun 	struct ieee80211_chanctx *ctx;
1536*4882a593Smuzhiyun 	bool use_reserved_switch = false;
1537*4882a593Smuzhiyun 
1538*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
1539*4882a593Smuzhiyun 
1540*4882a593Smuzhiyun 	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
1541*4882a593Smuzhiyun 					 lockdep_is_held(&local->chanctx_mtx));
1542*4882a593Smuzhiyun 	if (!conf)
1543*4882a593Smuzhiyun 		return;
1544*4882a593Smuzhiyun 
1545*4882a593Smuzhiyun 	ctx = container_of(conf, struct ieee80211_chanctx, conf);
1546*4882a593Smuzhiyun 
1547*4882a593Smuzhiyun 	if (sdata->reserved_chanctx) {
1548*4882a593Smuzhiyun 		if (sdata->reserved_chanctx->replace_state ==
1549*4882a593Smuzhiyun 		    IEEE80211_CHANCTX_REPLACES_OTHER &&
1550*4882a593Smuzhiyun 		    ieee80211_chanctx_num_reserved(local,
1551*4882a593Smuzhiyun 						   sdata->reserved_chanctx) > 1)
1552*4882a593Smuzhiyun 			use_reserved_switch = true;
1553*4882a593Smuzhiyun 
1554*4882a593Smuzhiyun 		ieee80211_vif_unreserve_chanctx(sdata);
1555*4882a593Smuzhiyun 	}
1556*4882a593Smuzhiyun 
1557*4882a593Smuzhiyun 	ieee80211_assign_vif_chanctx(sdata, NULL);
1558*4882a593Smuzhiyun 	if (ieee80211_chanctx_refcount(local, ctx) == 0)
1559*4882a593Smuzhiyun 		ieee80211_free_chanctx(local, ctx);
1560*4882a593Smuzhiyun 
1561*4882a593Smuzhiyun 	sdata->radar_required = false;
1562*4882a593Smuzhiyun 
1563*4882a593Smuzhiyun 	/* Unreserving may ready an in-place reservation. */
1564*4882a593Smuzhiyun 	if (use_reserved_switch)
1565*4882a593Smuzhiyun 		ieee80211_vif_use_reserved_switch(local);
1566*4882a593Smuzhiyun }
1567*4882a593Smuzhiyun 
ieee80211_vif_use_channel(struct ieee80211_sub_if_data * sdata,const struct cfg80211_chan_def * chandef,enum ieee80211_chanctx_mode mode)1568*4882a593Smuzhiyun int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
1569*4882a593Smuzhiyun 			      const struct cfg80211_chan_def *chandef,
1570*4882a593Smuzhiyun 			      enum ieee80211_chanctx_mode mode)
1571*4882a593Smuzhiyun {
1572*4882a593Smuzhiyun 	struct ieee80211_local *local = sdata->local;
1573*4882a593Smuzhiyun 	struct ieee80211_chanctx *ctx;
1574*4882a593Smuzhiyun 	u8 radar_detect_width = 0;
1575*4882a593Smuzhiyun 	int ret;
1576*4882a593Smuzhiyun 
1577*4882a593Smuzhiyun 	lockdep_assert_held(&local->mtx);
1578*4882a593Smuzhiyun 
1579*4882a593Smuzhiyun 	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
1580*4882a593Smuzhiyun 
1581*4882a593Smuzhiyun 	mutex_lock(&local->chanctx_mtx);
1582*4882a593Smuzhiyun 
1583*4882a593Smuzhiyun 	ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
1584*4882a593Smuzhiyun 					    chandef,
1585*4882a593Smuzhiyun 					    sdata->wdev.iftype);
1586*4882a593Smuzhiyun 	if (ret < 0)
1587*4882a593Smuzhiyun 		goto out;
1588*4882a593Smuzhiyun 	if (ret > 0)
1589*4882a593Smuzhiyun 		radar_detect_width = BIT(chandef->width);
1590*4882a593Smuzhiyun 
1591*4882a593Smuzhiyun 	sdata->radar_required = ret;
1592*4882a593Smuzhiyun 
1593*4882a593Smuzhiyun 	ret = ieee80211_check_combinations(sdata, chandef, mode,
1594*4882a593Smuzhiyun 					   radar_detect_width);
1595*4882a593Smuzhiyun 	if (ret < 0)
1596*4882a593Smuzhiyun 		goto out;
1597*4882a593Smuzhiyun 
1598*4882a593Smuzhiyun 	__ieee80211_vif_release_channel(sdata);
1599*4882a593Smuzhiyun 
1600*4882a593Smuzhiyun 	ctx = ieee80211_find_chanctx(local, chandef, mode);
1601*4882a593Smuzhiyun 	if (!ctx)
1602*4882a593Smuzhiyun 		ctx = ieee80211_new_chanctx(local, chandef, mode);
1603*4882a593Smuzhiyun 	if (IS_ERR(ctx)) {
1604*4882a593Smuzhiyun 		ret = PTR_ERR(ctx);
1605*4882a593Smuzhiyun 		goto out;
1606*4882a593Smuzhiyun 	}
1607*4882a593Smuzhiyun 
1608*4882a593Smuzhiyun 	ieee80211_vif_update_chandef(sdata, chandef);
1609*4882a593Smuzhiyun 
1610*4882a593Smuzhiyun 	ret = ieee80211_assign_vif_chanctx(sdata, ctx);
1611*4882a593Smuzhiyun 	if (ret) {
1612*4882a593Smuzhiyun 		/* if assign fails refcount stays the same */
1613*4882a593Smuzhiyun 		if (ieee80211_chanctx_refcount(local, ctx) == 0)
1614*4882a593Smuzhiyun 			ieee80211_free_chanctx(local, ctx);
1615*4882a593Smuzhiyun 		goto out;
1616*4882a593Smuzhiyun 	}
1617*4882a593Smuzhiyun 
1618*4882a593Smuzhiyun 	ieee80211_recalc_smps_chanctx(local, ctx);
1619*4882a593Smuzhiyun 	ieee80211_recalc_radar_chanctx(local, ctx);
1620*4882a593Smuzhiyun  out:
1621*4882a593Smuzhiyun 	if (ret)
1622*4882a593Smuzhiyun 		sdata->radar_required = false;
1623*4882a593Smuzhiyun 
1624*4882a593Smuzhiyun 	mutex_unlock(&local->chanctx_mtx);
1625*4882a593Smuzhiyun 	return ret;
1626*4882a593Smuzhiyun }
1627*4882a593Smuzhiyun 
ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data * sdata)1628*4882a593Smuzhiyun int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata)
1629*4882a593Smuzhiyun {
1630*4882a593Smuzhiyun 	struct ieee80211_local *local = sdata->local;
1631*4882a593Smuzhiyun 	struct ieee80211_chanctx *new_ctx;
1632*4882a593Smuzhiyun 	struct ieee80211_chanctx *old_ctx;
1633*4882a593Smuzhiyun 	int err;
1634*4882a593Smuzhiyun 
1635*4882a593Smuzhiyun 	lockdep_assert_held(&local->mtx);
1636*4882a593Smuzhiyun 	lockdep_assert_held(&local->chanctx_mtx);
1637*4882a593Smuzhiyun 
1638*4882a593Smuzhiyun 	new_ctx = sdata->reserved_chanctx;
1639*4882a593Smuzhiyun 	old_ctx = ieee80211_vif_get_chanctx(sdata);
1640*4882a593Smuzhiyun 
1641*4882a593Smuzhiyun 	if (WARN_ON(!new_ctx))
1642*4882a593Smuzhiyun 		return -EINVAL;
1643*4882a593Smuzhiyun 
1644*4882a593Smuzhiyun 	if (WARN_ON(new_ctx->replace_state ==
1645*4882a593Smuzhiyun 		    IEEE80211_CHANCTX_WILL_BE_REPLACED))
1646*4882a593Smuzhiyun 		return -EINVAL;
1647*4882a593Smuzhiyun 
1648*4882a593Smuzhiyun 	if (WARN_ON(sdata->reserved_ready))
1649*4882a593Smuzhiyun 		return -EINVAL;
1650*4882a593Smuzhiyun 
1651*4882a593Smuzhiyun 	sdata->reserved_ready = true;
1652*4882a593Smuzhiyun 
1653*4882a593Smuzhiyun 	if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) {
1654*4882a593Smuzhiyun 		if (old_ctx)
1655*4882a593Smuzhiyun 			return ieee80211_vif_use_reserved_reassign(sdata);
1656*4882a593Smuzhiyun 
1657*4882a593Smuzhiyun 		return ieee80211_vif_use_reserved_assign(sdata);
1658*4882a593Smuzhiyun 	}
1659*4882a593Smuzhiyun 
1660*4882a593Smuzhiyun 	/*
1661*4882a593Smuzhiyun 	 * In-place reservation may need to be finalized now either if:
1662*4882a593Smuzhiyun 	 *  a) sdata is taking part in the swapping itself and is the last one
1663*4882a593Smuzhiyun 	 *  b) sdata has switched with a re-assign reservation to an existing
1664*4882a593Smuzhiyun 	 *     context readying in-place switching of old_ctx
1665*4882a593Smuzhiyun 	 *
1666*4882a593Smuzhiyun 	 * In case of (b) do not propagate the error up because the requested
1667*4882a593Smuzhiyun 	 * sdata already switched successfully. Just spill an extra warning.
1668*4882a593Smuzhiyun 	 * The ieee80211_vif_use_reserved_switch() already stops all necessary
1669*4882a593Smuzhiyun 	 * interfaces upon failure.
1670*4882a593Smuzhiyun 	 */
1671*4882a593Smuzhiyun 	if ((old_ctx &&
1672*4882a593Smuzhiyun 	     old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
1673*4882a593Smuzhiyun 	    new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
1674*4882a593Smuzhiyun 		err = ieee80211_vif_use_reserved_switch(local);
1675*4882a593Smuzhiyun 		if (err && err != -EAGAIN) {
1676*4882a593Smuzhiyun 			if (new_ctx->replace_state ==
1677*4882a593Smuzhiyun 			    IEEE80211_CHANCTX_REPLACES_OTHER)
1678*4882a593Smuzhiyun 				return err;
1679*4882a593Smuzhiyun 
1680*4882a593Smuzhiyun 			wiphy_info(local->hw.wiphy,
1681*4882a593Smuzhiyun 				   "depending in-place reservation failed (err=%d)\n",
1682*4882a593Smuzhiyun 				   err);
1683*4882a593Smuzhiyun 		}
1684*4882a593Smuzhiyun 	}
1685*4882a593Smuzhiyun 
1686*4882a593Smuzhiyun 	return 0;
1687*4882a593Smuzhiyun }
1688*4882a593Smuzhiyun 
ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data * sdata,const struct cfg80211_chan_def * chandef,u32 * changed)1689*4882a593Smuzhiyun int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
1690*4882a593Smuzhiyun 				   const struct cfg80211_chan_def *chandef,
1691*4882a593Smuzhiyun 				   u32 *changed)
1692*4882a593Smuzhiyun {
1693*4882a593Smuzhiyun 	struct ieee80211_local *local = sdata->local;
1694*4882a593Smuzhiyun 	struct ieee80211_chanctx_conf *conf;
1695*4882a593Smuzhiyun 	struct ieee80211_chanctx *ctx;
1696*4882a593Smuzhiyun 	const struct cfg80211_chan_def *compat;
1697*4882a593Smuzhiyun 	int ret;
1698*4882a593Smuzhiyun 
1699*4882a593Smuzhiyun 	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
1700*4882a593Smuzhiyun 				     IEEE80211_CHAN_DISABLED))
1701*4882a593Smuzhiyun 		return -EINVAL;
1702*4882a593Smuzhiyun 
1703*4882a593Smuzhiyun 	mutex_lock(&local->chanctx_mtx);
1704*4882a593Smuzhiyun 	if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) {
1705*4882a593Smuzhiyun 		ret = 0;
1706*4882a593Smuzhiyun 		goto out;
1707*4882a593Smuzhiyun 	}
1708*4882a593Smuzhiyun 
1709*4882a593Smuzhiyun 	if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
1710*4882a593Smuzhiyun 	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) {
1711*4882a593Smuzhiyun 		ret = -EINVAL;
1712*4882a593Smuzhiyun 		goto out;
1713*4882a593Smuzhiyun 	}
1714*4882a593Smuzhiyun 
1715*4882a593Smuzhiyun 	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
1716*4882a593Smuzhiyun 					 lockdep_is_held(&local->chanctx_mtx));
1717*4882a593Smuzhiyun 	if (!conf) {
1718*4882a593Smuzhiyun 		ret = -EINVAL;
1719*4882a593Smuzhiyun 		goto out;
1720*4882a593Smuzhiyun 	}
1721*4882a593Smuzhiyun 
1722*4882a593Smuzhiyun 	ctx = container_of(conf, struct ieee80211_chanctx, conf);
1723*4882a593Smuzhiyun 
1724*4882a593Smuzhiyun 	compat = cfg80211_chandef_compatible(&conf->def, chandef);
1725*4882a593Smuzhiyun 	if (!compat) {
1726*4882a593Smuzhiyun 		ret = -EINVAL;
1727*4882a593Smuzhiyun 		goto out;
1728*4882a593Smuzhiyun 	}
1729*4882a593Smuzhiyun 
1730*4882a593Smuzhiyun 	switch (ctx->replace_state) {
1731*4882a593Smuzhiyun 	case IEEE80211_CHANCTX_REPLACE_NONE:
1732*4882a593Smuzhiyun 		if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) {
1733*4882a593Smuzhiyun 			ret = -EBUSY;
1734*4882a593Smuzhiyun 			goto out;
1735*4882a593Smuzhiyun 		}
1736*4882a593Smuzhiyun 		break;
1737*4882a593Smuzhiyun 	case IEEE80211_CHANCTX_WILL_BE_REPLACED:
1738*4882a593Smuzhiyun 		/* TODO: Perhaps the bandwidth change could be treated as a
1739*4882a593Smuzhiyun 		 * reservation itself? */
1740*4882a593Smuzhiyun 		ret = -EBUSY;
1741*4882a593Smuzhiyun 		goto out;
1742*4882a593Smuzhiyun 	case IEEE80211_CHANCTX_REPLACES_OTHER:
1743*4882a593Smuzhiyun 		/* channel context that is going to replace another channel
1744*4882a593Smuzhiyun 		 * context doesn't really exist and shouldn't be assigned
1745*4882a593Smuzhiyun 		 * anywhere yet */
1746*4882a593Smuzhiyun 		WARN_ON(1);
1747*4882a593Smuzhiyun 		break;
1748*4882a593Smuzhiyun 	}
1749*4882a593Smuzhiyun 
1750*4882a593Smuzhiyun 	ieee80211_vif_update_chandef(sdata, chandef);
1751*4882a593Smuzhiyun 
1752*4882a593Smuzhiyun 	ieee80211_recalc_chanctx_chantype(local, ctx);
1753*4882a593Smuzhiyun 
1754*4882a593Smuzhiyun 	*changed |= BSS_CHANGED_BANDWIDTH;
1755*4882a593Smuzhiyun 	ret = 0;
1756*4882a593Smuzhiyun  out:
1757*4882a593Smuzhiyun 	mutex_unlock(&local->chanctx_mtx);
1758*4882a593Smuzhiyun 	return ret;
1759*4882a593Smuzhiyun }
1760*4882a593Smuzhiyun 
ieee80211_vif_release_channel(struct ieee80211_sub_if_data * sdata)1761*4882a593Smuzhiyun void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
1762*4882a593Smuzhiyun {
1763*4882a593Smuzhiyun 	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
1764*4882a593Smuzhiyun 
1765*4882a593Smuzhiyun 	lockdep_assert_held(&sdata->local->mtx);
1766*4882a593Smuzhiyun 
1767*4882a593Smuzhiyun 	mutex_lock(&sdata->local->chanctx_mtx);
1768*4882a593Smuzhiyun 	__ieee80211_vif_release_channel(sdata);
1769*4882a593Smuzhiyun 	mutex_unlock(&sdata->local->chanctx_mtx);
1770*4882a593Smuzhiyun }
1771*4882a593Smuzhiyun 
ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data * sdata)1772*4882a593Smuzhiyun void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
1773*4882a593Smuzhiyun {
1774*4882a593Smuzhiyun 	struct ieee80211_local *local = sdata->local;
1775*4882a593Smuzhiyun 	struct ieee80211_sub_if_data *ap;
1776*4882a593Smuzhiyun 	struct ieee80211_chanctx_conf *conf;
1777*4882a593Smuzhiyun 
1778*4882a593Smuzhiyun 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
1779*4882a593Smuzhiyun 		return;
1780*4882a593Smuzhiyun 
1781*4882a593Smuzhiyun 	ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
1782*4882a593Smuzhiyun 
1783*4882a593Smuzhiyun 	mutex_lock(&local->chanctx_mtx);
1784*4882a593Smuzhiyun 
1785*4882a593Smuzhiyun 	conf = rcu_dereference_protected(ap->vif.chanctx_conf,
1786*4882a593Smuzhiyun 					 lockdep_is_held(&local->chanctx_mtx));
1787*4882a593Smuzhiyun 	rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
1788*4882a593Smuzhiyun 	mutex_unlock(&local->chanctx_mtx);
1789*4882a593Smuzhiyun }
1790*4882a593Smuzhiyun 
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)1791*4882a593Smuzhiyun void ieee80211_iter_chan_contexts_atomic(
1792*4882a593Smuzhiyun 	struct ieee80211_hw *hw,
1793*4882a593Smuzhiyun 	void (*iter)(struct ieee80211_hw *hw,
1794*4882a593Smuzhiyun 		     struct ieee80211_chanctx_conf *chanctx_conf,
1795*4882a593Smuzhiyun 		     void *data),
1796*4882a593Smuzhiyun 	void *iter_data)
1797*4882a593Smuzhiyun {
1798*4882a593Smuzhiyun 	struct ieee80211_local *local = hw_to_local(hw);
1799*4882a593Smuzhiyun 	struct ieee80211_chanctx *ctx;
1800*4882a593Smuzhiyun 
1801*4882a593Smuzhiyun 	rcu_read_lock();
1802*4882a593Smuzhiyun 	list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
1803*4882a593Smuzhiyun 		if (ctx->driver_present)
1804*4882a593Smuzhiyun 			iter(hw, &ctx->conf, iter_data);
1805*4882a593Smuzhiyun 	rcu_read_unlock();
1806*4882a593Smuzhiyun }
1807*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);
1808