xref: /openbmc/linux/net/mac80211/chan.c (revision 04ecd257)
1f444de05SJohannes Berg /*
2f444de05SJohannes Berg  * mac80211 - channel management
3f444de05SJohannes Berg  */
4f444de05SJohannes Berg 
50aaffa9bSJohannes Berg #include <linux/nl80211.h>
63117bbdbSPaul Stewart #include <net/cfg80211.h>
7f444de05SJohannes Berg #include "ieee80211_i.h"
835f2fce9SMichal Kazior #include "driver-ops.h"
9f444de05SJohannes Berg 
1023a85b45SMichal Kazior static bool
1123a85b45SMichal Kazior ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
1223a85b45SMichal Kazior 				       enum nl80211_channel_type chantype2,
1323a85b45SMichal Kazior 				       enum nl80211_channel_type *compat)
1423a85b45SMichal Kazior {
1523a85b45SMichal Kazior 	/*
1623a85b45SMichal Kazior 	 * start out with chantype1 being the result,
1723a85b45SMichal Kazior 	 * overwriting later if needed
1823a85b45SMichal Kazior 	 */
1923a85b45SMichal Kazior 	if (compat)
2023a85b45SMichal Kazior 		*compat = chantype1;
2123a85b45SMichal Kazior 
2223a85b45SMichal Kazior 	switch (chantype1) {
230aaffa9bSJohannes Berg 	case NL80211_CHAN_NO_HT:
2423a85b45SMichal Kazior 		if (compat)
2523a85b45SMichal Kazior 			*compat = chantype2;
2623a85b45SMichal Kazior 		break;
270aaffa9bSJohannes Berg 	case NL80211_CHAN_HT20:
280aaffa9bSJohannes Berg 		/*
290aaffa9bSJohannes Berg 		 * allow any change that doesn't go to no-HT
300aaffa9bSJohannes Berg 		 * (if it already is no-HT no change is needed)
310aaffa9bSJohannes Berg 		 */
3223a85b45SMichal Kazior 		if (chantype2 == NL80211_CHAN_NO_HT)
330aaffa9bSJohannes Berg 			break;
3423a85b45SMichal Kazior 		if (compat)
3523a85b45SMichal Kazior 			*compat = chantype2;
360aaffa9bSJohannes Berg 		break;
370aaffa9bSJohannes Berg 	case NL80211_CHAN_HT40PLUS:
380aaffa9bSJohannes Berg 	case NL80211_CHAN_HT40MINUS:
390aaffa9bSJohannes Berg 		/* allow smaller bandwidth and same */
4023a85b45SMichal Kazior 		if (chantype2 == NL80211_CHAN_NO_HT)
410aaffa9bSJohannes Berg 			break;
4223a85b45SMichal Kazior 		if (chantype2 == NL80211_CHAN_HT20)
430aaffa9bSJohannes Berg 			break;
4423a85b45SMichal Kazior 		if (chantype2 == chantype1)
450aaffa9bSJohannes Berg 			break;
4623a85b45SMichal Kazior 		return false;
470aaffa9bSJohannes Berg 	}
480aaffa9bSJohannes Berg 
4923a85b45SMichal Kazior 	return true;
5023a85b45SMichal Kazior }
5123a85b45SMichal Kazior 
52e89a96f5SMichal Kazior static void ieee80211_change_chantype(struct ieee80211_local *local,
53e89a96f5SMichal Kazior 				      struct ieee80211_chanctx *ctx,
54e89a96f5SMichal Kazior 				      enum nl80211_channel_type chantype)
55e89a96f5SMichal Kazior {
56e89a96f5SMichal Kazior 	if (chantype == ctx->conf.channel_type)
57e89a96f5SMichal Kazior 		return;
58e89a96f5SMichal Kazior 
59e89a96f5SMichal Kazior 	ctx->conf.channel_type = chantype;
60e89a96f5SMichal Kazior 	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE);
6155de908aSJohannes Berg 
6255de908aSJohannes Berg 	if (!local->use_chanctx) {
6355de908aSJohannes Berg 		local->_oper_channel_type = chantype;
6455de908aSJohannes Berg 		ieee80211_hw_config(local, 0);
6555de908aSJohannes Berg 	}
660aaffa9bSJohannes Berg }
67d01a1e65SMichal Kazior 
68d01a1e65SMichal Kazior static struct ieee80211_chanctx *
69d01a1e65SMichal Kazior ieee80211_find_chanctx(struct ieee80211_local *local,
70d01a1e65SMichal Kazior 		       struct ieee80211_channel *channel,
71d01a1e65SMichal Kazior 		       enum nl80211_channel_type channel_type,
72d01a1e65SMichal Kazior 		       enum ieee80211_chanctx_mode mode)
73d01a1e65SMichal Kazior {
74d01a1e65SMichal Kazior 	struct ieee80211_chanctx *ctx;
75e89a96f5SMichal Kazior 	enum nl80211_channel_type compat_type;
76d01a1e65SMichal Kazior 
77d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
78d01a1e65SMichal Kazior 
79d01a1e65SMichal Kazior 	if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
80d01a1e65SMichal Kazior 		return NULL;
81d01a1e65SMichal Kazior 	if (WARN_ON(!channel))
82d01a1e65SMichal Kazior 		return NULL;
83d01a1e65SMichal Kazior 
84d01a1e65SMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
85e89a96f5SMichal Kazior 		compat_type = ctx->conf.channel_type;
86e89a96f5SMichal Kazior 
87d01a1e65SMichal Kazior 		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
88d01a1e65SMichal Kazior 			continue;
89d01a1e65SMichal Kazior 		if (ctx->conf.channel != channel)
90d01a1e65SMichal Kazior 			continue;
91e89a96f5SMichal Kazior 		if (!ieee80211_channel_types_are_compatible(ctx->conf.channel_type,
92e89a96f5SMichal Kazior 							    channel_type,
93e89a96f5SMichal Kazior 							    &compat_type))
94d01a1e65SMichal Kazior 			continue;
95d01a1e65SMichal Kazior 
96e89a96f5SMichal Kazior 		ieee80211_change_chantype(local, ctx, compat_type);
97e89a96f5SMichal Kazior 
98d01a1e65SMichal Kazior 		return ctx;
99d01a1e65SMichal Kazior 	}
100d01a1e65SMichal Kazior 
101d01a1e65SMichal Kazior 	return NULL;
102d01a1e65SMichal Kazior }
103d01a1e65SMichal Kazior 
104d01a1e65SMichal Kazior static struct ieee80211_chanctx *
105d01a1e65SMichal Kazior ieee80211_new_chanctx(struct ieee80211_local *local,
106d01a1e65SMichal Kazior 		      struct ieee80211_channel *channel,
107d01a1e65SMichal Kazior 		      enum nl80211_channel_type channel_type,
108d01a1e65SMichal Kazior 		      enum ieee80211_chanctx_mode mode)
109d01a1e65SMichal Kazior {
110d01a1e65SMichal Kazior 	struct ieee80211_chanctx *ctx;
11135f2fce9SMichal Kazior 	int err;
112d01a1e65SMichal Kazior 
113d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
114d01a1e65SMichal Kazior 
115d01a1e65SMichal Kazior 	ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
116d01a1e65SMichal Kazior 	if (!ctx)
117d01a1e65SMichal Kazior 		return ERR_PTR(-ENOMEM);
118d01a1e65SMichal Kazior 
119d01a1e65SMichal Kazior 	ctx->conf.channel = channel;
120d01a1e65SMichal Kazior 	ctx->conf.channel_type = channel_type;
12104ecd257SJohannes Berg 	ctx->conf.rx_chains_static = 1;
12204ecd257SJohannes Berg 	ctx->conf.rx_chains_dynamic = 1;
123d01a1e65SMichal Kazior 	ctx->mode = mode;
124d01a1e65SMichal Kazior 
12555de908aSJohannes Berg 	if (!local->use_chanctx) {
12655de908aSJohannes Berg 		local->_oper_channel_type = channel_type;
12755de908aSJohannes Berg 		local->_oper_channel = channel;
12855de908aSJohannes Berg 		ieee80211_hw_config(local, 0);
12955de908aSJohannes Berg 	} else {
13035f2fce9SMichal Kazior 		err = drv_add_chanctx(local, ctx);
13135f2fce9SMichal Kazior 		if (err) {
13235f2fce9SMichal Kazior 			kfree(ctx);
13335f2fce9SMichal Kazior 			return ERR_PTR(err);
13435f2fce9SMichal Kazior 		}
13555de908aSJohannes Berg 	}
13635f2fce9SMichal Kazior 
137d01a1e65SMichal Kazior 	list_add(&ctx->list, &local->chanctx_list);
138d01a1e65SMichal Kazior 
139d01a1e65SMichal Kazior 	return ctx;
140d01a1e65SMichal Kazior }
141d01a1e65SMichal Kazior 
142d01a1e65SMichal Kazior static void ieee80211_free_chanctx(struct ieee80211_local *local,
143d01a1e65SMichal Kazior 				   struct ieee80211_chanctx *ctx)
144d01a1e65SMichal Kazior {
145d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
146d01a1e65SMichal Kazior 
147d01a1e65SMichal Kazior 	WARN_ON_ONCE(ctx->refcount != 0);
148d01a1e65SMichal Kazior 
14955de908aSJohannes Berg 	if (!local->use_chanctx) {
15055de908aSJohannes Berg 		local->_oper_channel_type = NL80211_CHAN_NO_HT;
15155de908aSJohannes Berg 		ieee80211_hw_config(local, 0);
15255de908aSJohannes Berg 	} else {
15335f2fce9SMichal Kazior 		drv_remove_chanctx(local, ctx);
15455de908aSJohannes Berg 	}
15535f2fce9SMichal Kazior 
156d01a1e65SMichal Kazior 	list_del(&ctx->list);
157d01a1e65SMichal Kazior 	kfree_rcu(ctx, rcu_head);
158d01a1e65SMichal Kazior }
159d01a1e65SMichal Kazior 
160d01a1e65SMichal Kazior static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
161d01a1e65SMichal Kazior 					struct ieee80211_chanctx *ctx)
162d01a1e65SMichal Kazior {
16335f2fce9SMichal Kazior 	struct ieee80211_local *local = sdata->local;
16435f2fce9SMichal Kazior 	int ret;
165d01a1e65SMichal Kazior 
166d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
167d01a1e65SMichal Kazior 
16835f2fce9SMichal Kazior 	ret = drv_assign_vif_chanctx(local, sdata, ctx);
16935f2fce9SMichal Kazior 	if (ret)
17035f2fce9SMichal Kazior 		return ret;
17135f2fce9SMichal Kazior 
172d01a1e65SMichal Kazior 	rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
173d01a1e65SMichal Kazior 	ctx->refcount++;
174d01a1e65SMichal Kazior 
175d01a1e65SMichal Kazior 	return 0;
176d01a1e65SMichal Kazior }
177d01a1e65SMichal Kazior 
178e89a96f5SMichal Kazior static enum nl80211_channel_type
179e89a96f5SMichal Kazior ieee80211_calc_chantype(struct ieee80211_local *local,
180e89a96f5SMichal Kazior 			struct ieee80211_chanctx *ctx)
181e89a96f5SMichal Kazior {
182e89a96f5SMichal Kazior 	struct ieee80211_chanctx_conf *conf = &ctx->conf;
183e89a96f5SMichal Kazior 	struct ieee80211_sub_if_data *sdata;
184e89a96f5SMichal Kazior 	enum nl80211_channel_type result = NL80211_CHAN_NO_HT;
185e89a96f5SMichal Kazior 
186e89a96f5SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
187e89a96f5SMichal Kazior 
188e89a96f5SMichal Kazior 	rcu_read_lock();
189e89a96f5SMichal Kazior 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
190e89a96f5SMichal Kazior 		if (!ieee80211_sdata_running(sdata))
191e89a96f5SMichal Kazior 			continue;
192e89a96f5SMichal Kazior 		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
193e89a96f5SMichal Kazior 			continue;
194e89a96f5SMichal Kazior 
195e89a96f5SMichal Kazior 		WARN_ON_ONCE(!ieee80211_channel_types_are_compatible(
196e89a96f5SMichal Kazior 					sdata->vif.bss_conf.channel_type,
197e89a96f5SMichal Kazior 					result, &result));
198e89a96f5SMichal Kazior 	}
199e89a96f5SMichal Kazior 	rcu_read_unlock();
200e89a96f5SMichal Kazior 
201e89a96f5SMichal Kazior 	return result;
202e89a96f5SMichal Kazior }
203e89a96f5SMichal Kazior 
204e89a96f5SMichal Kazior static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
205e89a96f5SMichal Kazior 					      struct ieee80211_chanctx *ctx)
206e89a96f5SMichal Kazior {
207e89a96f5SMichal Kazior 	enum nl80211_channel_type chantype;
208e89a96f5SMichal Kazior 
209e89a96f5SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
210e89a96f5SMichal Kazior 
211e89a96f5SMichal Kazior 	chantype = ieee80211_calc_chantype(local, ctx);
212e89a96f5SMichal Kazior 	ieee80211_change_chantype(local, ctx, chantype);
213e89a96f5SMichal Kazior }
214e89a96f5SMichal Kazior 
215d01a1e65SMichal Kazior static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
216d01a1e65SMichal Kazior 					   struct ieee80211_chanctx *ctx)
217d01a1e65SMichal Kazior {
21835f2fce9SMichal Kazior 	struct ieee80211_local *local = sdata->local;
219d01a1e65SMichal Kazior 
220d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
221d01a1e65SMichal Kazior 
222d01a1e65SMichal Kazior 	ctx->refcount--;
223d01a1e65SMichal Kazior 	rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
22435f2fce9SMichal Kazior 
22535f2fce9SMichal Kazior 	drv_unassign_vif_chanctx(local, sdata, ctx);
226e89a96f5SMichal Kazior 
22704ecd257SJohannes Berg 	if (ctx->refcount > 0) {
228e89a96f5SMichal Kazior 		ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
22904ecd257SJohannes Berg 		ieee80211_recalc_smps_chanctx(local, ctx);
23004ecd257SJohannes Berg 	}
231d01a1e65SMichal Kazior }
232d01a1e65SMichal Kazior 
233d01a1e65SMichal Kazior static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
234d01a1e65SMichal Kazior {
235d01a1e65SMichal Kazior 	struct ieee80211_local *local = sdata->local;
236d01a1e65SMichal Kazior 	struct ieee80211_chanctx_conf *conf;
237d01a1e65SMichal Kazior 	struct ieee80211_chanctx *ctx;
238d01a1e65SMichal Kazior 
239d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
240d01a1e65SMichal Kazior 
241d01a1e65SMichal Kazior 	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
242d01a1e65SMichal Kazior 					 lockdep_is_held(&local->chanctx_mtx));
243d01a1e65SMichal Kazior 	if (!conf)
244d01a1e65SMichal Kazior 		return;
245d01a1e65SMichal Kazior 
246d01a1e65SMichal Kazior 	ctx = container_of(conf, struct ieee80211_chanctx, conf);
247d01a1e65SMichal Kazior 
248d01a1e65SMichal Kazior 	ieee80211_unassign_vif_chanctx(sdata, ctx);
249d01a1e65SMichal Kazior 	if (ctx->refcount == 0)
250d01a1e65SMichal Kazior 		ieee80211_free_chanctx(local, ctx);
251d01a1e65SMichal Kazior }
252d01a1e65SMichal Kazior 
25304ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
25404ecd257SJohannes Berg 				   struct ieee80211_chanctx *chanctx)
25504ecd257SJohannes Berg {
25604ecd257SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
25704ecd257SJohannes Berg 	u8 rx_chains_static, rx_chains_dynamic;
25804ecd257SJohannes Berg 
25904ecd257SJohannes Berg 	lockdep_assert_held(&local->chanctx_mtx);
26004ecd257SJohannes Berg 
26104ecd257SJohannes Berg 	rx_chains_static = 1;
26204ecd257SJohannes Berg 	rx_chains_dynamic = 1;
26304ecd257SJohannes Berg 
26404ecd257SJohannes Berg 	rcu_read_lock();
26504ecd257SJohannes Berg 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
26604ecd257SJohannes Berg 		u8 needed_static, needed_dynamic;
26704ecd257SJohannes Berg 
26804ecd257SJohannes Berg 		if (!ieee80211_sdata_running(sdata))
26904ecd257SJohannes Berg 			continue;
27004ecd257SJohannes Berg 
27104ecd257SJohannes Berg 		if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
27204ecd257SJohannes Berg 						&chanctx->conf)
27304ecd257SJohannes Berg 			continue;
27404ecd257SJohannes Berg 
27504ecd257SJohannes Berg 		switch (sdata->vif.type) {
27604ecd257SJohannes Berg 		case NL80211_IFTYPE_P2P_DEVICE:
27704ecd257SJohannes Berg 			continue;
27804ecd257SJohannes Berg 		case NL80211_IFTYPE_STATION:
27904ecd257SJohannes Berg 			if (!sdata->u.mgd.associated)
28004ecd257SJohannes Berg 				continue;
28104ecd257SJohannes Berg 			break;
28204ecd257SJohannes Berg 		case NL80211_IFTYPE_AP_VLAN:
28304ecd257SJohannes Berg 			continue;
28404ecd257SJohannes Berg 		case NL80211_IFTYPE_AP:
28504ecd257SJohannes Berg 		case NL80211_IFTYPE_ADHOC:
28604ecd257SJohannes Berg 		case NL80211_IFTYPE_WDS:
28704ecd257SJohannes Berg 		case NL80211_IFTYPE_MESH_POINT:
28804ecd257SJohannes Berg 			break;
28904ecd257SJohannes Berg 		default:
29004ecd257SJohannes Berg 			WARN_ON_ONCE(1);
29104ecd257SJohannes Berg 		}
29204ecd257SJohannes Berg 
29304ecd257SJohannes Berg 		switch (sdata->smps_mode) {
29404ecd257SJohannes Berg 		default:
29504ecd257SJohannes Berg 			WARN_ONCE(1, "Invalid SMPS mode %d\n",
29604ecd257SJohannes Berg 				  sdata->smps_mode);
29704ecd257SJohannes Berg 			/* fall through */
29804ecd257SJohannes Berg 		case IEEE80211_SMPS_OFF:
29904ecd257SJohannes Berg 			needed_static = sdata->needed_rx_chains;
30004ecd257SJohannes Berg 			needed_dynamic = sdata->needed_rx_chains;
30104ecd257SJohannes Berg 			break;
30204ecd257SJohannes Berg 		case IEEE80211_SMPS_DYNAMIC:
30304ecd257SJohannes Berg 			needed_static = 1;
30404ecd257SJohannes Berg 			needed_dynamic = sdata->needed_rx_chains;
30504ecd257SJohannes Berg 			break;
30604ecd257SJohannes Berg 		case IEEE80211_SMPS_STATIC:
30704ecd257SJohannes Berg 			needed_static = 1;
30804ecd257SJohannes Berg 			needed_dynamic = 1;
30904ecd257SJohannes Berg 			break;
31004ecd257SJohannes Berg 		}
31104ecd257SJohannes Berg 
31204ecd257SJohannes Berg 		rx_chains_static = max(rx_chains_static, needed_static);
31304ecd257SJohannes Berg 		rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
31404ecd257SJohannes Berg 	}
31504ecd257SJohannes Berg 	rcu_read_unlock();
31604ecd257SJohannes Berg 
31704ecd257SJohannes Berg 	if (!local->use_chanctx) {
31804ecd257SJohannes Berg 		if (rx_chains_static > 1)
31904ecd257SJohannes Berg 			local->smps_mode = IEEE80211_SMPS_OFF;
32004ecd257SJohannes Berg 		else if (rx_chains_dynamic > 1)
32104ecd257SJohannes Berg 			local->smps_mode = IEEE80211_SMPS_DYNAMIC;
32204ecd257SJohannes Berg 		else
32304ecd257SJohannes Berg 			local->smps_mode = IEEE80211_SMPS_STATIC;
32404ecd257SJohannes Berg 		ieee80211_hw_config(local, 0);
32504ecd257SJohannes Berg 	}
32604ecd257SJohannes Berg 
32704ecd257SJohannes Berg 	if (rx_chains_static == chanctx->conf.rx_chains_static &&
32804ecd257SJohannes Berg 	    rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
32904ecd257SJohannes Berg 		return;
33004ecd257SJohannes Berg 
33104ecd257SJohannes Berg 	chanctx->conf.rx_chains_static = rx_chains_static;
33204ecd257SJohannes Berg 	chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
33304ecd257SJohannes Berg 	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
33404ecd257SJohannes Berg }
33504ecd257SJohannes Berg 
336d01a1e65SMichal Kazior int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
337d01a1e65SMichal Kazior 			      struct ieee80211_channel *channel,
338d01a1e65SMichal Kazior 			      enum nl80211_channel_type channel_type,
339d01a1e65SMichal Kazior 			      enum ieee80211_chanctx_mode mode)
340d01a1e65SMichal Kazior {
341d01a1e65SMichal Kazior 	struct ieee80211_local *local = sdata->local;
342d01a1e65SMichal Kazior 	struct ieee80211_chanctx *ctx;
343d01a1e65SMichal Kazior 	int ret;
344d01a1e65SMichal Kazior 
34555de908aSJohannes Berg 	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
34655de908aSJohannes Berg 
347d01a1e65SMichal Kazior 	mutex_lock(&local->chanctx_mtx);
348d01a1e65SMichal Kazior 	__ieee80211_vif_release_channel(sdata);
349d01a1e65SMichal Kazior 
350d01a1e65SMichal Kazior 	ctx = ieee80211_find_chanctx(local, channel, channel_type, mode);
351d01a1e65SMichal Kazior 	if (!ctx)
352d01a1e65SMichal Kazior 		ctx = ieee80211_new_chanctx(local, channel, channel_type, mode);
353d01a1e65SMichal Kazior 	if (IS_ERR(ctx)) {
354d01a1e65SMichal Kazior 		ret = PTR_ERR(ctx);
355d01a1e65SMichal Kazior 		goto out;
356d01a1e65SMichal Kazior 	}
357d01a1e65SMichal Kazior 
35855de908aSJohannes Berg 	sdata->vif.bss_conf.channel_type = channel_type;
35955de908aSJohannes Berg 
360d01a1e65SMichal Kazior 	ret = ieee80211_assign_vif_chanctx(sdata, ctx);
361d01a1e65SMichal Kazior 	if (ret) {
362d01a1e65SMichal Kazior 		/* if assign fails refcount stays the same */
363d01a1e65SMichal Kazior 		if (ctx->refcount == 0)
364d01a1e65SMichal Kazior 			ieee80211_free_chanctx(local, ctx);
365d01a1e65SMichal Kazior 		goto out;
366d01a1e65SMichal Kazior 	}
367d01a1e65SMichal Kazior 
36804ecd257SJohannes Berg 	ieee80211_recalc_smps_chanctx(local, ctx);
369d01a1e65SMichal Kazior  out:
370d01a1e65SMichal Kazior 	mutex_unlock(&local->chanctx_mtx);
371d01a1e65SMichal Kazior 	return ret;
372d01a1e65SMichal Kazior }
373d01a1e65SMichal Kazior 
374d01a1e65SMichal Kazior void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
375d01a1e65SMichal Kazior {
37655de908aSJohannes Berg 	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
37755de908aSJohannes Berg 
378d01a1e65SMichal Kazior 	mutex_lock(&sdata->local->chanctx_mtx);
379d01a1e65SMichal Kazior 	__ieee80211_vif_release_channel(sdata);
380d01a1e65SMichal Kazior 	mutex_unlock(&sdata->local->chanctx_mtx);
381d01a1e65SMichal Kazior }
382