18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * mac80211 - channel management
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/nl80211.h>
78c2ecf20Sopenharmony_ci#include <linux/export.h>
88c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
98c2ecf20Sopenharmony_ci#include <net/cfg80211.h>
108c2ecf20Sopenharmony_ci#include "ieee80211_i.h"
118c2ecf20Sopenharmony_ci#include "driver-ops.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic int ieee80211_chanctx_num_assigned(struct ieee80211_local *local,
148c2ecf20Sopenharmony_ci					  struct ieee80211_chanctx *ctx)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
178c2ecf20Sopenharmony_ci	int num = 0;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list)
228c2ecf20Sopenharmony_ci		num++;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	return num;
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int ieee80211_chanctx_num_reserved(struct ieee80211_local *local,
288c2ecf20Sopenharmony_ci					  struct ieee80211_chanctx *ctx)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
318c2ecf20Sopenharmony_ci	int num = 0;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list)
368c2ecf20Sopenharmony_ci		num++;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return num;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ciint ieee80211_chanctx_refcount(struct ieee80211_local *local,
428c2ecf20Sopenharmony_ci			       struct ieee80211_chanctx *ctx)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	return ieee80211_chanctx_num_assigned(local, ctx) +
458c2ecf20Sopenharmony_ci	       ieee80211_chanctx_num_reserved(local, ctx);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int ieee80211_num_chanctx(struct ieee80211_local *local)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *ctx;
518c2ecf20Sopenharmony_ci	int num = 0;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	list_for_each_entry(ctx, &local->chanctx_list, list)
568c2ecf20Sopenharmony_ci		num++;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return num;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
648c2ecf20Sopenharmony_ci	return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local);
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic struct ieee80211_chanctx *
688c2ecf20Sopenharmony_ciieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct ieee80211_local *local __maybe_unused = sdata->local;
718c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *conf;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
748c2ecf20Sopenharmony_ci					 lockdep_is_held(&local->chanctx_mtx));
758c2ecf20Sopenharmony_ci	if (!conf)
768c2ecf20Sopenharmony_ci		return NULL;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return container_of(conf, struct ieee80211_chanctx, conf);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic const struct cfg80211_chan_def *
828c2ecf20Sopenharmony_ciieee80211_chanctx_reserved_chandef(struct ieee80211_local *local,
838c2ecf20Sopenharmony_ci				   struct ieee80211_chanctx *ctx,
848c2ecf20Sopenharmony_ci				   const struct cfg80211_chan_def *compat)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	list_for_each_entry(sdata, &ctx->reserved_vifs,
918c2ecf20Sopenharmony_ci			    reserved_chanctx_list) {
928c2ecf20Sopenharmony_ci		if (!compat)
938c2ecf20Sopenharmony_ci			compat = &sdata->reserved_chandef;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci		compat = cfg80211_chandef_compatible(&sdata->reserved_chandef,
968c2ecf20Sopenharmony_ci						     compat);
978c2ecf20Sopenharmony_ci		if (!compat)
988c2ecf20Sopenharmony_ci			break;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return compat;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic const struct cfg80211_chan_def *
1058c2ecf20Sopenharmony_ciieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local,
1068c2ecf20Sopenharmony_ci				       struct ieee80211_chanctx *ctx,
1078c2ecf20Sopenharmony_ci				       const struct cfg80211_chan_def *compat)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	list_for_each_entry(sdata, &ctx->assigned_vifs,
1148c2ecf20Sopenharmony_ci			    assigned_chanctx_list) {
1158c2ecf20Sopenharmony_ci		if (sdata->reserved_chanctx != NULL)
1168c2ecf20Sopenharmony_ci			continue;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci		if (!compat)
1198c2ecf20Sopenharmony_ci			compat = &sdata->vif.bss_conf.chandef;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		compat = cfg80211_chandef_compatible(
1228c2ecf20Sopenharmony_ci				&sdata->vif.bss_conf.chandef, compat);
1238c2ecf20Sopenharmony_ci		if (!compat)
1248c2ecf20Sopenharmony_ci			break;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return compat;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic const struct cfg80211_chan_def *
1318c2ecf20Sopenharmony_ciieee80211_chanctx_combined_chandef(struct ieee80211_local *local,
1328c2ecf20Sopenharmony_ci				   struct ieee80211_chanctx *ctx,
1338c2ecf20Sopenharmony_ci				   const struct cfg80211_chan_def *compat)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat);
1388c2ecf20Sopenharmony_ci	if (!compat)
1398c2ecf20Sopenharmony_ci		return NULL;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat);
1428c2ecf20Sopenharmony_ci	if (!compat)
1438c2ecf20Sopenharmony_ci		return NULL;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return compat;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic bool
1498c2ecf20Sopenharmony_ciieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local,
1508c2ecf20Sopenharmony_ci				      struct ieee80211_chanctx *ctx,
1518c2ecf20Sopenharmony_ci				      const struct cfg80211_chan_def *def)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (ieee80211_chanctx_combined_chandef(local, ctx, def))
1568c2ecf20Sopenharmony_ci		return true;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (!list_empty(&ctx->reserved_vifs) &&
1598c2ecf20Sopenharmony_ci	    ieee80211_chanctx_reserved_chandef(local, ctx, def))
1608c2ecf20Sopenharmony_ci		return true;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return false;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic struct ieee80211_chanctx *
1668c2ecf20Sopenharmony_ciieee80211_find_reservation_chanctx(struct ieee80211_local *local,
1678c2ecf20Sopenharmony_ci				   const struct cfg80211_chan_def *chandef,
1688c2ecf20Sopenharmony_ci				   enum ieee80211_chanctx_mode mode)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *ctx;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
1758c2ecf20Sopenharmony_ci		return NULL;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	list_for_each_entry(ctx, &local->chanctx_list, list) {
1788c2ecf20Sopenharmony_ci		if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
1798c2ecf20Sopenharmony_ci			continue;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
1828c2ecf20Sopenharmony_ci			continue;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		if (!ieee80211_chanctx_can_reserve_chandef(local, ctx,
1858c2ecf20Sopenharmony_ci							   chandef))
1868c2ecf20Sopenharmony_ci			continue;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci		return ctx;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return NULL;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cienum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	switch (sta->bandwidth) {
1978c2ecf20Sopenharmony_ci	case IEEE80211_STA_RX_BW_20:
1988c2ecf20Sopenharmony_ci		if (sta->ht_cap.ht_supported)
1998c2ecf20Sopenharmony_ci			return NL80211_CHAN_WIDTH_20;
2008c2ecf20Sopenharmony_ci		else
2018c2ecf20Sopenharmony_ci			return NL80211_CHAN_WIDTH_20_NOHT;
2028c2ecf20Sopenharmony_ci	case IEEE80211_STA_RX_BW_40:
2038c2ecf20Sopenharmony_ci		return NL80211_CHAN_WIDTH_40;
2048c2ecf20Sopenharmony_ci	case IEEE80211_STA_RX_BW_80:
2058c2ecf20Sopenharmony_ci		return NL80211_CHAN_WIDTH_80;
2068c2ecf20Sopenharmony_ci	case IEEE80211_STA_RX_BW_160:
2078c2ecf20Sopenharmony_ci		/*
2088c2ecf20Sopenharmony_ci		 * This applied for both 160 and 80+80. since we use
2098c2ecf20Sopenharmony_ci		 * the returned value to consider degradation of
2108c2ecf20Sopenharmony_ci		 * ctx->conf.min_def, we have to make sure to take
2118c2ecf20Sopenharmony_ci		 * the bigger one (NL80211_CHAN_WIDTH_160).
2128c2ecf20Sopenharmony_ci		 * Otherwise we might try degrading even when not
2138c2ecf20Sopenharmony_ci		 * needed, as the max required sta_bw returned (80+80)
2148c2ecf20Sopenharmony_ci		 * might be smaller than the configured bw (160).
2158c2ecf20Sopenharmony_ci		 */
2168c2ecf20Sopenharmony_ci		return NL80211_CHAN_WIDTH_160;
2178c2ecf20Sopenharmony_ci	default:
2188c2ecf20Sopenharmony_ci		WARN_ON(1);
2198c2ecf20Sopenharmony_ci		return NL80211_CHAN_WIDTH_20;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic enum nl80211_chan_width
2248c2ecf20Sopenharmony_ciieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
2278c2ecf20Sopenharmony_ci	struct sta_info *sta;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	rcu_read_lock();
2308c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
2318c2ecf20Sopenharmony_ci		if (sdata != sta->sdata &&
2328c2ecf20Sopenharmony_ci		    !(sta->sdata->bss && sta->sdata->bss == sdata->bss))
2338c2ecf20Sopenharmony_ci			continue;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta));
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci	rcu_read_unlock();
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	return max_bw;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic enum nl80211_chan_width
2438c2ecf20Sopenharmony_ciieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
2448c2ecf20Sopenharmony_ci				      struct ieee80211_chanctx_conf *conf)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
2478c2ecf20Sopenharmony_ci	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	rcu_read_lock();
2508c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
2518c2ecf20Sopenharmony_ci		struct ieee80211_vif *vif = &sdata->vif;
2528c2ecf20Sopenharmony_ci		enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		if (!ieee80211_sdata_running(sdata))
2558c2ecf20Sopenharmony_ci			continue;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
2588c2ecf20Sopenharmony_ci			continue;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		switch (vif->type) {
2618c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_AP:
2628c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_AP_VLAN:
2638c2ecf20Sopenharmony_ci			width = ieee80211_get_max_required_bw(sdata);
2648c2ecf20Sopenharmony_ci			break;
2658c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_STATION:
2668c2ecf20Sopenharmony_ci			/*
2678c2ecf20Sopenharmony_ci			 * The ap's sta->bandwidth is not set yet at this
2688c2ecf20Sopenharmony_ci			 * point, so take the width from the chandef, but
2698c2ecf20Sopenharmony_ci			 * account also for TDLS peers
2708c2ecf20Sopenharmony_ci			 */
2718c2ecf20Sopenharmony_ci			width = max(vif->bss_conf.chandef.width,
2728c2ecf20Sopenharmony_ci				    ieee80211_get_max_required_bw(sdata));
2738c2ecf20Sopenharmony_ci			break;
2748c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_P2P_DEVICE:
2758c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_NAN:
2768c2ecf20Sopenharmony_ci			continue;
2778c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_ADHOC:
2788c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_WDS:
2798c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_MESH_POINT:
2808c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_OCB:
2818c2ecf20Sopenharmony_ci			width = vif->bss_conf.chandef.width;
2828c2ecf20Sopenharmony_ci			break;
2838c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_UNSPECIFIED:
2848c2ecf20Sopenharmony_ci		case NUM_NL80211_IFTYPES:
2858c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_MONITOR:
2868c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_P2P_CLIENT:
2878c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_P2P_GO:
2888c2ecf20Sopenharmony_ci			WARN_ON_ONCE(1);
2898c2ecf20Sopenharmony_ci		}
2908c2ecf20Sopenharmony_ci		max_bw = max(max_bw, width);
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* use the configured bandwidth in case of monitor interface */
2948c2ecf20Sopenharmony_ci	sdata = rcu_dereference(local->monitor_sdata);
2958c2ecf20Sopenharmony_ci	if (sdata && rcu_access_pointer(sdata->vif.chanctx_conf) == conf)
2968c2ecf20Sopenharmony_ci		max_bw = max(max_bw, conf->def.width);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	rcu_read_unlock();
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	return max_bw;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci/*
3048c2ecf20Sopenharmony_ci * recalc the min required chan width of the channel context, which is
3058c2ecf20Sopenharmony_ci * the max of min required widths of all the interfaces bound to this
3068c2ecf20Sopenharmony_ci * channel context.
3078c2ecf20Sopenharmony_ci */
3088c2ecf20Sopenharmony_civoid ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
3098c2ecf20Sopenharmony_ci				      struct ieee80211_chanctx *ctx)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	enum nl80211_chan_width max_bw;
3128c2ecf20Sopenharmony_ci	struct cfg80211_chan_def min_def;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* don't optimize non-20MHz based and radar_enabled confs */
3178c2ecf20Sopenharmony_ci	if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 ||
3188c2ecf20Sopenharmony_ci	    ctx->conf.def.width == NL80211_CHAN_WIDTH_10 ||
3198c2ecf20Sopenharmony_ci	    ctx->conf.def.width == NL80211_CHAN_WIDTH_1 ||
3208c2ecf20Sopenharmony_ci	    ctx->conf.def.width == NL80211_CHAN_WIDTH_2 ||
3218c2ecf20Sopenharmony_ci	    ctx->conf.def.width == NL80211_CHAN_WIDTH_4 ||
3228c2ecf20Sopenharmony_ci	    ctx->conf.def.width == NL80211_CHAN_WIDTH_8 ||
3238c2ecf20Sopenharmony_ci	    ctx->conf.def.width == NL80211_CHAN_WIDTH_16 ||
3248c2ecf20Sopenharmony_ci	    ctx->conf.radar_enabled) {
3258c2ecf20Sopenharmony_ci		ctx->conf.min_def = ctx->conf.def;
3268c2ecf20Sopenharmony_ci		return;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/* downgrade chandef up to max_bw */
3328c2ecf20Sopenharmony_ci	min_def = ctx->conf.def;
3338c2ecf20Sopenharmony_ci	while (min_def.width > max_bw)
3348c2ecf20Sopenharmony_ci		ieee80211_chandef_downgrade(&min_def);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def))
3378c2ecf20Sopenharmony_ci		return;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	ctx->conf.min_def = min_def;
3408c2ecf20Sopenharmony_ci	if (!ctx->driver_present)
3418c2ecf20Sopenharmony_ci		return;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH);
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic void ieee80211_change_chanctx(struct ieee80211_local *local,
3478c2ecf20Sopenharmony_ci				     struct ieee80211_chanctx *ctx,
3488c2ecf20Sopenharmony_ci				     const struct cfg80211_chan_def *chandef)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) {
3518c2ecf20Sopenharmony_ci		ieee80211_recalc_chanctx_min_def(local, ctx);
3528c2ecf20Sopenharmony_ci		return;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	ctx->conf.def = *chandef;
3588c2ecf20Sopenharmony_ci	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
3598c2ecf20Sopenharmony_ci	ieee80211_recalc_chanctx_min_def(local, ctx);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (!local->use_chanctx) {
3628c2ecf20Sopenharmony_ci		local->_oper_chandef = *chandef;
3638c2ecf20Sopenharmony_ci		ieee80211_hw_config(local, 0);
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic struct ieee80211_chanctx *
3688c2ecf20Sopenharmony_ciieee80211_find_chanctx(struct ieee80211_local *local,
3698c2ecf20Sopenharmony_ci		       const struct cfg80211_chan_def *chandef,
3708c2ecf20Sopenharmony_ci		       enum ieee80211_chanctx_mode mode)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *ctx;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
3778c2ecf20Sopenharmony_ci		return NULL;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	list_for_each_entry(ctx, &local->chanctx_list, list) {
3808c2ecf20Sopenharmony_ci		const struct cfg80211_chan_def *compat;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE)
3838c2ecf20Sopenharmony_ci			continue;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
3868c2ecf20Sopenharmony_ci			continue;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
3898c2ecf20Sopenharmony_ci		if (!compat)
3908c2ecf20Sopenharmony_ci			continue;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		compat = ieee80211_chanctx_reserved_chandef(local, ctx,
3938c2ecf20Sopenharmony_ci							    compat);
3948c2ecf20Sopenharmony_ci		if (!compat)
3958c2ecf20Sopenharmony_ci			continue;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci		ieee80211_change_chanctx(local, ctx, compat);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		return ctx;
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	return NULL;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cibool ieee80211_is_radar_required(struct ieee80211_local *local)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->mtx);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	rcu_read_lock();
4128c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
4138c2ecf20Sopenharmony_ci		if (sdata->radar_required) {
4148c2ecf20Sopenharmony_ci			rcu_read_unlock();
4158c2ecf20Sopenharmony_ci			return true;
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci	rcu_read_unlock();
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return false;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic bool
4248c2ecf20Sopenharmony_ciieee80211_chanctx_radar_required(struct ieee80211_local *local,
4258c2ecf20Sopenharmony_ci				 struct ieee80211_chanctx *ctx)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *conf = &ctx->conf;
4288c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
4298c2ecf20Sopenharmony_ci	bool required = false;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
4328c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->mtx);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	rcu_read_lock();
4358c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
4368c2ecf20Sopenharmony_ci		if (!ieee80211_sdata_running(sdata))
4378c2ecf20Sopenharmony_ci			continue;
4388c2ecf20Sopenharmony_ci		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
4398c2ecf20Sopenharmony_ci			continue;
4408c2ecf20Sopenharmony_ci		if (!sdata->radar_required)
4418c2ecf20Sopenharmony_ci			continue;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		required = true;
4448c2ecf20Sopenharmony_ci		break;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci	rcu_read_unlock();
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	return required;
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic struct ieee80211_chanctx *
4528c2ecf20Sopenharmony_ciieee80211_alloc_chanctx(struct ieee80211_local *local,
4538c2ecf20Sopenharmony_ci			const struct cfg80211_chan_def *chandef,
4548c2ecf20Sopenharmony_ci			enum ieee80211_chanctx_mode mode)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *ctx;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
4618c2ecf20Sopenharmony_ci	if (!ctx)
4628c2ecf20Sopenharmony_ci		return NULL;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctx->assigned_vifs);
4658c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ctx->reserved_vifs);
4668c2ecf20Sopenharmony_ci	ctx->conf.def = *chandef;
4678c2ecf20Sopenharmony_ci	ctx->conf.rx_chains_static = 1;
4688c2ecf20Sopenharmony_ci	ctx->conf.rx_chains_dynamic = 1;
4698c2ecf20Sopenharmony_ci	ctx->mode = mode;
4708c2ecf20Sopenharmony_ci	ctx->conf.radar_enabled = false;
4718c2ecf20Sopenharmony_ci	ieee80211_recalc_chanctx_min_def(local, ctx);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	return ctx;
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic int ieee80211_add_chanctx(struct ieee80211_local *local,
4778c2ecf20Sopenharmony_ci				 struct ieee80211_chanctx *ctx)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	u32 changed;
4808c2ecf20Sopenharmony_ci	int err;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->mtx);
4838c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	if (!local->use_chanctx)
4868c2ecf20Sopenharmony_ci		local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	/* turn idle off *before* setting channel -- some drivers need that */
4898c2ecf20Sopenharmony_ci	changed = ieee80211_idle_off(local);
4908c2ecf20Sopenharmony_ci	if (changed)
4918c2ecf20Sopenharmony_ci		ieee80211_hw_config(local, changed);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	if (!local->use_chanctx) {
4948c2ecf20Sopenharmony_ci		local->_oper_chandef = ctx->conf.def;
4958c2ecf20Sopenharmony_ci		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
4968c2ecf20Sopenharmony_ci	} else {
4978c2ecf20Sopenharmony_ci		err = drv_add_chanctx(local, ctx);
4988c2ecf20Sopenharmony_ci		if (err) {
4998c2ecf20Sopenharmony_ci			ieee80211_recalc_idle(local);
5008c2ecf20Sopenharmony_ci			return err;
5018c2ecf20Sopenharmony_ci		}
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	return 0;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic struct ieee80211_chanctx *
5088c2ecf20Sopenharmony_ciieee80211_new_chanctx(struct ieee80211_local *local,
5098c2ecf20Sopenharmony_ci		      const struct cfg80211_chan_def *chandef,
5108c2ecf20Sopenharmony_ci		      enum ieee80211_chanctx_mode mode)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *ctx;
5138c2ecf20Sopenharmony_ci	int err;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->mtx);
5168c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	ctx = ieee80211_alloc_chanctx(local, chandef, mode);
5198c2ecf20Sopenharmony_ci	if (!ctx)
5208c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	err = ieee80211_add_chanctx(local, ctx);
5238c2ecf20Sopenharmony_ci	if (err) {
5248c2ecf20Sopenharmony_ci		kfree(ctx);
5258c2ecf20Sopenharmony_ci		return ERR_PTR(err);
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	list_add_rcu(&ctx->list, &local->chanctx_list);
5298c2ecf20Sopenharmony_ci	return ctx;
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic void ieee80211_del_chanctx(struct ieee80211_local *local,
5338c2ecf20Sopenharmony_ci				  struct ieee80211_chanctx *ctx)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	if (!local->use_chanctx) {
5388c2ecf20Sopenharmony_ci		struct cfg80211_chan_def *chandef = &local->_oper_chandef;
5398c2ecf20Sopenharmony_ci		/* S1G doesn't have 20MHz, so get the correct width for the
5408c2ecf20Sopenharmony_ci		 * current channel.
5418c2ecf20Sopenharmony_ci		 */
5428c2ecf20Sopenharmony_ci		if (chandef->chan->band == NL80211_BAND_S1GHZ)
5438c2ecf20Sopenharmony_ci			chandef->width =
5448c2ecf20Sopenharmony_ci				ieee80211_s1g_channel_width(chandef->chan);
5458c2ecf20Sopenharmony_ci		else
5468c2ecf20Sopenharmony_ci			chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
5478c2ecf20Sopenharmony_ci		chandef->center_freq1 = chandef->chan->center_freq;
5488c2ecf20Sopenharmony_ci		chandef->freq1_offset = chandef->chan->freq_offset;
5498c2ecf20Sopenharmony_ci		chandef->center_freq2 = 0;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci		/* NOTE: Disabling radar is only valid here for
5528c2ecf20Sopenharmony_ci		 * single channel context. To be sure, check it ...
5538c2ecf20Sopenharmony_ci		 */
5548c2ecf20Sopenharmony_ci		WARN_ON(local->hw.conf.radar_enabled &&
5558c2ecf20Sopenharmony_ci			!list_empty(&local->chanctx_list));
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci		local->hw.conf.radar_enabled = false;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
5608c2ecf20Sopenharmony_ci	} else {
5618c2ecf20Sopenharmony_ci		drv_remove_chanctx(local, ctx);
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	ieee80211_recalc_idle(local);
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cistatic void ieee80211_free_chanctx(struct ieee80211_local *local,
5688c2ecf20Sopenharmony_ci				   struct ieee80211_chanctx *ctx)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	list_del_rcu(&ctx->list);
5758c2ecf20Sopenharmony_ci	ieee80211_del_chanctx(local, ctx);
5768c2ecf20Sopenharmony_ci	kfree_rcu(ctx, rcu_head);
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_civoid ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
5808c2ecf20Sopenharmony_ci				       struct ieee80211_chanctx *ctx)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *conf = &ctx->conf;
5838c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
5848c2ecf20Sopenharmony_ci	const struct cfg80211_chan_def *compat = NULL;
5858c2ecf20Sopenharmony_ci	struct sta_info *sta;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	rcu_read_lock();
5908c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci		if (!ieee80211_sdata_running(sdata))
5938c2ecf20Sopenharmony_ci			continue;
5948c2ecf20Sopenharmony_ci		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
5958c2ecf20Sopenharmony_ci			continue;
5968c2ecf20Sopenharmony_ci		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
5978c2ecf20Sopenharmony_ci			continue;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci		if (!compat)
6008c2ecf20Sopenharmony_ci			compat = &sdata->vif.bss_conf.chandef;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci		compat = cfg80211_chandef_compatible(
6038c2ecf20Sopenharmony_ci				&sdata->vif.bss_conf.chandef, compat);
6048c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(!compat))
6058c2ecf20Sopenharmony_ci			break;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	/* TDLS peers can sometimes affect the chandef width */
6098c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(sta, &local->sta_list, list) {
6108c2ecf20Sopenharmony_ci		if (!sta->uploaded ||
6118c2ecf20Sopenharmony_ci		    !test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) ||
6128c2ecf20Sopenharmony_ci		    !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
6138c2ecf20Sopenharmony_ci		    !sta->tdls_chandef.chan)
6148c2ecf20Sopenharmony_ci			continue;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci		compat = cfg80211_chandef_compatible(&sta->tdls_chandef,
6178c2ecf20Sopenharmony_ci						     compat);
6188c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(!compat))
6198c2ecf20Sopenharmony_ci			break;
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci	rcu_read_unlock();
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	if (!compat)
6248c2ecf20Sopenharmony_ci		return;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	ieee80211_change_chanctx(local, ctx, compat);
6278c2ecf20Sopenharmony_ci}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_cistatic void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
6308c2ecf20Sopenharmony_ci					   struct ieee80211_chanctx *chanctx)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	bool radar_enabled;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
6358c2ecf20Sopenharmony_ci	/* for ieee80211_is_radar_required */
6368c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->mtx);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	radar_enabled = ieee80211_chanctx_radar_required(local, chanctx);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if (radar_enabled == chanctx->conf.radar_enabled)
6418c2ecf20Sopenharmony_ci		return;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	chanctx->conf.radar_enabled = radar_enabled;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	if (!local->use_chanctx) {
6468c2ecf20Sopenharmony_ci		local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
6478c2ecf20Sopenharmony_ci		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_cistatic int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
6548c2ecf20Sopenharmony_ci					struct ieee80211_chanctx *new_ctx)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
6578c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *conf;
6588c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *curr_ctx = NULL;
6598c2ecf20Sopenharmony_ci	int ret = 0;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_NAN))
6628c2ecf20Sopenharmony_ci		return -ENOTSUPP;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
6658c2ecf20Sopenharmony_ci					 lockdep_is_held(&local->chanctx_mtx));
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if (conf) {
6688c2ecf20Sopenharmony_ci		curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci		drv_unassign_vif_chanctx(local, sdata, curr_ctx);
6718c2ecf20Sopenharmony_ci		conf = NULL;
6728c2ecf20Sopenharmony_ci		list_del(&sdata->assigned_chanctx_list);
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	if (new_ctx) {
6768c2ecf20Sopenharmony_ci		ret = drv_assign_vif_chanctx(local, sdata, new_ctx);
6778c2ecf20Sopenharmony_ci		if (ret)
6788c2ecf20Sopenharmony_ci			goto out;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci		conf = &new_ctx->conf;
6818c2ecf20Sopenharmony_ci		list_add(&sdata->assigned_chanctx_list,
6828c2ecf20Sopenharmony_ci			 &new_ctx->assigned_vifs);
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ciout:
6868c2ecf20Sopenharmony_ci	rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	sdata->vif.bss_conf.idle = !conf;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) {
6918c2ecf20Sopenharmony_ci		ieee80211_recalc_chanctx_chantype(local, curr_ctx);
6928c2ecf20Sopenharmony_ci		ieee80211_recalc_smps_chanctx(local, curr_ctx);
6938c2ecf20Sopenharmony_ci		ieee80211_recalc_radar_chanctx(local, curr_ctx);
6948c2ecf20Sopenharmony_ci		ieee80211_recalc_chanctx_min_def(local, curr_ctx);
6958c2ecf20Sopenharmony_ci	}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
6988c2ecf20Sopenharmony_ci		ieee80211_recalc_txpower(sdata, false);
6998c2ecf20Sopenharmony_ci		ieee80211_recalc_chanctx_min_def(local, new_ctx);
7008c2ecf20Sopenharmony_ci	}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
7038c2ecf20Sopenharmony_ci	    sdata->vif.type != NL80211_IFTYPE_MONITOR)
7048c2ecf20Sopenharmony_ci		ieee80211_bss_info_change_notify(sdata,
7058c2ecf20Sopenharmony_ci						 BSS_CHANGED_IDLE);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	ieee80211_check_fast_xmit_iface(sdata);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	return ret;
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_civoid ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
7138c2ecf20Sopenharmony_ci				   struct ieee80211_chanctx *chanctx)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
7168c2ecf20Sopenharmony_ci	u8 rx_chains_static, rx_chains_dynamic;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	rx_chains_static = 1;
7218c2ecf20Sopenharmony_ci	rx_chains_dynamic = 1;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	rcu_read_lock();
7248c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
7258c2ecf20Sopenharmony_ci		u8 needed_static, needed_dynamic;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci		if (!ieee80211_sdata_running(sdata))
7288c2ecf20Sopenharmony_ci			continue;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci		if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
7318c2ecf20Sopenharmony_ci						&chanctx->conf)
7328c2ecf20Sopenharmony_ci			continue;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci		switch (sdata->vif.type) {
7358c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_P2P_DEVICE:
7368c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_NAN:
7378c2ecf20Sopenharmony_ci			continue;
7388c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_STATION:
7398c2ecf20Sopenharmony_ci			if (!sdata->u.mgd.associated)
7408c2ecf20Sopenharmony_ci				continue;
7418c2ecf20Sopenharmony_ci			break;
7428c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_AP_VLAN:
7438c2ecf20Sopenharmony_ci			continue;
7448c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_AP:
7458c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_ADHOC:
7468c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_WDS:
7478c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_MESH_POINT:
7488c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_OCB:
7498c2ecf20Sopenharmony_ci			break;
7508c2ecf20Sopenharmony_ci		default:
7518c2ecf20Sopenharmony_ci			WARN_ON_ONCE(1);
7528c2ecf20Sopenharmony_ci		}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci		switch (sdata->smps_mode) {
7558c2ecf20Sopenharmony_ci		default:
7568c2ecf20Sopenharmony_ci			WARN_ONCE(1, "Invalid SMPS mode %d\n",
7578c2ecf20Sopenharmony_ci				  sdata->smps_mode);
7588c2ecf20Sopenharmony_ci			fallthrough;
7598c2ecf20Sopenharmony_ci		case IEEE80211_SMPS_OFF:
7608c2ecf20Sopenharmony_ci			needed_static = sdata->needed_rx_chains;
7618c2ecf20Sopenharmony_ci			needed_dynamic = sdata->needed_rx_chains;
7628c2ecf20Sopenharmony_ci			break;
7638c2ecf20Sopenharmony_ci		case IEEE80211_SMPS_DYNAMIC:
7648c2ecf20Sopenharmony_ci			needed_static = 1;
7658c2ecf20Sopenharmony_ci			needed_dynamic = sdata->needed_rx_chains;
7668c2ecf20Sopenharmony_ci			break;
7678c2ecf20Sopenharmony_ci		case IEEE80211_SMPS_STATIC:
7688c2ecf20Sopenharmony_ci			needed_static = 1;
7698c2ecf20Sopenharmony_ci			needed_dynamic = 1;
7708c2ecf20Sopenharmony_ci			break;
7718c2ecf20Sopenharmony_ci		}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci		rx_chains_static = max(rx_chains_static, needed_static);
7748c2ecf20Sopenharmony_ci		rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
7758c2ecf20Sopenharmony_ci	}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	/* Disable SMPS for the monitor interface */
7788c2ecf20Sopenharmony_ci	sdata = rcu_dereference(local->monitor_sdata);
7798c2ecf20Sopenharmony_ci	if (sdata &&
7808c2ecf20Sopenharmony_ci	    rcu_access_pointer(sdata->vif.chanctx_conf) == &chanctx->conf)
7818c2ecf20Sopenharmony_ci		rx_chains_dynamic = rx_chains_static = local->rx_chains;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	rcu_read_unlock();
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	if (!local->use_chanctx) {
7868c2ecf20Sopenharmony_ci		if (rx_chains_static > 1)
7878c2ecf20Sopenharmony_ci			local->smps_mode = IEEE80211_SMPS_OFF;
7888c2ecf20Sopenharmony_ci		else if (rx_chains_dynamic > 1)
7898c2ecf20Sopenharmony_ci			local->smps_mode = IEEE80211_SMPS_DYNAMIC;
7908c2ecf20Sopenharmony_ci		else
7918c2ecf20Sopenharmony_ci			local->smps_mode = IEEE80211_SMPS_STATIC;
7928c2ecf20Sopenharmony_ci		ieee80211_hw_config(local, 0);
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	if (rx_chains_static == chanctx->conf.rx_chains_static &&
7968c2ecf20Sopenharmony_ci	    rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
7978c2ecf20Sopenharmony_ci		return;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	chanctx->conf.rx_chains_static = rx_chains_static;
8008c2ecf20Sopenharmony_ci	chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
8018c2ecf20Sopenharmony_ci	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic void
8058c2ecf20Sopenharmony_ci__ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
8068c2ecf20Sopenharmony_ci				      bool clear)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	struct ieee80211_local *local __maybe_unused = sdata->local;
8098c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *vlan;
8108c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *conf;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
8138c2ecf20Sopenharmony_ci		return;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->mtx);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	/* Check that conf exists, even when clearing this function
8188c2ecf20Sopenharmony_ci	 * must be called with the AP's channel context still there
8198c2ecf20Sopenharmony_ci	 * as it would otherwise cause VLANs to have an invalid
8208c2ecf20Sopenharmony_ci	 * channel context pointer for a while, possibly pointing
8218c2ecf20Sopenharmony_ci	 * to a channel context that has already been freed.
8228c2ecf20Sopenharmony_ci	 */
8238c2ecf20Sopenharmony_ci	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
8248c2ecf20Sopenharmony_ci					 lockdep_is_held(&local->chanctx_mtx));
8258c2ecf20Sopenharmony_ci	WARN_ON(!conf);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	if (clear)
8288c2ecf20Sopenharmony_ci		conf = NULL;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
8318c2ecf20Sopenharmony_ci		rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
8328c2ecf20Sopenharmony_ci}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_civoid ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
8358c2ecf20Sopenharmony_ci					 bool clear)
8368c2ecf20Sopenharmony_ci{
8378c2ecf20Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	mutex_lock(&local->chanctx_mtx);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	__ieee80211_vif_copy_chanctx_to_vlans(sdata, clear);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	mutex_unlock(&local->chanctx_mtx);
8448c2ecf20Sopenharmony_ci}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ciint ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *ctx = sdata->reserved_chanctx;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	lockdep_assert_held(&sdata->local->chanctx_mtx);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	if (WARN_ON(!ctx))
8538c2ecf20Sopenharmony_ci		return -EINVAL;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	list_del(&sdata->reserved_chanctx_list);
8568c2ecf20Sopenharmony_ci	sdata->reserved_chanctx = NULL;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) {
8598c2ecf20Sopenharmony_ci		if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
8608c2ecf20Sopenharmony_ci			if (WARN_ON(!ctx->replace_ctx))
8618c2ecf20Sopenharmony_ci				return -EINVAL;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci			WARN_ON(ctx->replace_ctx->replace_state !=
8648c2ecf20Sopenharmony_ci			        IEEE80211_CHANCTX_WILL_BE_REPLACED);
8658c2ecf20Sopenharmony_ci			WARN_ON(ctx->replace_ctx->replace_ctx != ctx);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci			ctx->replace_ctx->replace_ctx = NULL;
8688c2ecf20Sopenharmony_ci			ctx->replace_ctx->replace_state =
8698c2ecf20Sopenharmony_ci					IEEE80211_CHANCTX_REPLACE_NONE;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci			list_del_rcu(&ctx->list);
8728c2ecf20Sopenharmony_ci			kfree_rcu(ctx, rcu_head);
8738c2ecf20Sopenharmony_ci		} else {
8748c2ecf20Sopenharmony_ci			ieee80211_free_chanctx(sdata->local, ctx);
8758c2ecf20Sopenharmony_ci		}
8768c2ecf20Sopenharmony_ci	}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	return 0;
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ciint ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
8828c2ecf20Sopenharmony_ci				  const struct cfg80211_chan_def *chandef,
8838c2ecf20Sopenharmony_ci				  enum ieee80211_chanctx_mode mode,
8848c2ecf20Sopenharmony_ci				  bool radar_required)
8858c2ecf20Sopenharmony_ci{
8868c2ecf20Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
8878c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	curr_ctx = ieee80211_vif_get_chanctx(sdata);
8928c2ecf20Sopenharmony_ci	if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx)
8938c2ecf20Sopenharmony_ci		return -ENOTSUPP;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
8968c2ecf20Sopenharmony_ci	if (!new_ctx) {
8978c2ecf20Sopenharmony_ci		if (ieee80211_can_create_new_chanctx(local)) {
8988c2ecf20Sopenharmony_ci			new_ctx = ieee80211_new_chanctx(local, chandef, mode);
8998c2ecf20Sopenharmony_ci			if (IS_ERR(new_ctx))
9008c2ecf20Sopenharmony_ci				return PTR_ERR(new_ctx);
9018c2ecf20Sopenharmony_ci		} else {
9028c2ecf20Sopenharmony_ci			if (!curr_ctx ||
9038c2ecf20Sopenharmony_ci			    (curr_ctx->replace_state ==
9048c2ecf20Sopenharmony_ci			     IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
9058c2ecf20Sopenharmony_ci			    !list_empty(&curr_ctx->reserved_vifs)) {
9068c2ecf20Sopenharmony_ci				/*
9078c2ecf20Sopenharmony_ci				 * Another vif already requested this context
9088c2ecf20Sopenharmony_ci				 * for a reservation. Find another one hoping
9098c2ecf20Sopenharmony_ci				 * all vifs assigned to it will also switch
9108c2ecf20Sopenharmony_ci				 * soon enough.
9118c2ecf20Sopenharmony_ci				 *
9128c2ecf20Sopenharmony_ci				 * TODO: This needs a little more work as some
9138c2ecf20Sopenharmony_ci				 * cases (more than 2 chanctx capable devices)
9148c2ecf20Sopenharmony_ci				 * may fail which could otherwise succeed
9158c2ecf20Sopenharmony_ci				 * provided some channel context juggling was
9168c2ecf20Sopenharmony_ci				 * performed.
9178c2ecf20Sopenharmony_ci				 *
9188c2ecf20Sopenharmony_ci				 * Consider ctx1..3, vif1..6, each ctx has 2
9198c2ecf20Sopenharmony_ci				 * vifs. vif1 and vif2 from ctx1 request new
9208c2ecf20Sopenharmony_ci				 * different chandefs starting 2 in-place
9218c2ecf20Sopenharmony_ci				 * reserations with ctx4 and ctx5 replacing
9228c2ecf20Sopenharmony_ci				 * ctx1 and ctx2 respectively. Next vif5 and
9238c2ecf20Sopenharmony_ci				 * vif6 from ctx3 reserve ctx4. If vif3 and
9248c2ecf20Sopenharmony_ci				 * vif4 remain on ctx2 as they are then this
9258c2ecf20Sopenharmony_ci				 * fails unless `replace_ctx` from ctx5 is
9268c2ecf20Sopenharmony_ci				 * replaced with ctx3.
9278c2ecf20Sopenharmony_ci				 */
9288c2ecf20Sopenharmony_ci				list_for_each_entry(ctx, &local->chanctx_list,
9298c2ecf20Sopenharmony_ci						    list) {
9308c2ecf20Sopenharmony_ci					if (ctx->replace_state !=
9318c2ecf20Sopenharmony_ci					    IEEE80211_CHANCTX_REPLACE_NONE)
9328c2ecf20Sopenharmony_ci						continue;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci					if (!list_empty(&ctx->reserved_vifs))
9358c2ecf20Sopenharmony_ci						continue;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci					curr_ctx = ctx;
9388c2ecf20Sopenharmony_ci					break;
9398c2ecf20Sopenharmony_ci				}
9408c2ecf20Sopenharmony_ci			}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci			/*
9438c2ecf20Sopenharmony_ci			 * If that's true then all available contexts already
9448c2ecf20Sopenharmony_ci			 * have reservations and cannot be used.
9458c2ecf20Sopenharmony_ci			 */
9468c2ecf20Sopenharmony_ci			if (!curr_ctx ||
9478c2ecf20Sopenharmony_ci			    (curr_ctx->replace_state ==
9488c2ecf20Sopenharmony_ci			     IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
9498c2ecf20Sopenharmony_ci			    !list_empty(&curr_ctx->reserved_vifs))
9508c2ecf20Sopenharmony_ci				return -EBUSY;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci			new_ctx = ieee80211_alloc_chanctx(local, chandef, mode);
9538c2ecf20Sopenharmony_ci			if (!new_ctx)
9548c2ecf20Sopenharmony_ci				return -ENOMEM;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci			new_ctx->replace_ctx = curr_ctx;
9578c2ecf20Sopenharmony_ci			new_ctx->replace_state =
9588c2ecf20Sopenharmony_ci					IEEE80211_CHANCTX_REPLACES_OTHER;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci			curr_ctx->replace_ctx = new_ctx;
9618c2ecf20Sopenharmony_ci			curr_ctx->replace_state =
9628c2ecf20Sopenharmony_ci					IEEE80211_CHANCTX_WILL_BE_REPLACED;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci			list_add_rcu(&new_ctx->list, &local->chanctx_list);
9658c2ecf20Sopenharmony_ci		}
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs);
9698c2ecf20Sopenharmony_ci	sdata->reserved_chanctx = new_ctx;
9708c2ecf20Sopenharmony_ci	sdata->reserved_chandef = *chandef;
9718c2ecf20Sopenharmony_ci	sdata->reserved_radar_required = radar_required;
9728c2ecf20Sopenharmony_ci	sdata->reserved_ready = false;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	return 0;
9758c2ecf20Sopenharmony_ci}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_cistatic void
9788c2ecf20Sopenharmony_ciieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata)
9798c2ecf20Sopenharmony_ci{
9808c2ecf20Sopenharmony_ci	switch (sdata->vif.type) {
9818c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
9828c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP:
9838c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT:
9848c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_OCB:
9858c2ecf20Sopenharmony_ci		ieee80211_queue_work(&sdata->local->hw,
9868c2ecf20Sopenharmony_ci				     &sdata->csa_finalize_work);
9878c2ecf20Sopenharmony_ci		break;
9888c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
9898c2ecf20Sopenharmony_ci		ieee80211_queue_work(&sdata->local->hw,
9908c2ecf20Sopenharmony_ci				     &sdata->u.mgd.chswitch_work);
9918c2ecf20Sopenharmony_ci		break;
9928c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_UNSPECIFIED:
9938c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP_VLAN:
9948c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_WDS:
9958c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
9968c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_P2P_CLIENT:
9978c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_P2P_GO:
9988c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_P2P_DEVICE:
9998c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_NAN:
10008c2ecf20Sopenharmony_ci	case NUM_NL80211_IFTYPES:
10018c2ecf20Sopenharmony_ci		WARN_ON(1);
10028c2ecf20Sopenharmony_ci		break;
10038c2ecf20Sopenharmony_ci	}
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_cistatic void
10078c2ecf20Sopenharmony_ciieee80211_vif_update_chandef(struct ieee80211_sub_if_data *sdata,
10088c2ecf20Sopenharmony_ci			     const struct cfg80211_chan_def *chandef)
10098c2ecf20Sopenharmony_ci{
10108c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *vlan;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	sdata->vif.bss_conf.chandef = *chandef;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	if (sdata->vif.type != NL80211_IFTYPE_AP)
10158c2ecf20Sopenharmony_ci		return;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
10188c2ecf20Sopenharmony_ci		vlan->vif.bss_conf.chandef = *chandef;
10198c2ecf20Sopenharmony_ci}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_cistatic int
10228c2ecf20Sopenharmony_ciieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
10238c2ecf20Sopenharmony_ci{
10248c2ecf20Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
10258c2ecf20Sopenharmony_ci	struct ieee80211_vif_chanctx_switch vif_chsw[1] = {};
10268c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *old_ctx, *new_ctx;
10278c2ecf20Sopenharmony_ci	const struct cfg80211_chan_def *chandef;
10288c2ecf20Sopenharmony_ci	u32 changed = 0;
10298c2ecf20Sopenharmony_ci	int err;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->mtx);
10328c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	new_ctx = sdata->reserved_chanctx;
10358c2ecf20Sopenharmony_ci	old_ctx = ieee80211_vif_get_chanctx(sdata);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	if (WARN_ON(!sdata->reserved_ready))
10388c2ecf20Sopenharmony_ci		return -EBUSY;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	if (WARN_ON(!new_ctx))
10418c2ecf20Sopenharmony_ci		return -EINVAL;
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	if (WARN_ON(!old_ctx))
10448c2ecf20Sopenharmony_ci		return -EINVAL;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	if (WARN_ON(new_ctx->replace_state ==
10478c2ecf20Sopenharmony_ci		    IEEE80211_CHANCTX_REPLACES_OTHER))
10488c2ecf20Sopenharmony_ci		return -EINVAL;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
10518c2ecf20Sopenharmony_ci				&sdata->reserved_chandef);
10528c2ecf20Sopenharmony_ci	if (WARN_ON(!chandef))
10538c2ecf20Sopenharmony_ci		return -EINVAL;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	ieee80211_change_chanctx(local, new_ctx, chandef);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	vif_chsw[0].vif = &sdata->vif;
10588c2ecf20Sopenharmony_ci	vif_chsw[0].old_ctx = &old_ctx->conf;
10598c2ecf20Sopenharmony_ci	vif_chsw[0].new_ctx = &new_ctx->conf;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	list_del(&sdata->reserved_chanctx_list);
10628c2ecf20Sopenharmony_ci	sdata->reserved_chanctx = NULL;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	err = drv_switch_vif_chanctx(local, vif_chsw, 1,
10658c2ecf20Sopenharmony_ci				     CHANCTX_SWMODE_REASSIGN_VIF);
10668c2ecf20Sopenharmony_ci	if (err) {
10678c2ecf20Sopenharmony_ci		if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
10688c2ecf20Sopenharmony_ci			ieee80211_free_chanctx(local, new_ctx);
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci		goto out;
10718c2ecf20Sopenharmony_ci	}
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs);
10748c2ecf20Sopenharmony_ci	rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf);
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	if (sdata->vif.type == NL80211_IFTYPE_AP)
10778c2ecf20Sopenharmony_ci		__ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	ieee80211_check_fast_xmit_iface(sdata);
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
10828c2ecf20Sopenharmony_ci		ieee80211_free_chanctx(local, old_ctx);
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
10858c2ecf20Sopenharmony_ci		changed = BSS_CHANGED_BANDWIDTH;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	ieee80211_recalc_smps_chanctx(local, new_ctx);
10908c2ecf20Sopenharmony_ci	ieee80211_recalc_radar_chanctx(local, new_ctx);
10918c2ecf20Sopenharmony_ci	ieee80211_recalc_chanctx_min_def(local, new_ctx);
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	if (changed)
10948c2ecf20Sopenharmony_ci		ieee80211_bss_info_change_notify(sdata, changed);
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ciout:
10978c2ecf20Sopenharmony_ci	ieee80211_vif_chanctx_reservation_complete(sdata);
10988c2ecf20Sopenharmony_ci	return err;
10998c2ecf20Sopenharmony_ci}
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_cistatic int
11028c2ecf20Sopenharmony_ciieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata)
11038c2ecf20Sopenharmony_ci{
11048c2ecf20Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
11058c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *old_ctx, *new_ctx;
11068c2ecf20Sopenharmony_ci	const struct cfg80211_chan_def *chandef;
11078c2ecf20Sopenharmony_ci	int err;
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	old_ctx = ieee80211_vif_get_chanctx(sdata);
11108c2ecf20Sopenharmony_ci	new_ctx = sdata->reserved_chanctx;
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	if (WARN_ON(!sdata->reserved_ready))
11138c2ecf20Sopenharmony_ci		return -EINVAL;
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	if (WARN_ON(old_ctx))
11168c2ecf20Sopenharmony_ci		return -EINVAL;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	if (WARN_ON(!new_ctx))
11198c2ecf20Sopenharmony_ci		return -EINVAL;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	if (WARN_ON(new_ctx->replace_state ==
11228c2ecf20Sopenharmony_ci		    IEEE80211_CHANCTX_REPLACES_OTHER))
11238c2ecf20Sopenharmony_ci		return -EINVAL;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
11268c2ecf20Sopenharmony_ci				&sdata->reserved_chandef);
11278c2ecf20Sopenharmony_ci	if (WARN_ON(!chandef))
11288c2ecf20Sopenharmony_ci		return -EINVAL;
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	ieee80211_change_chanctx(local, new_ctx, chandef);
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	list_del(&sdata->reserved_chanctx_list);
11338c2ecf20Sopenharmony_ci	sdata->reserved_chanctx = NULL;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	err = ieee80211_assign_vif_chanctx(sdata, new_ctx);
11368c2ecf20Sopenharmony_ci	if (err) {
11378c2ecf20Sopenharmony_ci		if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
11388c2ecf20Sopenharmony_ci			ieee80211_free_chanctx(local, new_ctx);
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci		goto out;
11418c2ecf20Sopenharmony_ci	}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ciout:
11448c2ecf20Sopenharmony_ci	ieee80211_vif_chanctx_reservation_complete(sdata);
11458c2ecf20Sopenharmony_ci	return err;
11468c2ecf20Sopenharmony_ci}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_cistatic bool
11498c2ecf20Sopenharmony_ciieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata)
11508c2ecf20Sopenharmony_ci{
11518c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *old_ctx, *new_ctx;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	lockdep_assert_held(&sdata->local->chanctx_mtx);
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	new_ctx = sdata->reserved_chanctx;
11568c2ecf20Sopenharmony_ci	old_ctx = ieee80211_vif_get_chanctx(sdata);
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	if (!old_ctx)
11598c2ecf20Sopenharmony_ci		return false;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	if (WARN_ON(!new_ctx))
11628c2ecf20Sopenharmony_ci		return false;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
11658c2ecf20Sopenharmony_ci		return false;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
11688c2ecf20Sopenharmony_ci		return false;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	return true;
11718c2ecf20Sopenharmony_ci}
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_cistatic int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local,
11748c2ecf20Sopenharmony_ci					struct ieee80211_chanctx *new_ctx)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	const struct cfg80211_chan_def *chandef;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->mtx);
11798c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL);
11828c2ecf20Sopenharmony_ci	if (WARN_ON(!chandef))
11838c2ecf20Sopenharmony_ci		return -EINVAL;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled;
11868c2ecf20Sopenharmony_ci	local->_oper_chandef = *chandef;
11878c2ecf20Sopenharmony_ci	ieee80211_hw_config(local, 0);
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	return 0;
11908c2ecf20Sopenharmony_ci}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_cistatic int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
11938c2ecf20Sopenharmony_ci				      int n_vifs)
11948c2ecf20Sopenharmony_ci{
11958c2ecf20Sopenharmony_ci	struct ieee80211_vif_chanctx_switch *vif_chsw;
11968c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
11978c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *ctx, *old_ctx;
11988c2ecf20Sopenharmony_ci	int i, err;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->mtx);
12018c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	vif_chsw = kcalloc(n_vifs, sizeof(vif_chsw[0]), GFP_KERNEL);
12048c2ecf20Sopenharmony_ci	if (!vif_chsw)
12058c2ecf20Sopenharmony_ci		return -ENOMEM;
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	i = 0;
12088c2ecf20Sopenharmony_ci	list_for_each_entry(ctx, &local->chanctx_list, list) {
12098c2ecf20Sopenharmony_ci		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
12108c2ecf20Sopenharmony_ci			continue;
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci		if (WARN_ON(!ctx->replace_ctx)) {
12138c2ecf20Sopenharmony_ci			err = -EINVAL;
12148c2ecf20Sopenharmony_ci			goto out;
12158c2ecf20Sopenharmony_ci		}
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci		list_for_each_entry(sdata, &ctx->reserved_vifs,
12188c2ecf20Sopenharmony_ci				    reserved_chanctx_list) {
12198c2ecf20Sopenharmony_ci			if (!ieee80211_vif_has_in_place_reservation(
12208c2ecf20Sopenharmony_ci					sdata))
12218c2ecf20Sopenharmony_ci				continue;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci			old_ctx = ieee80211_vif_get_chanctx(sdata);
12248c2ecf20Sopenharmony_ci			vif_chsw[i].vif = &sdata->vif;
12258c2ecf20Sopenharmony_ci			vif_chsw[i].old_ctx = &old_ctx->conf;
12268c2ecf20Sopenharmony_ci			vif_chsw[i].new_ctx = &ctx->conf;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci			i++;
12298c2ecf20Sopenharmony_ci		}
12308c2ecf20Sopenharmony_ci	}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs,
12338c2ecf20Sopenharmony_ci				     CHANCTX_SWMODE_SWAP_CONTEXTS);
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ciout:
12368c2ecf20Sopenharmony_ci	kfree(vif_chsw);
12378c2ecf20Sopenharmony_ci	return err;
12388c2ecf20Sopenharmony_ci}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_cistatic int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local)
12418c2ecf20Sopenharmony_ci{
12428c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *ctx;
12438c2ecf20Sopenharmony_ci	int err;
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->mtx);
12468c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	list_for_each_entry(ctx, &local->chanctx_list, list) {
12498c2ecf20Sopenharmony_ci		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
12508c2ecf20Sopenharmony_ci			continue;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci		if (!list_empty(&ctx->replace_ctx->assigned_vifs))
12538c2ecf20Sopenharmony_ci			continue;
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci		ieee80211_del_chanctx(local, ctx->replace_ctx);
12568c2ecf20Sopenharmony_ci		err = ieee80211_add_chanctx(local, ctx);
12578c2ecf20Sopenharmony_ci		if (err)
12588c2ecf20Sopenharmony_ci			goto err;
12598c2ecf20Sopenharmony_ci	}
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	return 0;
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_cierr:
12648c2ecf20Sopenharmony_ci	WARN_ON(ieee80211_add_chanctx(local, ctx));
12658c2ecf20Sopenharmony_ci	list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) {
12668c2ecf20Sopenharmony_ci		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
12678c2ecf20Sopenharmony_ci			continue;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci		if (!list_empty(&ctx->replace_ctx->assigned_vifs))
12708c2ecf20Sopenharmony_ci			continue;
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci		ieee80211_del_chanctx(local, ctx);
12738c2ecf20Sopenharmony_ci		WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx));
12748c2ecf20Sopenharmony_ci	}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	return err;
12778c2ecf20Sopenharmony_ci}
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_cistatic int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
12808c2ecf20Sopenharmony_ci{
12818c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *sdata, *sdata_tmp;
12828c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx;
12838c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *new_ctx = NULL;
12848c2ecf20Sopenharmony_ci	int err, n_assigned, n_reserved, n_ready;
12858c2ecf20Sopenharmony_ci	int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->mtx);
12888c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	/*
12918c2ecf20Sopenharmony_ci	 * If there are 2 independent pairs of channel contexts performing
12928c2ecf20Sopenharmony_ci	 * cross-switch of their vifs this code will still wait until both are
12938c2ecf20Sopenharmony_ci	 * ready even though it could be possible to switch one before the
12948c2ecf20Sopenharmony_ci	 * other is ready.
12958c2ecf20Sopenharmony_ci	 *
12968c2ecf20Sopenharmony_ci	 * For practical reasons and code simplicity just do a single huge
12978c2ecf20Sopenharmony_ci	 * switch.
12988c2ecf20Sopenharmony_ci	 */
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	/*
13018c2ecf20Sopenharmony_ci	 * Verify if the reservation is still feasible.
13028c2ecf20Sopenharmony_ci	 *  - if it's not then disconnect
13038c2ecf20Sopenharmony_ci	 *  - if it is but not all vifs necessary are ready then defer
13048c2ecf20Sopenharmony_ci	 */
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	list_for_each_entry(ctx, &local->chanctx_list, list) {
13078c2ecf20Sopenharmony_ci		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
13088c2ecf20Sopenharmony_ci			continue;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci		if (WARN_ON(!ctx->replace_ctx)) {
13118c2ecf20Sopenharmony_ci			err = -EINVAL;
13128c2ecf20Sopenharmony_ci			goto err;
13138c2ecf20Sopenharmony_ci		}
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci		if (!local->use_chanctx)
13168c2ecf20Sopenharmony_ci			new_ctx = ctx;
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci		n_ctx++;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci		n_assigned = 0;
13218c2ecf20Sopenharmony_ci		n_reserved = 0;
13228c2ecf20Sopenharmony_ci		n_ready = 0;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci		list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs,
13258c2ecf20Sopenharmony_ci				    assigned_chanctx_list) {
13268c2ecf20Sopenharmony_ci			n_assigned++;
13278c2ecf20Sopenharmony_ci			if (sdata->reserved_chanctx) {
13288c2ecf20Sopenharmony_ci				n_reserved++;
13298c2ecf20Sopenharmony_ci				if (sdata->reserved_ready)
13308c2ecf20Sopenharmony_ci					n_ready++;
13318c2ecf20Sopenharmony_ci			}
13328c2ecf20Sopenharmony_ci		}
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci		if (n_assigned != n_reserved) {
13358c2ecf20Sopenharmony_ci			if (n_ready == n_reserved) {
13368c2ecf20Sopenharmony_ci				wiphy_info(local->hw.wiphy,
13378c2ecf20Sopenharmony_ci					   "channel context reservation cannot be finalized because some interfaces aren't switching\n");
13388c2ecf20Sopenharmony_ci				err = -EBUSY;
13398c2ecf20Sopenharmony_ci				goto err;
13408c2ecf20Sopenharmony_ci			}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci			return -EAGAIN;
13438c2ecf20Sopenharmony_ci		}
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci		ctx->conf.radar_enabled = false;
13468c2ecf20Sopenharmony_ci		list_for_each_entry(sdata, &ctx->reserved_vifs,
13478c2ecf20Sopenharmony_ci				    reserved_chanctx_list) {
13488c2ecf20Sopenharmony_ci			if (ieee80211_vif_has_in_place_reservation(sdata) &&
13498c2ecf20Sopenharmony_ci			    !sdata->reserved_ready)
13508c2ecf20Sopenharmony_ci				return -EAGAIN;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci			old_ctx = ieee80211_vif_get_chanctx(sdata);
13538c2ecf20Sopenharmony_ci			if (old_ctx) {
13548c2ecf20Sopenharmony_ci				if (old_ctx->replace_state ==
13558c2ecf20Sopenharmony_ci				    IEEE80211_CHANCTX_WILL_BE_REPLACED)
13568c2ecf20Sopenharmony_ci					n_vifs_switch++;
13578c2ecf20Sopenharmony_ci				else
13588c2ecf20Sopenharmony_ci					n_vifs_assign++;
13598c2ecf20Sopenharmony_ci			} else {
13608c2ecf20Sopenharmony_ci				n_vifs_ctxless++;
13618c2ecf20Sopenharmony_ci			}
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci			if (sdata->reserved_radar_required)
13648c2ecf20Sopenharmony_ci				ctx->conf.radar_enabled = true;
13658c2ecf20Sopenharmony_ci		}
13668c2ecf20Sopenharmony_ci	}
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	if (WARN_ON(n_ctx == 0) ||
13698c2ecf20Sopenharmony_ci	    WARN_ON(n_vifs_switch == 0 &&
13708c2ecf20Sopenharmony_ci		    n_vifs_assign == 0 &&
13718c2ecf20Sopenharmony_ci		    n_vifs_ctxless == 0) ||
13728c2ecf20Sopenharmony_ci	    WARN_ON(n_ctx > 1 && !local->use_chanctx) ||
13738c2ecf20Sopenharmony_ci	    WARN_ON(!new_ctx && !local->use_chanctx)) {
13748c2ecf20Sopenharmony_ci		err = -EINVAL;
13758c2ecf20Sopenharmony_ci		goto err;
13768c2ecf20Sopenharmony_ci	}
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	/*
13798c2ecf20Sopenharmony_ci	 * All necessary vifs are ready. Perform the switch now depending on
13808c2ecf20Sopenharmony_ci	 * reservations and driver capabilities.
13818c2ecf20Sopenharmony_ci	 */
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	if (local->use_chanctx) {
13848c2ecf20Sopenharmony_ci		if (n_vifs_switch > 0) {
13858c2ecf20Sopenharmony_ci			err = ieee80211_chsw_switch_vifs(local, n_vifs_switch);
13868c2ecf20Sopenharmony_ci			if (err)
13878c2ecf20Sopenharmony_ci				goto err;
13888c2ecf20Sopenharmony_ci		}
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci		if (n_vifs_assign > 0 || n_vifs_ctxless > 0) {
13918c2ecf20Sopenharmony_ci			err = ieee80211_chsw_switch_ctxs(local);
13928c2ecf20Sopenharmony_ci			if (err)
13938c2ecf20Sopenharmony_ci				goto err;
13948c2ecf20Sopenharmony_ci		}
13958c2ecf20Sopenharmony_ci	} else {
13968c2ecf20Sopenharmony_ci		err = ieee80211_chsw_switch_hwconf(local, new_ctx);
13978c2ecf20Sopenharmony_ci		if (err)
13988c2ecf20Sopenharmony_ci			goto err;
13998c2ecf20Sopenharmony_ci	}
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	/*
14028c2ecf20Sopenharmony_ci	 * Update all structures, values and pointers to point to new channel
14038c2ecf20Sopenharmony_ci	 * context(s).
14048c2ecf20Sopenharmony_ci	 */
14058c2ecf20Sopenharmony_ci	list_for_each_entry(ctx, &local->chanctx_list, list) {
14068c2ecf20Sopenharmony_ci		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
14078c2ecf20Sopenharmony_ci			continue;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci		if (WARN_ON(!ctx->replace_ctx)) {
14108c2ecf20Sopenharmony_ci			err = -EINVAL;
14118c2ecf20Sopenharmony_ci			goto err;
14128c2ecf20Sopenharmony_ci		}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci		list_for_each_entry(sdata, &ctx->reserved_vifs,
14158c2ecf20Sopenharmony_ci				    reserved_chanctx_list) {
14168c2ecf20Sopenharmony_ci			u32 changed = 0;
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci			if (!ieee80211_vif_has_in_place_reservation(sdata))
14198c2ecf20Sopenharmony_ci				continue;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci			rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci			if (sdata->vif.type == NL80211_IFTYPE_AP)
14248c2ecf20Sopenharmony_ci				__ieee80211_vif_copy_chanctx_to_vlans(sdata,
14258c2ecf20Sopenharmony_ci								      false);
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci			ieee80211_check_fast_xmit_iface(sdata);
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci			sdata->radar_required = sdata->reserved_radar_required;
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci			if (sdata->vif.bss_conf.chandef.width !=
14328c2ecf20Sopenharmony_ci			    sdata->reserved_chandef.width)
14338c2ecf20Sopenharmony_ci				changed = BSS_CHANGED_BANDWIDTH;
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci			ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
14368c2ecf20Sopenharmony_ci			if (changed)
14378c2ecf20Sopenharmony_ci				ieee80211_bss_info_change_notify(sdata,
14388c2ecf20Sopenharmony_ci								 changed);
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci			ieee80211_recalc_txpower(sdata, false);
14418c2ecf20Sopenharmony_ci		}
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci		ieee80211_recalc_chanctx_chantype(local, ctx);
14448c2ecf20Sopenharmony_ci		ieee80211_recalc_smps_chanctx(local, ctx);
14458c2ecf20Sopenharmony_ci		ieee80211_recalc_radar_chanctx(local, ctx);
14468c2ecf20Sopenharmony_ci		ieee80211_recalc_chanctx_min_def(local, ctx);
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci		list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
14498c2ecf20Sopenharmony_ci					 reserved_chanctx_list) {
14508c2ecf20Sopenharmony_ci			if (ieee80211_vif_get_chanctx(sdata) != ctx)
14518c2ecf20Sopenharmony_ci				continue;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci			list_del(&sdata->reserved_chanctx_list);
14548c2ecf20Sopenharmony_ci			list_move(&sdata->assigned_chanctx_list,
14558c2ecf20Sopenharmony_ci				  &ctx->assigned_vifs);
14568c2ecf20Sopenharmony_ci			sdata->reserved_chanctx = NULL;
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci			ieee80211_vif_chanctx_reservation_complete(sdata);
14598c2ecf20Sopenharmony_ci		}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci		/*
14628c2ecf20Sopenharmony_ci		 * This context might have been a dependency for an already
14638c2ecf20Sopenharmony_ci		 * ready re-assign reservation interface that was deferred. Do
14648c2ecf20Sopenharmony_ci		 * not propagate error to the caller though. The in-place
14658c2ecf20Sopenharmony_ci		 * reservation for originally requested interface has already
14668c2ecf20Sopenharmony_ci		 * succeeded at this point.
14678c2ecf20Sopenharmony_ci		 */
14688c2ecf20Sopenharmony_ci		list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
14698c2ecf20Sopenharmony_ci					 reserved_chanctx_list) {
14708c2ecf20Sopenharmony_ci			if (WARN_ON(ieee80211_vif_has_in_place_reservation(
14718c2ecf20Sopenharmony_ci					sdata)))
14728c2ecf20Sopenharmony_ci				continue;
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci			if (WARN_ON(sdata->reserved_chanctx != ctx))
14758c2ecf20Sopenharmony_ci				continue;
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci			if (!sdata->reserved_ready)
14788c2ecf20Sopenharmony_ci				continue;
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci			if (ieee80211_vif_get_chanctx(sdata))
14818c2ecf20Sopenharmony_ci				err = ieee80211_vif_use_reserved_reassign(
14828c2ecf20Sopenharmony_ci						sdata);
14838c2ecf20Sopenharmony_ci			else
14848c2ecf20Sopenharmony_ci				err = ieee80211_vif_use_reserved_assign(sdata);
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci			if (err) {
14878c2ecf20Sopenharmony_ci				sdata_info(sdata,
14888c2ecf20Sopenharmony_ci					   "failed to finalize (re-)assign reservation (err=%d)\n",
14898c2ecf20Sopenharmony_ci					   err);
14908c2ecf20Sopenharmony_ci				ieee80211_vif_unreserve_chanctx(sdata);
14918c2ecf20Sopenharmony_ci				cfg80211_stop_iface(local->hw.wiphy,
14928c2ecf20Sopenharmony_ci						    &sdata->wdev,
14938c2ecf20Sopenharmony_ci						    GFP_KERNEL);
14948c2ecf20Sopenharmony_ci			}
14958c2ecf20Sopenharmony_ci		}
14968c2ecf20Sopenharmony_ci	}
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	/*
14998c2ecf20Sopenharmony_ci	 * Finally free old contexts
15008c2ecf20Sopenharmony_ci	 */
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) {
15038c2ecf20Sopenharmony_ci		if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
15048c2ecf20Sopenharmony_ci			continue;
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci		ctx->replace_ctx->replace_ctx = NULL;
15078c2ecf20Sopenharmony_ci		ctx->replace_ctx->replace_state =
15088c2ecf20Sopenharmony_ci				IEEE80211_CHANCTX_REPLACE_NONE;
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci		list_del_rcu(&ctx->list);
15118c2ecf20Sopenharmony_ci		kfree_rcu(ctx, rcu_head);
15128c2ecf20Sopenharmony_ci	}
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	return 0;
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_cierr:
15178c2ecf20Sopenharmony_ci	list_for_each_entry(ctx, &local->chanctx_list, list) {
15188c2ecf20Sopenharmony_ci		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
15198c2ecf20Sopenharmony_ci			continue;
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci		list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
15228c2ecf20Sopenharmony_ci					 reserved_chanctx_list) {
15238c2ecf20Sopenharmony_ci			ieee80211_vif_unreserve_chanctx(sdata);
15248c2ecf20Sopenharmony_ci			ieee80211_vif_chanctx_reservation_complete(sdata);
15258c2ecf20Sopenharmony_ci		}
15268c2ecf20Sopenharmony_ci	}
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	return err;
15298c2ecf20Sopenharmony_ci}
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_cistatic void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
15328c2ecf20Sopenharmony_ci{
15338c2ecf20Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
15348c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *conf;
15358c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *ctx;
15368c2ecf20Sopenharmony_ci	bool use_reserved_switch = false;
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
15418c2ecf20Sopenharmony_ci					 lockdep_is_held(&local->chanctx_mtx));
15428c2ecf20Sopenharmony_ci	if (!conf)
15438c2ecf20Sopenharmony_ci		return;
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	ctx = container_of(conf, struct ieee80211_chanctx, conf);
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci	if (sdata->reserved_chanctx) {
15488c2ecf20Sopenharmony_ci		if (sdata->reserved_chanctx->replace_state ==
15498c2ecf20Sopenharmony_ci		    IEEE80211_CHANCTX_REPLACES_OTHER &&
15508c2ecf20Sopenharmony_ci		    ieee80211_chanctx_num_reserved(local,
15518c2ecf20Sopenharmony_ci						   sdata->reserved_chanctx) > 1)
15528c2ecf20Sopenharmony_ci			use_reserved_switch = true;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci		ieee80211_vif_unreserve_chanctx(sdata);
15558c2ecf20Sopenharmony_ci	}
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	ieee80211_assign_vif_chanctx(sdata, NULL);
15588c2ecf20Sopenharmony_ci	if (ieee80211_chanctx_refcount(local, ctx) == 0)
15598c2ecf20Sopenharmony_ci		ieee80211_free_chanctx(local, ctx);
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	sdata->radar_required = false;
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	/* Unreserving may ready an in-place reservation. */
15648c2ecf20Sopenharmony_ci	if (use_reserved_switch)
15658c2ecf20Sopenharmony_ci		ieee80211_vif_use_reserved_switch(local);
15668c2ecf20Sopenharmony_ci}
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ciint ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
15698c2ecf20Sopenharmony_ci			      const struct cfg80211_chan_def *chandef,
15708c2ecf20Sopenharmony_ci			      enum ieee80211_chanctx_mode mode)
15718c2ecf20Sopenharmony_ci{
15728c2ecf20Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
15738c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *ctx;
15748c2ecf20Sopenharmony_ci	u8 radar_detect_width = 0;
15758c2ecf20Sopenharmony_ci	int ret;
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->mtx);
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	mutex_lock(&local->chanctx_mtx);
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
15848c2ecf20Sopenharmony_ci					    chandef,
15858c2ecf20Sopenharmony_ci					    sdata->wdev.iftype);
15868c2ecf20Sopenharmony_ci	if (ret < 0)
15878c2ecf20Sopenharmony_ci		goto out;
15888c2ecf20Sopenharmony_ci	if (ret > 0)
15898c2ecf20Sopenharmony_ci		radar_detect_width = BIT(chandef->width);
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	sdata->radar_required = ret;
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	ret = ieee80211_check_combinations(sdata, chandef, mode,
15948c2ecf20Sopenharmony_ci					   radar_detect_width);
15958c2ecf20Sopenharmony_ci	if (ret < 0)
15968c2ecf20Sopenharmony_ci		goto out;
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	__ieee80211_vif_release_channel(sdata);
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	ctx = ieee80211_find_chanctx(local, chandef, mode);
16018c2ecf20Sopenharmony_ci	if (!ctx)
16028c2ecf20Sopenharmony_ci		ctx = ieee80211_new_chanctx(local, chandef, mode);
16038c2ecf20Sopenharmony_ci	if (IS_ERR(ctx)) {
16048c2ecf20Sopenharmony_ci		ret = PTR_ERR(ctx);
16058c2ecf20Sopenharmony_ci		goto out;
16068c2ecf20Sopenharmony_ci	}
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci	ieee80211_vif_update_chandef(sdata, chandef);
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci	ret = ieee80211_assign_vif_chanctx(sdata, ctx);
16118c2ecf20Sopenharmony_ci	if (ret) {
16128c2ecf20Sopenharmony_ci		/* if assign fails refcount stays the same */
16138c2ecf20Sopenharmony_ci		if (ieee80211_chanctx_refcount(local, ctx) == 0)
16148c2ecf20Sopenharmony_ci			ieee80211_free_chanctx(local, ctx);
16158c2ecf20Sopenharmony_ci		goto out;
16168c2ecf20Sopenharmony_ci	}
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	ieee80211_recalc_smps_chanctx(local, ctx);
16198c2ecf20Sopenharmony_ci	ieee80211_recalc_radar_chanctx(local, ctx);
16208c2ecf20Sopenharmony_ci out:
16218c2ecf20Sopenharmony_ci	if (ret)
16228c2ecf20Sopenharmony_ci		sdata->radar_required = false;
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci	mutex_unlock(&local->chanctx_mtx);
16258c2ecf20Sopenharmony_ci	return ret;
16268c2ecf20Sopenharmony_ci}
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ciint ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata)
16298c2ecf20Sopenharmony_ci{
16308c2ecf20Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
16318c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *new_ctx;
16328c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *old_ctx;
16338c2ecf20Sopenharmony_ci	int err;
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->mtx);
16368c2ecf20Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	new_ctx = sdata->reserved_chanctx;
16398c2ecf20Sopenharmony_ci	old_ctx = ieee80211_vif_get_chanctx(sdata);
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	if (WARN_ON(!new_ctx))
16428c2ecf20Sopenharmony_ci		return -EINVAL;
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	if (WARN_ON(new_ctx->replace_state ==
16458c2ecf20Sopenharmony_ci		    IEEE80211_CHANCTX_WILL_BE_REPLACED))
16468c2ecf20Sopenharmony_ci		return -EINVAL;
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	if (WARN_ON(sdata->reserved_ready))
16498c2ecf20Sopenharmony_ci		return -EINVAL;
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	sdata->reserved_ready = true;
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci	if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) {
16548c2ecf20Sopenharmony_ci		if (old_ctx)
16558c2ecf20Sopenharmony_ci			return ieee80211_vif_use_reserved_reassign(sdata);
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci		return ieee80211_vif_use_reserved_assign(sdata);
16588c2ecf20Sopenharmony_ci	}
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	/*
16618c2ecf20Sopenharmony_ci	 * In-place reservation may need to be finalized now either if:
16628c2ecf20Sopenharmony_ci	 *  a) sdata is taking part in the swapping itself and is the last one
16638c2ecf20Sopenharmony_ci	 *  b) sdata has switched with a re-assign reservation to an existing
16648c2ecf20Sopenharmony_ci	 *     context readying in-place switching of old_ctx
16658c2ecf20Sopenharmony_ci	 *
16668c2ecf20Sopenharmony_ci	 * In case of (b) do not propagate the error up because the requested
16678c2ecf20Sopenharmony_ci	 * sdata already switched successfully. Just spill an extra warning.
16688c2ecf20Sopenharmony_ci	 * The ieee80211_vif_use_reserved_switch() already stops all necessary
16698c2ecf20Sopenharmony_ci	 * interfaces upon failure.
16708c2ecf20Sopenharmony_ci	 */
16718c2ecf20Sopenharmony_ci	if ((old_ctx &&
16728c2ecf20Sopenharmony_ci	     old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
16738c2ecf20Sopenharmony_ci	    new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
16748c2ecf20Sopenharmony_ci		err = ieee80211_vif_use_reserved_switch(local);
16758c2ecf20Sopenharmony_ci		if (err && err != -EAGAIN) {
16768c2ecf20Sopenharmony_ci			if (new_ctx->replace_state ==
16778c2ecf20Sopenharmony_ci			    IEEE80211_CHANCTX_REPLACES_OTHER)
16788c2ecf20Sopenharmony_ci				return err;
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci			wiphy_info(local->hw.wiphy,
16818c2ecf20Sopenharmony_ci				   "depending in-place reservation failed (err=%d)\n",
16828c2ecf20Sopenharmony_ci				   err);
16838c2ecf20Sopenharmony_ci		}
16848c2ecf20Sopenharmony_ci	}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	return 0;
16878c2ecf20Sopenharmony_ci}
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ciint ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
16908c2ecf20Sopenharmony_ci				   const struct cfg80211_chan_def *chandef,
16918c2ecf20Sopenharmony_ci				   u32 *changed)
16928c2ecf20Sopenharmony_ci{
16938c2ecf20Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
16948c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *conf;
16958c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *ctx;
16968c2ecf20Sopenharmony_ci	const struct cfg80211_chan_def *compat;
16978c2ecf20Sopenharmony_ci	int ret;
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
17008c2ecf20Sopenharmony_ci				     IEEE80211_CHAN_DISABLED))
17018c2ecf20Sopenharmony_ci		return -EINVAL;
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	mutex_lock(&local->chanctx_mtx);
17048c2ecf20Sopenharmony_ci	if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) {
17058c2ecf20Sopenharmony_ci		ret = 0;
17068c2ecf20Sopenharmony_ci		goto out;
17078c2ecf20Sopenharmony_ci	}
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
17108c2ecf20Sopenharmony_ci	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) {
17118c2ecf20Sopenharmony_ci		ret = -EINVAL;
17128c2ecf20Sopenharmony_ci		goto out;
17138c2ecf20Sopenharmony_ci	}
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
17168c2ecf20Sopenharmony_ci					 lockdep_is_held(&local->chanctx_mtx));
17178c2ecf20Sopenharmony_ci	if (!conf) {
17188c2ecf20Sopenharmony_ci		ret = -EINVAL;
17198c2ecf20Sopenharmony_ci		goto out;
17208c2ecf20Sopenharmony_ci	}
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	ctx = container_of(conf, struct ieee80211_chanctx, conf);
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	compat = cfg80211_chandef_compatible(&conf->def, chandef);
17258c2ecf20Sopenharmony_ci	if (!compat) {
17268c2ecf20Sopenharmony_ci		ret = -EINVAL;
17278c2ecf20Sopenharmony_ci		goto out;
17288c2ecf20Sopenharmony_ci	}
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	switch (ctx->replace_state) {
17318c2ecf20Sopenharmony_ci	case IEEE80211_CHANCTX_REPLACE_NONE:
17328c2ecf20Sopenharmony_ci		if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) {
17338c2ecf20Sopenharmony_ci			ret = -EBUSY;
17348c2ecf20Sopenharmony_ci			goto out;
17358c2ecf20Sopenharmony_ci		}
17368c2ecf20Sopenharmony_ci		break;
17378c2ecf20Sopenharmony_ci	case IEEE80211_CHANCTX_WILL_BE_REPLACED:
17388c2ecf20Sopenharmony_ci		/* TODO: Perhaps the bandwidth change could be treated as a
17398c2ecf20Sopenharmony_ci		 * reservation itself? */
17408c2ecf20Sopenharmony_ci		ret = -EBUSY;
17418c2ecf20Sopenharmony_ci		goto out;
17428c2ecf20Sopenharmony_ci	case IEEE80211_CHANCTX_REPLACES_OTHER:
17438c2ecf20Sopenharmony_ci		/* channel context that is going to replace another channel
17448c2ecf20Sopenharmony_ci		 * context doesn't really exist and shouldn't be assigned
17458c2ecf20Sopenharmony_ci		 * anywhere yet */
17468c2ecf20Sopenharmony_ci		WARN_ON(1);
17478c2ecf20Sopenharmony_ci		break;
17488c2ecf20Sopenharmony_ci	}
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci	ieee80211_vif_update_chandef(sdata, chandef);
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	ieee80211_recalc_chanctx_chantype(local, ctx);
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	*changed |= BSS_CHANGED_BANDWIDTH;
17558c2ecf20Sopenharmony_ci	ret = 0;
17568c2ecf20Sopenharmony_ci out:
17578c2ecf20Sopenharmony_ci	mutex_unlock(&local->chanctx_mtx);
17588c2ecf20Sopenharmony_ci	return ret;
17598c2ecf20Sopenharmony_ci}
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_civoid ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
17628c2ecf20Sopenharmony_ci{
17638c2ecf20Sopenharmony_ci	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci	lockdep_assert_held(&sdata->local->mtx);
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	mutex_lock(&sdata->local->chanctx_mtx);
17688c2ecf20Sopenharmony_ci	__ieee80211_vif_release_channel(sdata);
17698c2ecf20Sopenharmony_ci	mutex_unlock(&sdata->local->chanctx_mtx);
17708c2ecf20Sopenharmony_ci}
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_civoid ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
17738c2ecf20Sopenharmony_ci{
17748c2ecf20Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
17758c2ecf20Sopenharmony_ci	struct ieee80211_sub_if_data *ap;
17768c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *conf;
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
17798c2ecf20Sopenharmony_ci		return;
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	mutex_lock(&local->chanctx_mtx);
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci	conf = rcu_dereference_protected(ap->vif.chanctx_conf,
17868c2ecf20Sopenharmony_ci					 lockdep_is_held(&local->chanctx_mtx));
17878c2ecf20Sopenharmony_ci	rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
17888c2ecf20Sopenharmony_ci	mutex_unlock(&local->chanctx_mtx);
17898c2ecf20Sopenharmony_ci}
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_civoid ieee80211_iter_chan_contexts_atomic(
17928c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw,
17938c2ecf20Sopenharmony_ci	void (*iter)(struct ieee80211_hw *hw,
17948c2ecf20Sopenharmony_ci		     struct ieee80211_chanctx_conf *chanctx_conf,
17958c2ecf20Sopenharmony_ci		     void *data),
17968c2ecf20Sopenharmony_ci	void *iter_data)
17978c2ecf20Sopenharmony_ci{
17988c2ecf20Sopenharmony_ci	struct ieee80211_local *local = hw_to_local(hw);
17998c2ecf20Sopenharmony_ci	struct ieee80211_chanctx *ctx;
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	rcu_read_lock();
18028c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
18038c2ecf20Sopenharmony_ci		if (ctx->driver_present)
18048c2ecf20Sopenharmony_ci			iter(hw, &ctx->conf, iter_data);
18058c2ecf20Sopenharmony_ci	rcu_read_unlock();
18068c2ecf20Sopenharmony_ci}
18078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);
1808