xref: /openbmc/linux/net/wireless/chan.c (revision b25413fe)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
259bbb6f7SJohannes Berg /*
359bbb6f7SJohannes Berg  * This file contains helper code to handle channel
459bbb6f7SJohannes Berg  * settings and keeping track of what is possible at
559bbb6f7SJohannes Berg  * any point in time.
659bbb6f7SJohannes Berg  *
759bbb6f7SJohannes Berg  * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
82740f0cfSJohannes Berg  * Copyright 2013-2014  Intel Mobile Communications GmbH
934c9a0e7SJohannes Berg  * Copyright 2018-2022	Intel Corporation
1059bbb6f7SJohannes Berg  */
1159bbb6f7SJohannes Berg 
1254858ee5SAlexander Simon #include <linux/export.h>
1335799944SShay Bar #include <linux/bitfield.h>
1459bbb6f7SJohannes Berg #include <net/cfg80211.h>
1559bbb6f7SJohannes Berg #include "core.h"
16e35e4d28SHila Gonen #include "rdev-ops.h"
1759bbb6f7SJohannes Berg 
cfg80211_valid_60g_freq(u32 freq)182a38075cSAlexei Avshalom Lazar static bool cfg80211_valid_60g_freq(u32 freq)
192a38075cSAlexei Avshalom Lazar {
202a38075cSAlexei Avshalom Lazar 	return freq >= 58320 && freq <= 70200;
212a38075cSAlexei Avshalom Lazar }
222a38075cSAlexei Avshalom Lazar 
cfg80211_chandef_create(struct cfg80211_chan_def * chandef,struct ieee80211_channel * chan,enum nl80211_channel_type chan_type)233d9d1d66SJohannes Berg void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
243d9d1d66SJohannes Berg 			     struct ieee80211_channel *chan,
253d9d1d66SJohannes Berg 			     enum nl80211_channel_type chan_type)
263d9d1d66SJohannes Berg {
273d9d1d66SJohannes Berg 	if (WARN_ON(!chan))
283d9d1d66SJohannes Berg 		return;
293d9d1d66SJohannes Berg 
303d9d1d66SJohannes Berg 	chandef->chan = chan;
31934f4c7dSThomas Pedersen 	chandef->freq1_offset = chan->freq_offset;
323d9d1d66SJohannes Berg 	chandef->center_freq2 = 0;
332a38075cSAlexei Avshalom Lazar 	chandef->edmg.bw_config = 0;
342a38075cSAlexei Avshalom Lazar 	chandef->edmg.channels = 0;
353d9d1d66SJohannes Berg 
363d9d1d66SJohannes Berg 	switch (chan_type) {
373d9d1d66SJohannes Berg 	case NL80211_CHAN_NO_HT:
383d9d1d66SJohannes Berg 		chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
393d9d1d66SJohannes Berg 		chandef->center_freq1 = chan->center_freq;
403d9d1d66SJohannes Berg 		break;
413d9d1d66SJohannes Berg 	case NL80211_CHAN_HT20:
423d9d1d66SJohannes Berg 		chandef->width = NL80211_CHAN_WIDTH_20;
433d9d1d66SJohannes Berg 		chandef->center_freq1 = chan->center_freq;
443d9d1d66SJohannes Berg 		break;
453d9d1d66SJohannes Berg 	case NL80211_CHAN_HT40PLUS:
463d9d1d66SJohannes Berg 		chandef->width = NL80211_CHAN_WIDTH_40;
473d9d1d66SJohannes Berg 		chandef->center_freq1 = chan->center_freq + 10;
483d9d1d66SJohannes Berg 		break;
493d9d1d66SJohannes Berg 	case NL80211_CHAN_HT40MINUS:
503d9d1d66SJohannes Berg 		chandef->width = NL80211_CHAN_WIDTH_40;
513d9d1d66SJohannes Berg 		chandef->center_freq1 = chan->center_freq - 10;
523d9d1d66SJohannes Berg 		break;
533d9d1d66SJohannes Berg 	default:
543d9d1d66SJohannes Berg 		WARN_ON(1);
553d9d1d66SJohannes Berg 	}
563d9d1d66SJohannes Berg }
573d9d1d66SJohannes Berg EXPORT_SYMBOL(cfg80211_chandef_create);
583d9d1d66SJohannes Berg 
cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def * chandef)592a38075cSAlexei Avshalom Lazar static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef)
602a38075cSAlexei Avshalom Lazar {
612a38075cSAlexei Avshalom Lazar 	int max_contiguous = 0;
622a38075cSAlexei Avshalom Lazar 	int num_of_enabled = 0;
632a38075cSAlexei Avshalom Lazar 	int contiguous = 0;
642a38075cSAlexei Avshalom Lazar 	int i;
652a38075cSAlexei Avshalom Lazar 
662a38075cSAlexei Avshalom Lazar 	if (!chandef->edmg.channels || !chandef->edmg.bw_config)
672a38075cSAlexei Avshalom Lazar 		return false;
682a38075cSAlexei Avshalom Lazar 
692a38075cSAlexei Avshalom Lazar 	if (!cfg80211_valid_60g_freq(chandef->chan->center_freq))
702a38075cSAlexei Avshalom Lazar 		return false;
712a38075cSAlexei Avshalom Lazar 
722a38075cSAlexei Avshalom Lazar 	for (i = 0; i < 6; i++) {
732a38075cSAlexei Avshalom Lazar 		if (chandef->edmg.channels & BIT(i)) {
742a38075cSAlexei Avshalom Lazar 			contiguous++;
752a38075cSAlexei Avshalom Lazar 			num_of_enabled++;
762a38075cSAlexei Avshalom Lazar 		} else {
772a38075cSAlexei Avshalom Lazar 			contiguous = 0;
782a38075cSAlexei Avshalom Lazar 		}
792a38075cSAlexei Avshalom Lazar 
802a38075cSAlexei Avshalom Lazar 		max_contiguous = max(contiguous, max_contiguous);
812a38075cSAlexei Avshalom Lazar 	}
822a38075cSAlexei Avshalom Lazar 	/* basic verification of edmg configuration according to
832a38075cSAlexei Avshalom Lazar 	 * IEEE P802.11ay/D4.0 section 9.4.2.251
842a38075cSAlexei Avshalom Lazar 	 */
852a38075cSAlexei Avshalom Lazar 	/* check bw_config against contiguous edmg channels */
862a38075cSAlexei Avshalom Lazar 	switch (chandef->edmg.bw_config) {
872a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_4:
882a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_8:
892a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_12:
902a38075cSAlexei Avshalom Lazar 		if (max_contiguous < 1)
912a38075cSAlexei Avshalom Lazar 			return false;
922a38075cSAlexei Avshalom Lazar 		break;
932a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_5:
942a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_9:
952a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_13:
962a38075cSAlexei Avshalom Lazar 		if (max_contiguous < 2)
972a38075cSAlexei Avshalom Lazar 			return false;
982a38075cSAlexei Avshalom Lazar 		break;
992a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_6:
1002a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_10:
1012a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_14:
1022a38075cSAlexei Avshalom Lazar 		if (max_contiguous < 3)
1032a38075cSAlexei Avshalom Lazar 			return false;
1042a38075cSAlexei Avshalom Lazar 		break;
1052a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_7:
1062a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_11:
1072a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_15:
1082a38075cSAlexei Avshalom Lazar 		if (max_contiguous < 4)
1092a38075cSAlexei Avshalom Lazar 			return false;
1102a38075cSAlexei Avshalom Lazar 		break;
1112a38075cSAlexei Avshalom Lazar 
1122a38075cSAlexei Avshalom Lazar 	default:
1132a38075cSAlexei Avshalom Lazar 		return false;
1142a38075cSAlexei Avshalom Lazar 	}
1152a38075cSAlexei Avshalom Lazar 
1162a38075cSAlexei Avshalom Lazar 	/* check bw_config against aggregated (non contiguous) edmg channels */
1172a38075cSAlexei Avshalom Lazar 	switch (chandef->edmg.bw_config) {
1182a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_4:
1192a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_5:
1202a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_6:
1212a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_7:
1222a38075cSAlexei Avshalom Lazar 		break;
1232a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_8:
1242a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_9:
1252a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_10:
1262a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_11:
1272a38075cSAlexei Avshalom Lazar 		if (num_of_enabled < 2)
1282a38075cSAlexei Avshalom Lazar 			return false;
1292a38075cSAlexei Avshalom Lazar 		break;
1302a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_12:
1312a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_13:
1322a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_14:
1332a38075cSAlexei Avshalom Lazar 	case IEEE80211_EDMG_BW_CONFIG_15:
1342a38075cSAlexei Avshalom Lazar 		if (num_of_enabled < 4 || max_contiguous < 2)
1352a38075cSAlexei Avshalom Lazar 			return false;
1362a38075cSAlexei Avshalom Lazar 		break;
1372a38075cSAlexei Avshalom Lazar 	default:
1382a38075cSAlexei Avshalom Lazar 		return false;
1392a38075cSAlexei Avshalom Lazar 	}
1402a38075cSAlexei Avshalom Lazar 
1412a38075cSAlexei Avshalom Lazar 	return true;
1422a38075cSAlexei Avshalom Lazar }
1432a38075cSAlexei Avshalom Lazar 
nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)14411b34737SThomas Pedersen static int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)
14511b34737SThomas Pedersen {
14611b34737SThomas Pedersen 	int mhz;
14711b34737SThomas Pedersen 
14811b34737SThomas Pedersen 	switch (chan_width) {
14911b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_1:
15011b34737SThomas Pedersen 		mhz = 1;
15111b34737SThomas Pedersen 		break;
15211b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_2:
15311b34737SThomas Pedersen 		mhz = 2;
15411b34737SThomas Pedersen 		break;
15511b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_4:
15611b34737SThomas Pedersen 		mhz = 4;
15711b34737SThomas Pedersen 		break;
15811b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_8:
15911b34737SThomas Pedersen 		mhz = 8;
16011b34737SThomas Pedersen 		break;
16111b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_16:
16211b34737SThomas Pedersen 		mhz = 16;
16311b34737SThomas Pedersen 		break;
16411b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_5:
16511b34737SThomas Pedersen 		mhz = 5;
16611b34737SThomas Pedersen 		break;
16711b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_10:
16811b34737SThomas Pedersen 		mhz = 10;
16911b34737SThomas Pedersen 		break;
17011b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_20:
17111b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_20_NOHT:
17211b34737SThomas Pedersen 		mhz = 20;
17311b34737SThomas Pedersen 		break;
17411b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_40:
17511b34737SThomas Pedersen 		mhz = 40;
17611b34737SThomas Pedersen 		break;
17711b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_80P80:
17811b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_80:
17911b34737SThomas Pedersen 		mhz = 80;
18011b34737SThomas Pedersen 		break;
18111b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_160:
18211b34737SThomas Pedersen 		mhz = 160;
18311b34737SThomas Pedersen 		break;
1843743bec6SJia Ding 	case NL80211_CHAN_WIDTH_320:
1853743bec6SJia Ding 		mhz = 320;
1863743bec6SJia Ding 		break;
18711b34737SThomas Pedersen 	default:
18811b34737SThomas Pedersen 		WARN_ON_ONCE(1);
18911b34737SThomas Pedersen 		return -1;
19011b34737SThomas Pedersen 	}
19111b34737SThomas Pedersen 	return mhz;
19211b34737SThomas Pedersen }
19311b34737SThomas Pedersen 
cfg80211_chandef_get_width(const struct cfg80211_chan_def * c)19411b34737SThomas Pedersen static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
19511b34737SThomas Pedersen {
19611b34737SThomas Pedersen 	return nl80211_chan_width_to_mhz(c->width);
19711b34737SThomas Pedersen }
19811b34737SThomas Pedersen 
cfg80211_chandef_valid(const struct cfg80211_chan_def * chandef)1999f5e8f6eSJohannes Berg bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
2003d9d1d66SJohannes Berg {
20111b34737SThomas Pedersen 	u32 control_freq, oper_freq;
20211b34737SThomas Pedersen 	int oper_width, control_width;
2033d9d1d66SJohannes Berg 
2043d9d1d66SJohannes Berg 	if (!chandef->chan)
2053d9d1d66SJohannes Berg 		return false;
2063d9d1d66SJohannes Berg 
207be689f68SJohannes Berg 	if (chandef->freq1_offset >= 1000)
208be689f68SJohannes Berg 		return false;
209be689f68SJohannes Berg 
2103d9d1d66SJohannes Berg 	control_freq = chandef->chan->center_freq;
2113d9d1d66SJohannes Berg 
2123d9d1d66SJohannes Berg 	switch (chandef->width) {
2132f301ab2SSimon Wunderlich 	case NL80211_CHAN_WIDTH_5:
2142f301ab2SSimon Wunderlich 	case NL80211_CHAN_WIDTH_10:
2153d9d1d66SJohannes Berg 	case NL80211_CHAN_WIDTH_20:
2163d9d1d66SJohannes Berg 	case NL80211_CHAN_WIDTH_20_NOHT:
217934f4c7dSThomas Pedersen 		if (ieee80211_chandef_to_khz(chandef) !=
218934f4c7dSThomas Pedersen 		    ieee80211_channel_to_khz(chandef->chan))
2193d9d1d66SJohannes Berg 			return false;
2203d9d1d66SJohannes Berg 		if (chandef->center_freq2)
2213d9d1d66SJohannes Berg 			return false;
2223d9d1d66SJohannes Berg 		break;
223c1cd35c6SThomas Pedersen 	case NL80211_CHAN_WIDTH_1:
22411b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_2:
22511b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_4:
22611b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_8:
22711b34737SThomas Pedersen 	case NL80211_CHAN_WIDTH_16:
228c1cd35c6SThomas Pedersen 		if (chandef->chan->band != NL80211_BAND_S1GHZ)
229c1cd35c6SThomas Pedersen 			return false;
230c1cd35c6SThomas Pedersen 
23111b34737SThomas Pedersen 		control_freq = ieee80211_channel_to_khz(chandef->chan);
23211b34737SThomas Pedersen 		oper_freq = ieee80211_chandef_to_khz(chandef);
23311b34737SThomas Pedersen 		control_width = nl80211_chan_width_to_mhz(
23411b34737SThomas Pedersen 					ieee80211_s1g_channel_width(
23511b34737SThomas Pedersen 								chandef->chan));
23611b34737SThomas Pedersen 		oper_width = cfg80211_chandef_get_width(chandef);
23711b34737SThomas Pedersen 
23811b34737SThomas Pedersen 		if (oper_width < 0 || control_width < 0)
23911b34737SThomas Pedersen 			return false;
24011b34737SThomas Pedersen 		if (chandef->center_freq2)
24111b34737SThomas Pedersen 			return false;
24211b34737SThomas Pedersen 
24311b34737SThomas Pedersen 		if (control_freq + MHZ_TO_KHZ(control_width) / 2 >
24411b34737SThomas Pedersen 		    oper_freq + MHZ_TO_KHZ(oper_width) / 2)
24511b34737SThomas Pedersen 			return false;
24611b34737SThomas Pedersen 
24711b34737SThomas Pedersen 		if (control_freq - MHZ_TO_KHZ(control_width) / 2 <
24811b34737SThomas Pedersen 		    oper_freq - MHZ_TO_KHZ(oper_width) / 2)
24911b34737SThomas Pedersen 			return false;
25011b34737SThomas Pedersen 		break;
2513d9d1d66SJohannes Berg 	case NL80211_CHAN_WIDTH_80P80:
2523d9d1d66SJohannes Berg 		if (!chandef->center_freq2)
2533d9d1d66SJohannes Berg 			return false;
2549cab3151SJohannes Berg 		/* adjacent is not allowed -- that's a 160 MHz channel */
2559cab3151SJohannes Berg 		if (chandef->center_freq1 - chandef->center_freq2 == 80 ||
2569cab3151SJohannes Berg 		    chandef->center_freq2 - chandef->center_freq1 == 80)
2579cab3151SJohannes Berg 			return false;
2583d9d1d66SJohannes Berg 		break;
2593bb1ccc4SJohannes Berg 	default:
2603d9d1d66SJohannes Berg 		if (chandef->center_freq2)
2613d9d1d66SJohannes Berg 			return false;
2623d9d1d66SJohannes Berg 		break;
2633bb1ccc4SJohannes Berg 	}
2643bb1ccc4SJohannes Berg 
2653bb1ccc4SJohannes Berg 	switch (chandef->width) {
2663bb1ccc4SJohannes Berg 	case NL80211_CHAN_WIDTH_5:
2673bb1ccc4SJohannes Berg 	case NL80211_CHAN_WIDTH_10:
2683bb1ccc4SJohannes Berg 	case NL80211_CHAN_WIDTH_20:
2693bb1ccc4SJohannes Berg 	case NL80211_CHAN_WIDTH_20_NOHT:
2703bb1ccc4SJohannes Berg 	case NL80211_CHAN_WIDTH_1:
2713bb1ccc4SJohannes Berg 	case NL80211_CHAN_WIDTH_2:
2723bb1ccc4SJohannes Berg 	case NL80211_CHAN_WIDTH_4:
2733bb1ccc4SJohannes Berg 	case NL80211_CHAN_WIDTH_8:
2743bb1ccc4SJohannes Berg 	case NL80211_CHAN_WIDTH_16:
2753bb1ccc4SJohannes Berg 		/* all checked above */
2763bb1ccc4SJohannes Berg 		break;
2773743bec6SJia Ding 	case NL80211_CHAN_WIDTH_320:
2783743bec6SJia Ding 		if (chandef->center_freq1 == control_freq + 150 ||
2793743bec6SJia Ding 		    chandef->center_freq1 == control_freq + 130 ||
2803743bec6SJia Ding 		    chandef->center_freq1 == control_freq + 110 ||
2813743bec6SJia Ding 		    chandef->center_freq1 == control_freq + 90 ||
2823743bec6SJia Ding 		    chandef->center_freq1 == control_freq - 90 ||
2833743bec6SJia Ding 		    chandef->center_freq1 == control_freq - 110 ||
2843743bec6SJia Ding 		    chandef->center_freq1 == control_freq - 130 ||
2853743bec6SJia Ding 		    chandef->center_freq1 == control_freq - 150)
2863743bec6SJia Ding 			break;
2873743bec6SJia Ding 		fallthrough;
2883d9d1d66SJohannes Berg 	case NL80211_CHAN_WIDTH_160:
2893bb1ccc4SJohannes Berg 		if (chandef->center_freq1 == control_freq + 70 ||
2903bb1ccc4SJohannes Berg 		    chandef->center_freq1 == control_freq + 50 ||
2913bb1ccc4SJohannes Berg 		    chandef->center_freq1 == control_freq - 50 ||
2923bb1ccc4SJohannes Berg 		    chandef->center_freq1 == control_freq - 70)
2933d9d1d66SJohannes Berg 			break;
2943bb1ccc4SJohannes Berg 		fallthrough;
2953bb1ccc4SJohannes Berg 	case NL80211_CHAN_WIDTH_80P80:
2963bb1ccc4SJohannes Berg 	case NL80211_CHAN_WIDTH_80:
2973bb1ccc4SJohannes Berg 		if (chandef->center_freq1 == control_freq + 30 ||
2983bb1ccc4SJohannes Berg 		    chandef->center_freq1 == control_freq - 30)
2993bb1ccc4SJohannes Berg 			break;
3003bb1ccc4SJohannes Berg 		fallthrough;
3013bb1ccc4SJohannes Berg 	case NL80211_CHAN_WIDTH_40:
3023bb1ccc4SJohannes Berg 		if (chandef->center_freq1 == control_freq + 10 ||
3033bb1ccc4SJohannes Berg 		    chandef->center_freq1 == control_freq - 10)
3043bb1ccc4SJohannes Berg 			break;
3053bb1ccc4SJohannes Berg 		fallthrough;
3063d9d1d66SJohannes Berg 	default:
3073d9d1d66SJohannes Berg 		return false;
3083d9d1d66SJohannes Berg 	}
3093d9d1d66SJohannes Berg 
310ec649fedSMasashi Honma 	/* channel 14 is only for IEEE 802.11b */
311ec649fedSMasashi Honma 	if (chandef->center_freq1 == 2484 &&
312ec649fedSMasashi Honma 	    chandef->width != NL80211_CHAN_WIDTH_20_NOHT)
313ec649fedSMasashi Honma 		return false;
314ec649fedSMasashi Honma 
3152a38075cSAlexei Avshalom Lazar 	if (cfg80211_chandef_is_edmg(chandef) &&
3162a38075cSAlexei Avshalom Lazar 	    !cfg80211_edmg_chandef_valid(chandef))
3172a38075cSAlexei Avshalom Lazar 		return false;
3182a38075cSAlexei Avshalom Lazar 
3193d9d1d66SJohannes Berg 	return true;
3203d9d1d66SJohannes Berg }
3219f5e8f6eSJohannes Berg EXPORT_SYMBOL(cfg80211_chandef_valid);
3223d9d1d66SJohannes Berg 
chandef_primary_freqs(const struct cfg80211_chan_def * c,u32 * pri40,u32 * pri80,u32 * pri160)3233d9d1d66SJohannes Berg static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
3243743bec6SJia Ding 				  u32 *pri40, u32 *pri80, u32 *pri160)
3253d9d1d66SJohannes Berg {
3263d9d1d66SJohannes Berg 	int tmp;
3273d9d1d66SJohannes Berg 
3283d9d1d66SJohannes Berg 	switch (c->width) {
3293d9d1d66SJohannes Berg 	case NL80211_CHAN_WIDTH_40:
3303d9d1d66SJohannes Berg 		*pri40 = c->center_freq1;
3313d9d1d66SJohannes Berg 		*pri80 = 0;
3323743bec6SJia Ding 		*pri160 = 0;
3333d9d1d66SJohannes Berg 		break;
3343d9d1d66SJohannes Berg 	case NL80211_CHAN_WIDTH_80:
3353d9d1d66SJohannes Berg 	case NL80211_CHAN_WIDTH_80P80:
3363743bec6SJia Ding 		*pri160 = 0;
3373d9d1d66SJohannes Berg 		*pri80 = c->center_freq1;
3383d9d1d66SJohannes Berg 		/* n_P20 */
3393d9d1d66SJohannes Berg 		tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
3403d9d1d66SJohannes Berg 		/* n_P40 */
3413d9d1d66SJohannes Berg 		tmp /= 2;
3423d9d1d66SJohannes Berg 		/* freq_P40 */
3433d9d1d66SJohannes Berg 		*pri40 = c->center_freq1 - 20 + 40 * tmp;
3443d9d1d66SJohannes Berg 		break;
3453d9d1d66SJohannes Berg 	case NL80211_CHAN_WIDTH_160:
3463743bec6SJia Ding 		*pri160 = c->center_freq1;
3473d9d1d66SJohannes Berg 		/* n_P20 */
3483d9d1d66SJohannes Berg 		tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
3493d9d1d66SJohannes Berg 		/* n_P40 */
3503d9d1d66SJohannes Berg 		tmp /= 2;
3513d9d1d66SJohannes Berg 		/* freq_P40 */
3523d9d1d66SJohannes Berg 		*pri40 = c->center_freq1 - 60 + 40 * tmp;
3533d9d1d66SJohannes Berg 		/* n_P80 */
3543d9d1d66SJohannes Berg 		tmp /= 2;
3553d9d1d66SJohannes Berg 		*pri80 = c->center_freq1 - 40 + 80 * tmp;
3563d9d1d66SJohannes Berg 		break;
3573743bec6SJia Ding 	case NL80211_CHAN_WIDTH_320:
3583743bec6SJia Ding 		/* n_P20 */
3593743bec6SJia Ding 		tmp = (150 + c->chan->center_freq - c->center_freq1) / 20;
3603743bec6SJia Ding 		/* n_P40 */
3613743bec6SJia Ding 		tmp /= 2;
3623743bec6SJia Ding 		/* freq_P40 */
3633743bec6SJia Ding 		*pri40 = c->center_freq1 - 140 + 40 * tmp;
3643743bec6SJia Ding 		/* n_P80 */
3653743bec6SJia Ding 		tmp /= 2;
3663743bec6SJia Ding 		*pri80 = c->center_freq1 - 120 + 80 * tmp;
3673743bec6SJia Ding 		/* n_P160 */
3683743bec6SJia Ding 		tmp /= 2;
3693743bec6SJia Ding 		*pri160 = c->center_freq1 - 80 + 160 * tmp;
3703743bec6SJia Ding 		break;
3713d9d1d66SJohannes Berg 	default:
3723d9d1d66SJohannes Berg 		WARN_ON_ONCE(1);
3733d9d1d66SJohannes Berg 	}
3743d9d1d66SJohannes Berg }
3753d9d1d66SJohannes Berg 
3763d9d1d66SJohannes Berg const struct cfg80211_chan_def *
cfg80211_chandef_compatible(const struct cfg80211_chan_def * c1,const struct cfg80211_chan_def * c2)3773d9d1d66SJohannes Berg cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
3783d9d1d66SJohannes Berg 			    const struct cfg80211_chan_def *c2)
3793d9d1d66SJohannes Berg {
3803743bec6SJia Ding 	u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80, c1_pri160, c2_pri160;
3813d9d1d66SJohannes Berg 
3823d9d1d66SJohannes Berg 	/* If they are identical, return */
3833d9d1d66SJohannes Berg 	if (cfg80211_chandef_identical(c1, c2))
3843d9d1d66SJohannes Berg 		return c1;
3853d9d1d66SJohannes Berg 
3863d9d1d66SJohannes Berg 	/* otherwise, must have same control channel */
3873d9d1d66SJohannes Berg 	if (c1->chan != c2->chan)
3883d9d1d66SJohannes Berg 		return NULL;
3893d9d1d66SJohannes Berg 
3903d9d1d66SJohannes Berg 	/*
3913d9d1d66SJohannes Berg 	 * If they have the same width, but aren't identical,
3923d9d1d66SJohannes Berg 	 * then they can't be compatible.
3933d9d1d66SJohannes Berg 	 */
3943d9d1d66SJohannes Berg 	if (c1->width == c2->width)
3953d9d1d66SJohannes Berg 		return NULL;
3963d9d1d66SJohannes Berg 
3972f301ab2SSimon Wunderlich 	/*
3982f301ab2SSimon Wunderlich 	 * can't be compatible if one of them is 5 or 10 MHz,
3992f301ab2SSimon Wunderlich 	 * but they don't have the same width.
4002f301ab2SSimon Wunderlich 	 */
4012f301ab2SSimon Wunderlich 	if (c1->width == NL80211_CHAN_WIDTH_5 ||
4022f301ab2SSimon Wunderlich 	    c1->width == NL80211_CHAN_WIDTH_10 ||
4032f301ab2SSimon Wunderlich 	    c2->width == NL80211_CHAN_WIDTH_5 ||
4042f301ab2SSimon Wunderlich 	    c2->width == NL80211_CHAN_WIDTH_10)
4052f301ab2SSimon Wunderlich 		return NULL;
4062f301ab2SSimon Wunderlich 
4073d9d1d66SJohannes Berg 	if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
4083d9d1d66SJohannes Berg 	    c1->width == NL80211_CHAN_WIDTH_20)
4093d9d1d66SJohannes Berg 		return c2;
4103d9d1d66SJohannes Berg 
4113d9d1d66SJohannes Berg 	if (c2->width == NL80211_CHAN_WIDTH_20_NOHT ||
4123d9d1d66SJohannes Berg 	    c2->width == NL80211_CHAN_WIDTH_20)
4133d9d1d66SJohannes Berg 		return c1;
4143d9d1d66SJohannes Berg 
4153743bec6SJia Ding 	chandef_primary_freqs(c1, &c1_pri40, &c1_pri80, &c1_pri160);
4163743bec6SJia Ding 	chandef_primary_freqs(c2, &c2_pri40, &c2_pri80, &c2_pri160);
4173d9d1d66SJohannes Berg 
4183d9d1d66SJohannes Berg 	if (c1_pri40 != c2_pri40)
4193d9d1d66SJohannes Berg 		return NULL;
4203d9d1d66SJohannes Berg 
4213743bec6SJia Ding 	if (c1->width == NL80211_CHAN_WIDTH_40)
4223743bec6SJia Ding 		return c2;
4233743bec6SJia Ding 
4243743bec6SJia Ding 	if (c2->width == NL80211_CHAN_WIDTH_40)
4253743bec6SJia Ding 		return c1;
4263743bec6SJia Ding 
4273743bec6SJia Ding 	if (c1_pri80 != c2_pri80)
4283743bec6SJia Ding 		return NULL;
4293743bec6SJia Ding 
4303743bec6SJia Ding 	if (c1->width == NL80211_CHAN_WIDTH_80 &&
4313743bec6SJia Ding 	    c2->width > NL80211_CHAN_WIDTH_80)
4323743bec6SJia Ding 		return c2;
4333743bec6SJia Ding 
4343743bec6SJia Ding 	if (c2->width == NL80211_CHAN_WIDTH_80 &&
4353743bec6SJia Ding 	    c1->width > NL80211_CHAN_WIDTH_80)
4363743bec6SJia Ding 		return c1;
4373743bec6SJia Ding 
4383743bec6SJia Ding 	WARN_ON(!c1_pri160 && !c2_pri160);
4393743bec6SJia Ding 	if (c1_pri160 && c2_pri160 && c1_pri160 != c2_pri160)
4403d9d1d66SJohannes Berg 		return NULL;
4413d9d1d66SJohannes Berg 
4423d9d1d66SJohannes Berg 	if (c1->width > c2->width)
4433d9d1d66SJohannes Berg 		return c1;
4443d9d1d66SJohannes Berg 	return c2;
4453d9d1d66SJohannes Berg }
4463d9d1d66SJohannes Berg EXPORT_SYMBOL(cfg80211_chandef_compatible);
4473d9d1d66SJohannes Berg 
cfg80211_set_chans_dfs_state(struct wiphy * wiphy,u32 center_freq,u32 bandwidth,enum nl80211_dfs_state dfs_state)44804f39047SSimon Wunderlich static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
44904f39047SSimon Wunderlich 					 u32 bandwidth,
45004f39047SSimon Wunderlich 					 enum nl80211_dfs_state dfs_state)
45104f39047SSimon Wunderlich {
45204f39047SSimon Wunderlich 	struct ieee80211_channel *c;
45304f39047SSimon Wunderlich 	u32 freq;
45404f39047SSimon Wunderlich 
45504f39047SSimon Wunderlich 	for (freq = center_freq - bandwidth/2 + 10;
45604f39047SSimon Wunderlich 	     freq <= center_freq + bandwidth/2 - 10;
45704f39047SSimon Wunderlich 	     freq += 20) {
45804f39047SSimon Wunderlich 		c = ieee80211_get_channel(wiphy, freq);
45904f39047SSimon Wunderlich 		if (!c || !(c->flags & IEEE80211_CHAN_RADAR))
46004f39047SSimon Wunderlich 			continue;
46104f39047SSimon Wunderlich 
46204f39047SSimon Wunderlich 		c->dfs_state = dfs_state;
46304f39047SSimon Wunderlich 		c->dfs_state_entered = jiffies;
46404f39047SSimon Wunderlich 	}
46504f39047SSimon Wunderlich }
46604f39047SSimon Wunderlich 
cfg80211_set_dfs_state(struct wiphy * wiphy,const struct cfg80211_chan_def * chandef,enum nl80211_dfs_state dfs_state)46704f39047SSimon Wunderlich void cfg80211_set_dfs_state(struct wiphy *wiphy,
46804f39047SSimon Wunderlich 			    const struct cfg80211_chan_def *chandef,
46904f39047SSimon Wunderlich 			    enum nl80211_dfs_state dfs_state)
47004f39047SSimon Wunderlich {
47104f39047SSimon Wunderlich 	int width;
47204f39047SSimon Wunderlich 
47304f39047SSimon Wunderlich 	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
47404f39047SSimon Wunderlich 		return;
47504f39047SSimon Wunderlich 
47604f39047SSimon Wunderlich 	width = cfg80211_chandef_get_width(chandef);
47704f39047SSimon Wunderlich 	if (width < 0)
47804f39047SSimon Wunderlich 		return;
47904f39047SSimon Wunderlich 
48004f39047SSimon Wunderlich 	cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1,
48104f39047SSimon Wunderlich 				     width, dfs_state);
48204f39047SSimon Wunderlich 
48304f39047SSimon Wunderlich 	if (!chandef->center_freq2)
48404f39047SSimon Wunderlich 		return;
48504f39047SSimon Wunderlich 	cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2,
48604f39047SSimon Wunderlich 				     width, dfs_state);
48704f39047SSimon Wunderlich }
48804f39047SSimon Wunderlich 
cfg80211_get_start_freq(u32 center_freq,u32 bandwidth)48940d1ba63SJanusz Dziedzic static u32 cfg80211_get_start_freq(u32 center_freq,
49040d1ba63SJanusz Dziedzic 				   u32 bandwidth)
49140d1ba63SJanusz Dziedzic {
49240d1ba63SJanusz Dziedzic 	u32 start_freq;
49340d1ba63SJanusz Dziedzic 
494934f4c7dSThomas Pedersen 	bandwidth = MHZ_TO_KHZ(bandwidth);
495934f4c7dSThomas Pedersen 	if (bandwidth <= MHZ_TO_KHZ(20))
49640d1ba63SJanusz Dziedzic 		start_freq = center_freq;
49740d1ba63SJanusz Dziedzic 	else
498934f4c7dSThomas Pedersen 		start_freq = center_freq - bandwidth / 2 + MHZ_TO_KHZ(10);
49940d1ba63SJanusz Dziedzic 
50040d1ba63SJanusz Dziedzic 	return start_freq;
50140d1ba63SJanusz Dziedzic }
50240d1ba63SJanusz Dziedzic 
cfg80211_get_end_freq(u32 center_freq,u32 bandwidth)50340d1ba63SJanusz Dziedzic static u32 cfg80211_get_end_freq(u32 center_freq,
50440d1ba63SJanusz Dziedzic 				 u32 bandwidth)
50540d1ba63SJanusz Dziedzic {
50640d1ba63SJanusz Dziedzic 	u32 end_freq;
50740d1ba63SJanusz Dziedzic 
508934f4c7dSThomas Pedersen 	bandwidth = MHZ_TO_KHZ(bandwidth);
509934f4c7dSThomas Pedersen 	if (bandwidth <= MHZ_TO_KHZ(20))
51040d1ba63SJanusz Dziedzic 		end_freq = center_freq;
51140d1ba63SJanusz Dziedzic 	else
512934f4c7dSThomas Pedersen 		end_freq = center_freq + bandwidth / 2 - MHZ_TO_KHZ(10);
51340d1ba63SJanusz Dziedzic 
51440d1ba63SJanusz Dziedzic 	return end_freq;
51540d1ba63SJanusz Dziedzic }
51640d1ba63SJanusz Dziedzic 
cfg80211_get_chans_dfs_required(struct wiphy * wiphy,u32 center_freq,u32 bandwidth)51704f39047SSimon Wunderlich static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
51804f39047SSimon Wunderlich 					    u32 center_freq,
51904f39047SSimon Wunderlich 					    u32 bandwidth)
52004f39047SSimon Wunderlich {
52104f39047SSimon Wunderlich 	struct ieee80211_channel *c;
5222f301ab2SSimon Wunderlich 	u32 freq, start_freq, end_freq;
52304f39047SSimon Wunderlich 
52440d1ba63SJanusz Dziedzic 	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
52540d1ba63SJanusz Dziedzic 	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
5262f301ab2SSimon Wunderlich 
527934f4c7dSThomas Pedersen 	for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
528934f4c7dSThomas Pedersen 		c = ieee80211_get_channel_khz(wiphy, freq);
52904f39047SSimon Wunderlich 		if (!c)
53004f39047SSimon Wunderlich 			return -EINVAL;
53104f39047SSimon Wunderlich 
53204f39047SSimon Wunderlich 		if (c->flags & IEEE80211_CHAN_RADAR)
53304f39047SSimon Wunderlich 			return 1;
53404f39047SSimon Wunderlich 	}
53504f39047SSimon Wunderlich 	return 0;
53604f39047SSimon Wunderlich }
53704f39047SSimon Wunderlich 
53804f39047SSimon Wunderlich 
cfg80211_chandef_dfs_required(struct wiphy * wiphy,const struct cfg80211_chan_def * chandef,enum nl80211_iftype iftype)53904f39047SSimon Wunderlich int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
5402beb6dabSLuciano Coelho 				  const struct cfg80211_chan_def *chandef,
5412beb6dabSLuciano Coelho 				  enum nl80211_iftype iftype)
54204f39047SSimon Wunderlich {
54304f39047SSimon Wunderlich 	int width;
5442beb6dabSLuciano Coelho 	int ret;
54504f39047SSimon Wunderlich 
54604f39047SSimon Wunderlich 	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
54704f39047SSimon Wunderlich 		return -EINVAL;
54804f39047SSimon Wunderlich 
5492beb6dabSLuciano Coelho 	switch (iftype) {
5502beb6dabSLuciano Coelho 	case NL80211_IFTYPE_ADHOC:
5512beb6dabSLuciano Coelho 	case NL80211_IFTYPE_AP:
5522beb6dabSLuciano Coelho 	case NL80211_IFTYPE_P2P_GO:
5532beb6dabSLuciano Coelho 	case NL80211_IFTYPE_MESH_POINT:
55404f39047SSimon Wunderlich 		width = cfg80211_chandef_get_width(chandef);
55504f39047SSimon Wunderlich 		if (width < 0)
55604f39047SSimon Wunderlich 			return -EINVAL;
55704f39047SSimon Wunderlich 
5582beb6dabSLuciano Coelho 		ret = cfg80211_get_chans_dfs_required(wiphy,
559934f4c7dSThomas Pedersen 					ieee80211_chandef_to_khz(chandef),
56004f39047SSimon Wunderlich 					width);
5612beb6dabSLuciano Coelho 		if (ret < 0)
5622beb6dabSLuciano Coelho 			return ret;
5632beb6dabSLuciano Coelho 		else if (ret > 0)
5642beb6dabSLuciano Coelho 			return BIT(chandef->width);
56504f39047SSimon Wunderlich 
56604f39047SSimon Wunderlich 		if (!chandef->center_freq2)
56704f39047SSimon Wunderlich 			return 0;
56804f39047SSimon Wunderlich 
5692beb6dabSLuciano Coelho 		ret = cfg80211_get_chans_dfs_required(wiphy,
570934f4c7dSThomas Pedersen 					MHZ_TO_KHZ(chandef->center_freq2),
57104f39047SSimon Wunderlich 					width);
5722beb6dabSLuciano Coelho 		if (ret < 0)
5732beb6dabSLuciano Coelho 			return ret;
5742beb6dabSLuciano Coelho 		else if (ret > 0)
5752beb6dabSLuciano Coelho 			return BIT(chandef->width);
5762beb6dabSLuciano Coelho 
5772beb6dabSLuciano Coelho 		break;
5782beb6dabSLuciano Coelho 	case NL80211_IFTYPE_STATION:
5796e0bd6c3SRostislav Lisovy 	case NL80211_IFTYPE_OCB:
5802beb6dabSLuciano Coelho 	case NL80211_IFTYPE_P2P_CLIENT:
5812beb6dabSLuciano Coelho 	case NL80211_IFTYPE_MONITOR:
5822beb6dabSLuciano Coelho 	case NL80211_IFTYPE_AP_VLAN:
5832beb6dabSLuciano Coelho 	case NL80211_IFTYPE_P2P_DEVICE:
584cb3b7d87SAyala Beker 	case NL80211_IFTYPE_NAN:
5852beb6dabSLuciano Coelho 		break;
586e7e0517cSJohannes Berg 	case NL80211_IFTYPE_WDS:
58700ec75fcSLuciano Coelho 	case NL80211_IFTYPE_UNSPECIFIED:
5882beb6dabSLuciano Coelho 	case NUM_NL80211_IFTYPES:
5892beb6dabSLuciano Coelho 		WARN_ON(1);
5902beb6dabSLuciano Coelho 	}
5912beb6dabSLuciano Coelho 
5922beb6dabSLuciano Coelho 	return 0;
59304f39047SSimon Wunderlich }
594774f0734SSimon Wunderlich EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
59504f39047SSimon Wunderlich 
cfg80211_get_chans_dfs_usable(struct wiphy * wiphy,u32 center_freq,u32 bandwidth)596fe7c3a1fSJanusz Dziedzic static int cfg80211_get_chans_dfs_usable(struct wiphy *wiphy,
597fe7c3a1fSJanusz Dziedzic 					 u32 center_freq,
598fe7c3a1fSJanusz Dziedzic 					 u32 bandwidth)
599fe7c3a1fSJanusz Dziedzic {
600fe7c3a1fSJanusz Dziedzic 	struct ieee80211_channel *c;
601fe7c3a1fSJanusz Dziedzic 	u32 freq, start_freq, end_freq;
602fe7c3a1fSJanusz Dziedzic 	int count = 0;
603fe7c3a1fSJanusz Dziedzic 
604fe7c3a1fSJanusz Dziedzic 	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
605fe7c3a1fSJanusz Dziedzic 	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
606fe7c3a1fSJanusz Dziedzic 
607fe7c3a1fSJanusz Dziedzic 	/*
608fe7c3a1fSJanusz Dziedzic 	 * Check entire range of channels for the bandwidth.
609fe7c3a1fSJanusz Dziedzic 	 * Check all channels are DFS channels (DFS_USABLE or
610fe7c3a1fSJanusz Dziedzic 	 * DFS_AVAILABLE). Return number of usable channels
611fe7c3a1fSJanusz Dziedzic 	 * (require CAC). Allow DFS and non-DFS channel mix.
612fe7c3a1fSJanusz Dziedzic 	 */
613934f4c7dSThomas Pedersen 	for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
614934f4c7dSThomas Pedersen 		c = ieee80211_get_channel_khz(wiphy, freq);
615fe7c3a1fSJanusz Dziedzic 		if (!c)
616fe7c3a1fSJanusz Dziedzic 			return -EINVAL;
617fe7c3a1fSJanusz Dziedzic 
618fe7c3a1fSJanusz Dziedzic 		if (c->flags & IEEE80211_CHAN_DISABLED)
619fe7c3a1fSJanusz Dziedzic 			return -EINVAL;
620fe7c3a1fSJanusz Dziedzic 
621fe7c3a1fSJanusz Dziedzic 		if (c->flags & IEEE80211_CHAN_RADAR) {
622fe7c3a1fSJanusz Dziedzic 			if (c->dfs_state == NL80211_DFS_UNAVAILABLE)
623fe7c3a1fSJanusz Dziedzic 				return -EINVAL;
624fe7c3a1fSJanusz Dziedzic 
625fe7c3a1fSJanusz Dziedzic 			if (c->dfs_state == NL80211_DFS_USABLE)
626fe7c3a1fSJanusz Dziedzic 				count++;
627fe7c3a1fSJanusz Dziedzic 		}
628fe7c3a1fSJanusz Dziedzic 	}
629fe7c3a1fSJanusz Dziedzic 
630fe7c3a1fSJanusz Dziedzic 	return count;
631fe7c3a1fSJanusz Dziedzic }
632fe7c3a1fSJanusz Dziedzic 
cfg80211_chandef_dfs_usable(struct wiphy * wiphy,const struct cfg80211_chan_def * chandef)633fe7c3a1fSJanusz Dziedzic bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
634fe7c3a1fSJanusz Dziedzic 				 const struct cfg80211_chan_def *chandef)
635fe7c3a1fSJanusz Dziedzic {
636fe7c3a1fSJanusz Dziedzic 	int width;
637fe7c3a1fSJanusz Dziedzic 	int r1, r2 = 0;
638fe7c3a1fSJanusz Dziedzic 
639fe7c3a1fSJanusz Dziedzic 	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
640fe7c3a1fSJanusz Dziedzic 		return false;
641fe7c3a1fSJanusz Dziedzic 
642fe7c3a1fSJanusz Dziedzic 	width = cfg80211_chandef_get_width(chandef);
643fe7c3a1fSJanusz Dziedzic 	if (width < 0)
644fe7c3a1fSJanusz Dziedzic 		return false;
645fe7c3a1fSJanusz Dziedzic 
646934f4c7dSThomas Pedersen 	r1 = cfg80211_get_chans_dfs_usable(wiphy,
647934f4c7dSThomas Pedersen 					   MHZ_TO_KHZ(chandef->center_freq1),
648fe7c3a1fSJanusz Dziedzic 					   width);
649fe7c3a1fSJanusz Dziedzic 
650fe7c3a1fSJanusz Dziedzic 	if (r1 < 0)
651fe7c3a1fSJanusz Dziedzic 		return false;
652fe7c3a1fSJanusz Dziedzic 
653fe7c3a1fSJanusz Dziedzic 	switch (chandef->width) {
654fe7c3a1fSJanusz Dziedzic 	case NL80211_CHAN_WIDTH_80P80:
655fe7c3a1fSJanusz Dziedzic 		WARN_ON(!chandef->center_freq2);
656fe7c3a1fSJanusz Dziedzic 		r2 = cfg80211_get_chans_dfs_usable(wiphy,
657934f4c7dSThomas Pedersen 					MHZ_TO_KHZ(chandef->center_freq2),
658fe7c3a1fSJanusz Dziedzic 					width);
659fe7c3a1fSJanusz Dziedzic 		if (r2 < 0)
660fe7c3a1fSJanusz Dziedzic 			return false;
661fe7c3a1fSJanusz Dziedzic 		break;
662fe7c3a1fSJanusz Dziedzic 	default:
663fe7c3a1fSJanusz Dziedzic 		WARN_ON(chandef->center_freq2);
664fe7c3a1fSJanusz Dziedzic 		break;
665fe7c3a1fSJanusz Dziedzic 	}
666fe7c3a1fSJanusz Dziedzic 
667fe7c3a1fSJanusz Dziedzic 	return (r1 + r2 > 0);
668fe7c3a1fSJanusz Dziedzic }
669fe7c3a1fSJanusz Dziedzic 
670b35a51c7SVasanthakumar Thiagarajan /*
671b35a51c7SVasanthakumar Thiagarajan  * Checks if center frequency of chan falls with in the bandwidth
672b35a51c7SVasanthakumar Thiagarajan  * range of chandef.
673b35a51c7SVasanthakumar Thiagarajan  */
cfg80211_is_sub_chan(struct cfg80211_chan_def * chandef,struct ieee80211_channel * chan,bool primary_only)674b35a51c7SVasanthakumar Thiagarajan bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef,
6757b0a0e3cSJohannes Berg 			  struct ieee80211_channel *chan,
6767b0a0e3cSJohannes Berg 			  bool primary_only)
677b35a51c7SVasanthakumar Thiagarajan {
678b35a51c7SVasanthakumar Thiagarajan 	int width;
679a67a4893SJohannes Berg 	u32 freq;
680b35a51c7SVasanthakumar Thiagarajan 
6817b0a0e3cSJohannes Berg 	if (!chandef->chan)
6827b0a0e3cSJohannes Berg 		return false;
6837b0a0e3cSJohannes Berg 
684b35a51c7SVasanthakumar Thiagarajan 	if (chandef->chan->center_freq == chan->center_freq)
685b35a51c7SVasanthakumar Thiagarajan 		return true;
686b35a51c7SVasanthakumar Thiagarajan 
6877b0a0e3cSJohannes Berg 	if (primary_only)
6887b0a0e3cSJohannes Berg 		return false;
6897b0a0e3cSJohannes Berg 
690b35a51c7SVasanthakumar Thiagarajan 	width = cfg80211_chandef_get_width(chandef);
691b35a51c7SVasanthakumar Thiagarajan 	if (width <= 20)
692b35a51c7SVasanthakumar Thiagarajan 		return false;
693b35a51c7SVasanthakumar Thiagarajan 
694b35a51c7SVasanthakumar Thiagarajan 	for (freq = chandef->center_freq1 - width / 2 + 10;
695b35a51c7SVasanthakumar Thiagarajan 	     freq <= chandef->center_freq1 + width / 2 - 10; freq += 20) {
696b35a51c7SVasanthakumar Thiagarajan 		if (chan->center_freq == freq)
697b35a51c7SVasanthakumar Thiagarajan 			return true;
698b35a51c7SVasanthakumar Thiagarajan 	}
699b35a51c7SVasanthakumar Thiagarajan 
700b35a51c7SVasanthakumar Thiagarajan 	if (!chandef->center_freq2)
701b35a51c7SVasanthakumar Thiagarajan 		return false;
702b35a51c7SVasanthakumar Thiagarajan 
703b35a51c7SVasanthakumar Thiagarajan 	for (freq = chandef->center_freq2 - width / 2 + 10;
704b35a51c7SVasanthakumar Thiagarajan 	     freq <= chandef->center_freq2 + width / 2 - 10; freq += 20) {
705b35a51c7SVasanthakumar Thiagarajan 		if (chan->center_freq == freq)
706b35a51c7SVasanthakumar Thiagarajan 			return true;
707b35a51c7SVasanthakumar Thiagarajan 	}
708b35a51c7SVasanthakumar Thiagarajan 
709b35a51c7SVasanthakumar Thiagarajan 	return false;
710b35a51c7SVasanthakumar Thiagarajan }
711b35a51c7SVasanthakumar Thiagarajan 
cfg80211_beaconing_iface_active(struct wireless_dev * wdev)712b35a51c7SVasanthakumar Thiagarajan bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev)
713b35a51c7SVasanthakumar Thiagarajan {
7147b0a0e3cSJohannes Berg 	unsigned int link;
715b35a51c7SVasanthakumar Thiagarajan 
716b35a51c7SVasanthakumar Thiagarajan 	ASSERT_WDEV_LOCK(wdev);
717b35a51c7SVasanthakumar Thiagarajan 
718b35a51c7SVasanthakumar Thiagarajan 	switch (wdev->iftype) {
719b35a51c7SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_AP:
720b35a51c7SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_P2P_GO:
7217b0a0e3cSJohannes Berg 		for_each_valid_link(wdev, link) {
7227b0a0e3cSJohannes Berg 			if (wdev->links[link].ap.beacon_interval)
7237b0a0e3cSJohannes Berg 				return true;
7247b0a0e3cSJohannes Berg 		}
725b35a51c7SVasanthakumar Thiagarajan 		break;
726b35a51c7SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_ADHOC:
7277b0a0e3cSJohannes Berg 		if (wdev->u.ibss.ssid_len)
7287b0a0e3cSJohannes Berg 			return true;
729b35a51c7SVasanthakumar Thiagarajan 		break;
730b35a51c7SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_MESH_POINT:
7317b0a0e3cSJohannes Berg 		if (wdev->u.mesh.id_len)
7327b0a0e3cSJohannes Berg 			return true;
733b35a51c7SVasanthakumar Thiagarajan 		break;
734b35a51c7SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_STATION:
735b35a51c7SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_OCB:
736b35a51c7SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_P2P_CLIENT:
737b35a51c7SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_MONITOR:
738b35a51c7SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_AP_VLAN:
739b35a51c7SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_P2P_DEVICE:
740b35a51c7SVasanthakumar Thiagarajan 	/* Can NAN type be considered as beaconing interface? */
741b35a51c7SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_NAN:
742b35a51c7SVasanthakumar Thiagarajan 		break;
743b35a51c7SVasanthakumar Thiagarajan 	case NL80211_IFTYPE_UNSPECIFIED:
744e7e0517cSJohannes Berg 	case NL80211_IFTYPE_WDS:
745b35a51c7SVasanthakumar Thiagarajan 	case NUM_NL80211_IFTYPES:
746b35a51c7SVasanthakumar Thiagarajan 		WARN_ON(1);
747b35a51c7SVasanthakumar Thiagarajan 	}
748b35a51c7SVasanthakumar Thiagarajan 
7497b0a0e3cSJohannes Berg 	return false;
7507b0a0e3cSJohannes Berg }
7517b0a0e3cSJohannes Berg 
cfg80211_wdev_on_sub_chan(struct wireless_dev * wdev,struct ieee80211_channel * chan,bool primary_only)7527b0a0e3cSJohannes Berg bool cfg80211_wdev_on_sub_chan(struct wireless_dev *wdev,
7537b0a0e3cSJohannes Berg 			       struct ieee80211_channel *chan,
7547b0a0e3cSJohannes Berg 			       bool primary_only)
7557b0a0e3cSJohannes Berg {
7567b0a0e3cSJohannes Berg 	unsigned int link;
7577b0a0e3cSJohannes Berg 
7587b0a0e3cSJohannes Berg 	switch (wdev->iftype) {
7597b0a0e3cSJohannes Berg 	case NL80211_IFTYPE_AP:
7607b0a0e3cSJohannes Berg 	case NL80211_IFTYPE_P2P_GO:
7617b0a0e3cSJohannes Berg 		for_each_valid_link(wdev, link) {
7627b0a0e3cSJohannes Berg 			if (cfg80211_is_sub_chan(&wdev->links[link].ap.chandef,
7637b0a0e3cSJohannes Berg 						 chan, primary_only))
7647b0a0e3cSJohannes Berg 				return true;
7657b0a0e3cSJohannes Berg 		}
7667b0a0e3cSJohannes Berg 		break;
7677b0a0e3cSJohannes Berg 	case NL80211_IFTYPE_ADHOC:
7687b0a0e3cSJohannes Berg 		return cfg80211_is_sub_chan(&wdev->u.ibss.chandef, chan,
7697b0a0e3cSJohannes Berg 					    primary_only);
7707b0a0e3cSJohannes Berg 	case NL80211_IFTYPE_MESH_POINT:
7717b0a0e3cSJohannes Berg 		return cfg80211_is_sub_chan(&wdev->u.mesh.chandef, chan,
7727b0a0e3cSJohannes Berg 					    primary_only);
7737b0a0e3cSJohannes Berg 	default:
7747b0a0e3cSJohannes Berg 		break;
7757b0a0e3cSJohannes Berg 	}
7767b0a0e3cSJohannes Berg 
7777b0a0e3cSJohannes Berg 	return false;
778b35a51c7SVasanthakumar Thiagarajan }
779b35a51c7SVasanthakumar Thiagarajan 
cfg80211_is_wiphy_oper_chan(struct wiphy * wiphy,struct ieee80211_channel * chan)78089766727SVasanthakumar Thiagarajan static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy,
781b35a51c7SVasanthakumar Thiagarajan 					struct ieee80211_channel *chan)
782b35a51c7SVasanthakumar Thiagarajan {
783b35a51c7SVasanthakumar Thiagarajan 	struct wireless_dev *wdev;
784b35a51c7SVasanthakumar Thiagarajan 
785b35a51c7SVasanthakumar Thiagarajan 	list_for_each_entry(wdev, &wiphy->wdev_list, list) {
786b35a51c7SVasanthakumar Thiagarajan 		wdev_lock(wdev);
787b35a51c7SVasanthakumar Thiagarajan 		if (!cfg80211_beaconing_iface_active(wdev)) {
788b35a51c7SVasanthakumar Thiagarajan 			wdev_unlock(wdev);
789b35a51c7SVasanthakumar Thiagarajan 			continue;
790b35a51c7SVasanthakumar Thiagarajan 		}
791b35a51c7SVasanthakumar Thiagarajan 
7927b0a0e3cSJohannes Berg 		if (cfg80211_wdev_on_sub_chan(wdev, chan, false)) {
793b35a51c7SVasanthakumar Thiagarajan 			wdev_unlock(wdev);
794b35a51c7SVasanthakumar Thiagarajan 			return true;
795b35a51c7SVasanthakumar Thiagarajan 		}
796b35a51c7SVasanthakumar Thiagarajan 		wdev_unlock(wdev);
797b35a51c7SVasanthakumar Thiagarajan 	}
798b35a51c7SVasanthakumar Thiagarajan 
799b35a51c7SVasanthakumar Thiagarajan 	return false;
800b35a51c7SVasanthakumar Thiagarajan }
801fe7c3a1fSJanusz Dziedzic 
80284158164SLorenzo Bianconi static bool
cfg80211_offchan_chain_is_active(struct cfg80211_registered_device * rdev,struct ieee80211_channel * channel)80384158164SLorenzo Bianconi cfg80211_offchan_chain_is_active(struct cfg80211_registered_device *rdev,
80484158164SLorenzo Bianconi 				 struct ieee80211_channel *channel)
80584158164SLorenzo Bianconi {
806a95bfb87SLorenzo Bianconi 	if (!rdev->background_radar_wdev)
80784158164SLorenzo Bianconi 		return false;
80884158164SLorenzo Bianconi 
809a95bfb87SLorenzo Bianconi 	if (!cfg80211_chandef_valid(&rdev->background_radar_chandef))
81084158164SLorenzo Bianconi 		return false;
81184158164SLorenzo Bianconi 
8127b0a0e3cSJohannes Berg 	return cfg80211_is_sub_chan(&rdev->background_radar_chandef, channel,
8137b0a0e3cSJohannes Berg 				    false);
81484158164SLorenzo Bianconi }
81584158164SLorenzo Bianconi 
cfg80211_any_wiphy_oper_chan(struct wiphy * wiphy,struct ieee80211_channel * chan)81689766727SVasanthakumar Thiagarajan bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
81789766727SVasanthakumar Thiagarajan 				  struct ieee80211_channel *chan)
81889766727SVasanthakumar Thiagarajan {
81989766727SVasanthakumar Thiagarajan 	struct cfg80211_registered_device *rdev;
82089766727SVasanthakumar Thiagarajan 
82189766727SVasanthakumar Thiagarajan 	ASSERT_RTNL();
82289766727SVasanthakumar Thiagarajan 
82389766727SVasanthakumar Thiagarajan 	if (!(chan->flags & IEEE80211_CHAN_RADAR))
82489766727SVasanthakumar Thiagarajan 		return false;
82589766727SVasanthakumar Thiagarajan 
82689766727SVasanthakumar Thiagarajan 	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
82789766727SVasanthakumar Thiagarajan 		if (!reg_dfs_domain_same(wiphy, &rdev->wiphy))
82889766727SVasanthakumar Thiagarajan 			continue;
82989766727SVasanthakumar Thiagarajan 
83089766727SVasanthakumar Thiagarajan 		if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan))
83189766727SVasanthakumar Thiagarajan 			return true;
83284158164SLorenzo Bianconi 
83384158164SLorenzo Bianconi 		if (cfg80211_offchan_chain_is_active(rdev, chan))
83484158164SLorenzo Bianconi 			return true;
83589766727SVasanthakumar Thiagarajan 	}
83689766727SVasanthakumar Thiagarajan 
83789766727SVasanthakumar Thiagarajan 	return false;
83889766727SVasanthakumar Thiagarajan }
83989766727SVasanthakumar Thiagarajan 
cfg80211_get_chans_dfs_available(struct wiphy * wiphy,u32 center_freq,u32 bandwidth)8406bc54fbcSJanusz Dziedzic static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
8416bc54fbcSJanusz Dziedzic 					     u32 center_freq,
8426bc54fbcSJanusz Dziedzic 					     u32 bandwidth)
8436bc54fbcSJanusz Dziedzic {
8446bc54fbcSJanusz Dziedzic 	struct ieee80211_channel *c;
8456bc54fbcSJanusz Dziedzic 	u32 freq, start_freq, end_freq;
8462c390e44SDmitry Lebed 	bool dfs_offload;
8472c390e44SDmitry Lebed 
8482c390e44SDmitry Lebed 	dfs_offload = wiphy_ext_feature_isset(wiphy,
8492c390e44SDmitry Lebed 					      NL80211_EXT_FEATURE_DFS_OFFLOAD);
8506bc54fbcSJanusz Dziedzic 
8516bc54fbcSJanusz Dziedzic 	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
8526bc54fbcSJanusz Dziedzic 	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
8536bc54fbcSJanusz Dziedzic 
8546bc54fbcSJanusz Dziedzic 	/*
8556bc54fbcSJanusz Dziedzic 	 * Check entire range of channels for the bandwidth.
8566bc54fbcSJanusz Dziedzic 	 * If any channel in between is disabled or has not
8576bc54fbcSJanusz Dziedzic 	 * had gone through CAC return false
8586bc54fbcSJanusz Dziedzic 	 */
859934f4c7dSThomas Pedersen 	for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
860934f4c7dSThomas Pedersen 		c = ieee80211_get_channel_khz(wiphy, freq);
8616bc54fbcSJanusz Dziedzic 		if (!c)
8626bc54fbcSJanusz Dziedzic 			return false;
8636bc54fbcSJanusz Dziedzic 
8646bc54fbcSJanusz Dziedzic 		if (c->flags & IEEE80211_CHAN_DISABLED)
8656bc54fbcSJanusz Dziedzic 			return false;
8666bc54fbcSJanusz Dziedzic 
8676bc54fbcSJanusz Dziedzic 		if ((c->flags & IEEE80211_CHAN_RADAR) &&
8682c390e44SDmitry Lebed 		    (c->dfs_state != NL80211_DFS_AVAILABLE) &&
8692c390e44SDmitry Lebed 		    !(c->dfs_state == NL80211_DFS_USABLE && dfs_offload))
8706bc54fbcSJanusz Dziedzic 			return false;
8716bc54fbcSJanusz Dziedzic 	}
8726bc54fbcSJanusz Dziedzic 
8736bc54fbcSJanusz Dziedzic 	return true;
8746bc54fbcSJanusz Dziedzic }
8756bc54fbcSJanusz Dziedzic 
cfg80211_chandef_dfs_available(struct wiphy * wiphy,const struct cfg80211_chan_def * chandef)8766bc54fbcSJanusz Dziedzic static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
8776bc54fbcSJanusz Dziedzic 				const struct cfg80211_chan_def *chandef)
8786bc54fbcSJanusz Dziedzic {
8796bc54fbcSJanusz Dziedzic 	int width;
8806bc54fbcSJanusz Dziedzic 	int r;
8816bc54fbcSJanusz Dziedzic 
8826bc54fbcSJanusz Dziedzic 	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
8836bc54fbcSJanusz Dziedzic 		return false;
8846bc54fbcSJanusz Dziedzic 
8856bc54fbcSJanusz Dziedzic 	width = cfg80211_chandef_get_width(chandef);
8866bc54fbcSJanusz Dziedzic 	if (width < 0)
8876bc54fbcSJanusz Dziedzic 		return false;
8886bc54fbcSJanusz Dziedzic 
889934f4c7dSThomas Pedersen 	r = cfg80211_get_chans_dfs_available(wiphy,
890934f4c7dSThomas Pedersen 					     MHZ_TO_KHZ(chandef->center_freq1),
8916bc54fbcSJanusz Dziedzic 					     width);
8926bc54fbcSJanusz Dziedzic 
8936bc54fbcSJanusz Dziedzic 	/* If any of channels unavailable for cf1 just return */
8946bc54fbcSJanusz Dziedzic 	if (!r)
8956bc54fbcSJanusz Dziedzic 		return r;
8966bc54fbcSJanusz Dziedzic 
8976bc54fbcSJanusz Dziedzic 	switch (chandef->width) {
8986bc54fbcSJanusz Dziedzic 	case NL80211_CHAN_WIDTH_80P80:
8996bc54fbcSJanusz Dziedzic 		WARN_ON(!chandef->center_freq2);
9006bc54fbcSJanusz Dziedzic 		r = cfg80211_get_chans_dfs_available(wiphy,
901934f4c7dSThomas Pedersen 					MHZ_TO_KHZ(chandef->center_freq2),
9026bc54fbcSJanusz Dziedzic 					width);
903680682d4SColin Ian King 		break;
9046bc54fbcSJanusz Dziedzic 	default:
9056bc54fbcSJanusz Dziedzic 		WARN_ON(chandef->center_freq2);
9066bc54fbcSJanusz Dziedzic 		break;
9076bc54fbcSJanusz Dziedzic 	}
9086bc54fbcSJanusz Dziedzic 
9096bc54fbcSJanusz Dziedzic 	return r;
9106bc54fbcSJanusz Dziedzic }
9116bc54fbcSJanusz Dziedzic 
cfg80211_get_chans_dfs_cac_time(struct wiphy * wiphy,u32 center_freq,u32 bandwidth)91231559f35SJanusz Dziedzic static unsigned int cfg80211_get_chans_dfs_cac_time(struct wiphy *wiphy,
91331559f35SJanusz Dziedzic 						    u32 center_freq,
91431559f35SJanusz Dziedzic 						    u32 bandwidth)
91531559f35SJanusz Dziedzic {
91631559f35SJanusz Dziedzic 	struct ieee80211_channel *c;
91731559f35SJanusz Dziedzic 	u32 start_freq, end_freq, freq;
91831559f35SJanusz Dziedzic 	unsigned int dfs_cac_ms = 0;
91931559f35SJanusz Dziedzic 
92031559f35SJanusz Dziedzic 	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
92131559f35SJanusz Dziedzic 	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
92231559f35SJanusz Dziedzic 
923934f4c7dSThomas Pedersen 	for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
924934f4c7dSThomas Pedersen 		c = ieee80211_get_channel_khz(wiphy, freq);
92531559f35SJanusz Dziedzic 		if (!c)
92631559f35SJanusz Dziedzic 			return 0;
92731559f35SJanusz Dziedzic 
92831559f35SJanusz Dziedzic 		if (c->flags & IEEE80211_CHAN_DISABLED)
92931559f35SJanusz Dziedzic 			return 0;
93031559f35SJanusz Dziedzic 
93131559f35SJanusz Dziedzic 		if (!(c->flags & IEEE80211_CHAN_RADAR))
93231559f35SJanusz Dziedzic 			continue;
93331559f35SJanusz Dziedzic 
93431559f35SJanusz Dziedzic 		if (c->dfs_cac_ms > dfs_cac_ms)
93531559f35SJanusz Dziedzic 			dfs_cac_ms = c->dfs_cac_ms;
93631559f35SJanusz Dziedzic 	}
93731559f35SJanusz Dziedzic 
93831559f35SJanusz Dziedzic 	return dfs_cac_ms;
93931559f35SJanusz Dziedzic }
94031559f35SJanusz Dziedzic 
94131559f35SJanusz Dziedzic unsigned int
cfg80211_chandef_dfs_cac_time(struct wiphy * wiphy,const struct cfg80211_chan_def * chandef)94231559f35SJanusz Dziedzic cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
94331559f35SJanusz Dziedzic 			      const struct cfg80211_chan_def *chandef)
94431559f35SJanusz Dziedzic {
94531559f35SJanusz Dziedzic 	int width;
94631559f35SJanusz Dziedzic 	unsigned int t1 = 0, t2 = 0;
94731559f35SJanusz Dziedzic 
94831559f35SJanusz Dziedzic 	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
94931559f35SJanusz Dziedzic 		return 0;
95031559f35SJanusz Dziedzic 
95131559f35SJanusz Dziedzic 	width = cfg80211_chandef_get_width(chandef);
95231559f35SJanusz Dziedzic 	if (width < 0)
95331559f35SJanusz Dziedzic 		return 0;
95431559f35SJanusz Dziedzic 
95531559f35SJanusz Dziedzic 	t1 = cfg80211_get_chans_dfs_cac_time(wiphy,
956934f4c7dSThomas Pedersen 					     MHZ_TO_KHZ(chandef->center_freq1),
95731559f35SJanusz Dziedzic 					     width);
95831559f35SJanusz Dziedzic 
95931559f35SJanusz Dziedzic 	if (!chandef->center_freq2)
96031559f35SJanusz Dziedzic 		return t1;
96131559f35SJanusz Dziedzic 
96231559f35SJanusz Dziedzic 	t2 = cfg80211_get_chans_dfs_cac_time(wiphy,
963934f4c7dSThomas Pedersen 					     MHZ_TO_KHZ(chandef->center_freq2),
96431559f35SJanusz Dziedzic 					     width);
96531559f35SJanusz Dziedzic 
96631559f35SJanusz Dziedzic 	return max(t1, t2);
96731559f35SJanusz Dziedzic }
9686bc54fbcSJanusz Dziedzic 
cfg80211_secondary_chans_ok(struct wiphy * wiphy,u32 center_freq,u32 bandwidth,u32 prohibited_flags)9699f5e8f6eSJohannes Berg static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
9703d9d1d66SJohannes Berg 					u32 center_freq, u32 bandwidth,
9713d9d1d66SJohannes Berg 					u32 prohibited_flags)
9723d9d1d66SJohannes Berg {
9733d9d1d66SJohannes Berg 	struct ieee80211_channel *c;
9742f301ab2SSimon Wunderlich 	u32 freq, start_freq, end_freq;
9753d9d1d66SJohannes Berg 
97640d1ba63SJanusz Dziedzic 	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
97740d1ba63SJanusz Dziedzic 	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
9782f301ab2SSimon Wunderlich 
979934f4c7dSThomas Pedersen 	for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
980934f4c7dSThomas Pedersen 		c = ieee80211_get_channel_khz(wiphy, freq);
9816bc54fbcSJanusz Dziedzic 		if (!c || c->flags & prohibited_flags)
9823d9d1d66SJohannes Berg 			return false;
9833d9d1d66SJohannes Berg 	}
9843d9d1d66SJohannes Berg 
9853d9d1d66SJohannes Berg 	return true;
9863d9d1d66SJohannes Berg }
9873d9d1d66SJohannes Berg 
9882a38075cSAlexei Avshalom Lazar /* check if the operating channels are valid and supported */
cfg80211_edmg_usable(struct wiphy * wiphy,u8 edmg_channels,enum ieee80211_edmg_bw_config edmg_bw_config,int primary_channel,struct ieee80211_edmg * edmg_cap)9892a38075cSAlexei Avshalom Lazar static bool cfg80211_edmg_usable(struct wiphy *wiphy, u8 edmg_channels,
9902a38075cSAlexei Avshalom Lazar 				 enum ieee80211_edmg_bw_config edmg_bw_config,
9912a38075cSAlexei Avshalom Lazar 				 int primary_channel,
9922a38075cSAlexei Avshalom Lazar 				 struct ieee80211_edmg *edmg_cap)
9932a38075cSAlexei Avshalom Lazar {
9942a38075cSAlexei Avshalom Lazar 	struct ieee80211_channel *chan;
9952a38075cSAlexei Avshalom Lazar 	int i, freq;
9962a38075cSAlexei Avshalom Lazar 	int channels_counter = 0;
9972a38075cSAlexei Avshalom Lazar 
9982a38075cSAlexei Avshalom Lazar 	if (!edmg_channels && !edmg_bw_config)
9992a38075cSAlexei Avshalom Lazar 		return true;
10002a38075cSAlexei Avshalom Lazar 
10012a38075cSAlexei Avshalom Lazar 	if ((!edmg_channels && edmg_bw_config) ||
10022a38075cSAlexei Avshalom Lazar 	    (edmg_channels && !edmg_bw_config))
10032a38075cSAlexei Avshalom Lazar 		return false;
10042a38075cSAlexei Avshalom Lazar 
10052a38075cSAlexei Avshalom Lazar 	if (!(edmg_channels & BIT(primary_channel - 1)))
10062a38075cSAlexei Avshalom Lazar 		return false;
10072a38075cSAlexei Avshalom Lazar 
10082a38075cSAlexei Avshalom Lazar 	/* 60GHz channels 1..6 */
10092a38075cSAlexei Avshalom Lazar 	for (i = 0; i < 6; i++) {
10102a38075cSAlexei Avshalom Lazar 		if (!(edmg_channels & BIT(i)))
10112a38075cSAlexei Avshalom Lazar 			continue;
10122a38075cSAlexei Avshalom Lazar 
10132a38075cSAlexei Avshalom Lazar 		if (!(edmg_cap->channels & BIT(i)))
10142a38075cSAlexei Avshalom Lazar 			return false;
10152a38075cSAlexei Avshalom Lazar 
10162a38075cSAlexei Avshalom Lazar 		channels_counter++;
10172a38075cSAlexei Avshalom Lazar 
10182a38075cSAlexei Avshalom Lazar 		freq = ieee80211_channel_to_frequency(i + 1,
10192a38075cSAlexei Avshalom Lazar 						      NL80211_BAND_60GHZ);
10202a38075cSAlexei Avshalom Lazar 		chan = ieee80211_get_channel(wiphy, freq);
10212a38075cSAlexei Avshalom Lazar 		if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
10222a38075cSAlexei Avshalom Lazar 			return false;
10232a38075cSAlexei Avshalom Lazar 	}
10242a38075cSAlexei Avshalom Lazar 
10252a38075cSAlexei Avshalom Lazar 	/* IEEE802.11 allows max 4 channels */
10262a38075cSAlexei Avshalom Lazar 	if (channels_counter > 4)
10272a38075cSAlexei Avshalom Lazar 		return false;
10282a38075cSAlexei Avshalom Lazar 
10292a38075cSAlexei Avshalom Lazar 	/* check bw_config is a subset of what driver supports
10302a38075cSAlexei Avshalom Lazar 	 * (see IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13)
10312a38075cSAlexei Avshalom Lazar 	 */
10322a38075cSAlexei Avshalom Lazar 	if ((edmg_bw_config % 4) > (edmg_cap->bw_config % 4))
10332a38075cSAlexei Avshalom Lazar 		return false;
10342a38075cSAlexei Avshalom Lazar 
10352a38075cSAlexei Avshalom Lazar 	if (edmg_bw_config > edmg_cap->bw_config)
10362a38075cSAlexei Avshalom Lazar 		return false;
10372a38075cSAlexei Avshalom Lazar 
10382a38075cSAlexei Avshalom Lazar 	return true;
10392a38075cSAlexei Avshalom Lazar }
10402a38075cSAlexei Avshalom Lazar 
cfg80211_chandef_usable(struct wiphy * wiphy,const struct cfg80211_chan_def * chandef,u32 prohibited_flags)10419f5e8f6eSJohannes Berg bool cfg80211_chandef_usable(struct wiphy *wiphy,
10429f5e8f6eSJohannes Berg 			     const struct cfg80211_chan_def *chandef,
10439f5e8f6eSJohannes Berg 			     u32 prohibited_flags)
10443d9d1d66SJohannes Berg {
10459f5e8f6eSJohannes Berg 	struct ieee80211_sta_ht_cap *ht_cap;
10469f5e8f6eSJohannes Berg 	struct ieee80211_sta_vht_cap *vht_cap;
10472a38075cSAlexei Avshalom Lazar 	struct ieee80211_edmg *edmg_cap;
104808f6f147SJouni Malinen 	u32 width, control_freq, cap;
10493743bec6SJia Ding 	bool ext_nss_cap, support_80_80 = false, support_320 = false;
10503743bec6SJia Ding 	const struct ieee80211_sband_iftype_data *iftd;
10513743bec6SJia Ding 	struct ieee80211_supported_band *sband;
10523743bec6SJia Ding 	int i;
10533d9d1d66SJohannes Berg 
10549f5e8f6eSJohannes Berg 	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
10553d9d1d66SJohannes Berg 		return false;
10569f5e8f6eSJohannes Berg 
10579f5e8f6eSJohannes Berg 	ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
10589f5e8f6eSJohannes Berg 	vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
10592a38075cSAlexei Avshalom Lazar 	edmg_cap = &wiphy->bands[chandef->chan->band]->edmg_cap;
1060e6ed929bSWen Gong 	ext_nss_cap = __le16_to_cpu(vht_cap->vht_mcs.tx_highest) &
1061e6ed929bSWen Gong 			IEEE80211_VHT_EXT_NSS_BW_CAPABLE;
10622a38075cSAlexei Avshalom Lazar 
10632a38075cSAlexei Avshalom Lazar 	if (edmg_cap->channels &&
10642a38075cSAlexei Avshalom Lazar 	    !cfg80211_edmg_usable(wiphy,
10652a38075cSAlexei Avshalom Lazar 				  chandef->edmg.channels,
10662a38075cSAlexei Avshalom Lazar 				  chandef->edmg.bw_config,
10672a38075cSAlexei Avshalom Lazar 				  chandef->chan->hw_value,
10682a38075cSAlexei Avshalom Lazar 				  edmg_cap))
10692a38075cSAlexei Avshalom Lazar 		return false;
10709f5e8f6eSJohannes Berg 
10719f5e8f6eSJohannes Berg 	control_freq = chandef->chan->center_freq;
10723d9d1d66SJohannes Berg 
10733d9d1d66SJohannes Berg 	switch (chandef->width) {
1074df78a0c0SThomas Pedersen 	case NL80211_CHAN_WIDTH_1:
1075df78a0c0SThomas Pedersen 		width = 1;
1076df78a0c0SThomas Pedersen 		break;
1077df78a0c0SThomas Pedersen 	case NL80211_CHAN_WIDTH_2:
1078df78a0c0SThomas Pedersen 		width = 2;
1079df78a0c0SThomas Pedersen 		break;
1080df78a0c0SThomas Pedersen 	case NL80211_CHAN_WIDTH_4:
1081df78a0c0SThomas Pedersen 		width = 4;
1082df78a0c0SThomas Pedersen 		break;
1083df78a0c0SThomas Pedersen 	case NL80211_CHAN_WIDTH_8:
1084df78a0c0SThomas Pedersen 		width = 8;
1085df78a0c0SThomas Pedersen 		break;
1086df78a0c0SThomas Pedersen 	case NL80211_CHAN_WIDTH_16:
1087df78a0c0SThomas Pedersen 		width = 16;
1088df78a0c0SThomas Pedersen 		break;
10892f301ab2SSimon Wunderlich 	case NL80211_CHAN_WIDTH_5:
10902f301ab2SSimon Wunderlich 		width = 5;
10912f301ab2SSimon Wunderlich 		break;
10922f301ab2SSimon Wunderlich 	case NL80211_CHAN_WIDTH_10:
1093ea077c1cSRostislav Lisovy 		prohibited_flags |= IEEE80211_CHAN_NO_10MHZ;
10942f301ab2SSimon Wunderlich 		width = 10;
10952f301ab2SSimon Wunderlich 		break;
10963d9d1d66SJohannes Berg 	case NL80211_CHAN_WIDTH_20:
1097ba8f6a03SJohannes Berg 		if (!ht_cap->ht_supported &&
1098ba8f6a03SJohannes Berg 		    chandef->chan->band != NL80211_BAND_6GHZ)
10999f5e8f6eSJohannes Berg 			return false;
1100df561f66SGustavo A. R. Silva 		fallthrough;
11019f5e8f6eSJohannes Berg 	case NL80211_CHAN_WIDTH_20_NOHT:
1102ea077c1cSRostislav Lisovy 		prohibited_flags |= IEEE80211_CHAN_NO_20MHZ;
11033d9d1d66SJohannes Berg 		width = 20;
110409a02fdbSMark Mentovai 		break;
11053d9d1d66SJohannes Berg 	case NL80211_CHAN_WIDTH_40:
11063d9d1d66SJohannes Berg 		width = 40;
1107ba8f6a03SJohannes Berg 		if (chandef->chan->band == NL80211_BAND_6GHZ)
1108ba8f6a03SJohannes Berg 			break;
11099f5e8f6eSJohannes Berg 		if (!ht_cap->ht_supported)
11109f5e8f6eSJohannes Berg 			return false;
11119f5e8f6eSJohannes Berg 		if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
11129f5e8f6eSJohannes Berg 		    ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
11139f5e8f6eSJohannes Berg 			return false;
11149f5e8f6eSJohannes Berg 		if (chandef->center_freq1 < control_freq &&
11159f5e8f6eSJohannes Berg 		    chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
11169f5e8f6eSJohannes Berg 			return false;
11179f5e8f6eSJohannes Berg 		if (chandef->center_freq1 > control_freq &&
11189f5e8f6eSJohannes Berg 		    chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
11199f5e8f6eSJohannes Berg 			return false;
11203d9d1d66SJohannes Berg 		break;
11213d9d1d66SJohannes Berg 	case NL80211_CHAN_WIDTH_80P80:
112235799944SShay Bar 		cap = vht_cap->cap;
112335799944SShay Bar 		support_80_80 =
112435799944SShay Bar 			(cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) ||
112535799944SShay Bar 			(cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ &&
112635799944SShay Bar 			 cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) ||
1127e6ed929bSWen Gong 			(ext_nss_cap &&
1128e6ed929bSWen Gong 			 u32_get_bits(cap, IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) > 1);
112935799944SShay Bar 		if (chandef->chan->band != NL80211_BAND_6GHZ && !support_80_80)
11309f5e8f6eSJohannes Berg 			return false;
1131df561f66SGustavo A. R. Silva 		fallthrough;
11329f5e8f6eSJohannes Berg 	case NL80211_CHAN_WIDTH_80:
1133c7a6ee27SJohannes Berg 		prohibited_flags |= IEEE80211_CHAN_NO_80MHZ;
11343d9d1d66SJohannes Berg 		width = 80;
1135ba8f6a03SJohannes Berg 		if (chandef->chan->band == NL80211_BAND_6GHZ)
1136ba8f6a03SJohannes Berg 			break;
1137ba8f6a03SJohannes Berg 		if (!vht_cap->vht_supported)
1138ba8f6a03SJohannes Berg 			return false;
11393d9d1d66SJohannes Berg 		break;
11403d9d1d66SJohannes Berg 	case NL80211_CHAN_WIDTH_160:
1141ba8f6a03SJohannes Berg 		prohibited_flags |= IEEE80211_CHAN_NO_160MHZ;
1142ba8f6a03SJohannes Berg 		width = 160;
1143ba8f6a03SJohannes Berg 		if (chandef->chan->band == NL80211_BAND_6GHZ)
1144ba8f6a03SJohannes Berg 			break;
11459f5e8f6eSJohannes Berg 		if (!vht_cap->vht_supported)
11469f5e8f6eSJohannes Berg 			return false;
114708f6f147SJouni Malinen 		cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
114808f6f147SJouni Malinen 		if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ &&
114935799944SShay Bar 		    cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ &&
1150e6ed929bSWen Gong 		    !(ext_nss_cap &&
1151e6ed929bSWen Gong 		      (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)))
11529f5e8f6eSJohannes Berg 			return false;
115309a02fdbSMark Mentovai 		break;
11543743bec6SJia Ding 	case NL80211_CHAN_WIDTH_320:
11553743bec6SJia Ding 		prohibited_flags |= IEEE80211_CHAN_NO_320MHZ;
11563743bec6SJia Ding 		width = 320;
11573743bec6SJia Ding 
11583743bec6SJia Ding 		if (chandef->chan->band != NL80211_BAND_6GHZ)
11593743bec6SJia Ding 			return false;
11603743bec6SJia Ding 
11613743bec6SJia Ding 		sband = wiphy->bands[NL80211_BAND_6GHZ];
11623743bec6SJia Ding 		if (!sband)
11633743bec6SJia Ding 			return false;
11643743bec6SJia Ding 
11653743bec6SJia Ding 		for (i = 0; i < sband->n_iftype_data; i++) {
11663743bec6SJia Ding 			iftd = &sband->iftype_data[i];
11673743bec6SJia Ding 			if (!iftd->eht_cap.has_eht)
11683743bec6SJia Ding 				continue;
11693743bec6SJia Ding 
11703743bec6SJia Ding 			if (iftd->eht_cap.eht_cap_elem.phy_cap_info[0] &
11713743bec6SJia Ding 			    IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) {
11723743bec6SJia Ding 				support_320 = true;
11733743bec6SJia Ding 				break;
11743743bec6SJia Ding 			}
11753743bec6SJia Ding 		}
11763743bec6SJia Ding 
11773743bec6SJia Ding 		if (!support_320)
11783743bec6SJia Ding 			return false;
11793743bec6SJia Ding 		break;
11809236d838SLuis R. Rodriguez 	default:
11813d9d1d66SJohannes Berg 		WARN_ON_ONCE(1);
11829236d838SLuis R. Rodriguez 		return false;
11834ee3e063SBeni Lev 	}
11849236d838SLuis R. Rodriguez 
1185c7a6ee27SJohannes Berg 	/*
1186c7a6ee27SJohannes Berg 	 * TODO: What if there are only certain 80/160/80+80 MHz channels
1187c7a6ee27SJohannes Berg 	 *	 allowed by the driver, or only certain combinations?
1188c7a6ee27SJohannes Berg 	 *	 For 40 MHz the driver can set the NO_HT40 flags, but for
1189c7a6ee27SJohannes Berg 	 *	 80/160 MHz and in particular 80+80 MHz this isn't really
1190c7a6ee27SJohannes Berg 	 *	 feasible and we only have NO_80MHZ/NO_160MHZ so far but
1191c7a6ee27SJohannes Berg 	 *	 no way to cover 80+80 MHz or more complex restrictions.
1192c7a6ee27SJohannes Berg 	 *	 Note that such restrictions also need to be advertised to
1193c7a6ee27SJohannes Berg 	 *	 userspace, for example for P2P channel selection.
1194c7a6ee27SJohannes Berg 	 */
11953d9d1d66SJohannes Berg 
1196a6662dbaSJohannes Berg 	if (width > 20)
1197a6662dbaSJohannes Berg 		prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
1198a6662dbaSJohannes Berg 
11992f301ab2SSimon Wunderlich 	/* 5 and 10 MHz are only defined for the OFDM PHY */
12002f301ab2SSimon Wunderlich 	if (width < 20)
12012f301ab2SSimon Wunderlich 		prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
12022f301ab2SSimon Wunderlich 
12032f301ab2SSimon Wunderlich 
1204934f4c7dSThomas Pedersen 	if (!cfg80211_secondary_chans_ok(wiphy,
1205934f4c7dSThomas Pedersen 					 ieee80211_chandef_to_khz(chandef),
12069f5e8f6eSJohannes Berg 					 width, prohibited_flags))
12079f5e8f6eSJohannes Berg 		return false;
12089f5e8f6eSJohannes Berg 
12099f5e8f6eSJohannes Berg 	if (!chandef->center_freq2)
12109f5e8f6eSJohannes Berg 		return true;
1211934f4c7dSThomas Pedersen 	return cfg80211_secondary_chans_ok(wiphy,
1212934f4c7dSThomas Pedersen 					   MHZ_TO_KHZ(chandef->center_freq2),
12139f5e8f6eSJohannes Berg 					   width, prohibited_flags);
12149f5e8f6eSJohannes Berg }
12159f5e8f6eSJohannes Berg EXPORT_SYMBOL(cfg80211_chandef_usable);
12169f5e8f6eSJohannes Berg 
cfg80211_ir_permissive_check_wdev(enum nl80211_iftype iftype,struct wireless_dev * wdev,struct ieee80211_channel * chan)12177b0a0e3cSJohannes Berg static bool cfg80211_ir_permissive_check_wdev(enum nl80211_iftype iftype,
12187b0a0e3cSJohannes Berg 					      struct wireless_dev *wdev,
12197b0a0e3cSJohannes Berg 					      struct ieee80211_channel *chan)
12207b0a0e3cSJohannes Berg {
12217b0a0e3cSJohannes Berg 	struct ieee80211_channel *other_chan = NULL;
12227b0a0e3cSJohannes Berg 	unsigned int link_id;
12237b0a0e3cSJohannes Berg 	int r1, r2;
12247b0a0e3cSJohannes Berg 
12257b0a0e3cSJohannes Berg 	for_each_valid_link(wdev, link_id) {
12267b0a0e3cSJohannes Berg 		if (wdev->iftype == NL80211_IFTYPE_STATION &&
12277b0a0e3cSJohannes Berg 		    wdev->links[link_id].client.current_bss)
12287b0a0e3cSJohannes Berg 			other_chan = wdev->links[link_id].client.current_bss->pub.channel;
12297b0a0e3cSJohannes Berg 
12307b0a0e3cSJohannes Berg 		/*
12317b0a0e3cSJohannes Berg 		 * If a GO already operates on the same GO_CONCURRENT channel,
12327b0a0e3cSJohannes Berg 		 * this one (maybe the same one) can beacon as well. We allow
12337b0a0e3cSJohannes Berg 		 * the operation even if the station we relied on with
12347b0a0e3cSJohannes Berg 		 * GO_CONCURRENT is disconnected now. But then we must make sure
12357b0a0e3cSJohannes Berg 		 * we're not outdoor on an indoor-only channel.
12367b0a0e3cSJohannes Berg 		 */
12377b0a0e3cSJohannes Berg 		if (iftype == NL80211_IFTYPE_P2P_GO &&
12387b0a0e3cSJohannes Berg 		    wdev->iftype == NL80211_IFTYPE_P2P_GO &&
12397b0a0e3cSJohannes Berg 		    wdev->links[link_id].ap.beacon_interval &&
12407b0a0e3cSJohannes Berg 		    !(chan->flags & IEEE80211_CHAN_INDOOR_ONLY))
12417b0a0e3cSJohannes Berg 			other_chan = wdev->links[link_id].ap.chandef.chan;
12427b0a0e3cSJohannes Berg 
12437b0a0e3cSJohannes Berg 		if (!other_chan)
12447b0a0e3cSJohannes Berg 			continue;
12457b0a0e3cSJohannes Berg 
12467b0a0e3cSJohannes Berg 		if (chan == other_chan)
12477b0a0e3cSJohannes Berg 			return true;
12487b0a0e3cSJohannes Berg 
12497b0a0e3cSJohannes Berg 		if (chan->band != NL80211_BAND_5GHZ &&
12507b0a0e3cSJohannes Berg 		    chan->band != NL80211_BAND_6GHZ)
12517b0a0e3cSJohannes Berg 			continue;
12527b0a0e3cSJohannes Berg 
12537b0a0e3cSJohannes Berg 		r1 = cfg80211_get_unii(chan->center_freq);
12547b0a0e3cSJohannes Berg 		r2 = cfg80211_get_unii(other_chan->center_freq);
12557b0a0e3cSJohannes Berg 
12567b0a0e3cSJohannes Berg 		if (r1 != -EINVAL && r1 == r2) {
12577b0a0e3cSJohannes Berg 			/*
12587b0a0e3cSJohannes Berg 			 * At some locations channels 149-165 are considered a
12597b0a0e3cSJohannes Berg 			 * bundle, but at other locations, e.g., Indonesia,
12607b0a0e3cSJohannes Berg 			 * channels 149-161 are considered a bundle while
12617b0a0e3cSJohannes Berg 			 * channel 165 is left out and considered to be in a
12627b0a0e3cSJohannes Berg 			 * different bundle. Thus, in case that there is a
12637b0a0e3cSJohannes Berg 			 * station interface connected to an AP on channel 165,
12647b0a0e3cSJohannes Berg 			 * it is assumed that channels 149-161 are allowed for
12657b0a0e3cSJohannes Berg 			 * GO operations. However, having a station interface
12667b0a0e3cSJohannes Berg 			 * connected to an AP on channels 149-161, does not
12677b0a0e3cSJohannes Berg 			 * allow GO operation on channel 165.
12687b0a0e3cSJohannes Berg 			 */
12697b0a0e3cSJohannes Berg 			if (chan->center_freq == 5825 &&
12707b0a0e3cSJohannes Berg 			    other_chan->center_freq != 5825)
12717b0a0e3cSJohannes Berg 				continue;
12727b0a0e3cSJohannes Berg 			return true;
12737b0a0e3cSJohannes Berg 		}
12747b0a0e3cSJohannes Berg 	}
12757b0a0e3cSJohannes Berg 
12767b0a0e3cSJohannes Berg 	return false;
12777b0a0e3cSJohannes Berg }
12787b0a0e3cSJohannes Berg 
1279174e0cd2SIlan Peer /*
128006f207fcSArik Nemtsov  * Check if the channel can be used under permissive conditions mandated by
128106f207fcSArik Nemtsov  * some regulatory bodies, i.e., the channel is marked with
128206f207fcSArik Nemtsov  * IEEE80211_CHAN_IR_CONCURRENT and there is an additional station interface
1283174e0cd2SIlan Peer  * associated to an AP on the same channel or on the same UNII band
1284174e0cd2SIlan Peer  * (assuming that the AP is an authorized master).
128506f207fcSArik Nemtsov  * In addition allow operation on a channel on which indoor operation is
1286c8866e55SIlan Peer  * allowed, iff we are currently operating in an indoor environment.
1287174e0cd2SIlan Peer  */
cfg80211_ir_permissive_chan(struct wiphy * wiphy,enum nl80211_iftype iftype,struct ieee80211_channel * chan)128806f207fcSArik Nemtsov static bool cfg80211_ir_permissive_chan(struct wiphy *wiphy,
128906f207fcSArik Nemtsov 					enum nl80211_iftype iftype,
1290174e0cd2SIlan Peer 					struct ieee80211_channel *chan)
12919f5e8f6eSJohannes Berg {
1292be69c24aSAvraham Stern 	struct wireless_dev *wdev;
129306f207fcSArik Nemtsov 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
1294174e0cd2SIlan Peer 
1295a05829a7SJohannes Berg 	lockdep_assert_held(&rdev->wiphy.mtx);
1296174e0cd2SIlan Peer 
129797f2645fSMasahiro Yamada 	if (!IS_ENABLED(CONFIG_CFG80211_REG_RELAX_NO_IR) ||
1298c8866e55SIlan Peer 	    !(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR))
1299c8866e55SIlan Peer 		return false;
1300c8866e55SIlan Peer 
130106f207fcSArik Nemtsov 	/* only valid for GO and TDLS off-channel (station/p2p-CL) */
130206f207fcSArik Nemtsov 	if (iftype != NL80211_IFTYPE_P2P_GO &&
130306f207fcSArik Nemtsov 	    iftype != NL80211_IFTYPE_STATION &&
130406f207fcSArik Nemtsov 	    iftype != NL80211_IFTYPE_P2P_CLIENT)
130506f207fcSArik Nemtsov 		return false;
130606f207fcSArik Nemtsov 
1307c8866e55SIlan Peer 	if (regulatory_indoor_allowed() &&
1308c8866e55SIlan Peer 	    (chan->flags & IEEE80211_CHAN_INDOOR_ONLY))
1309c8866e55SIlan Peer 		return true;
1310c8866e55SIlan Peer 
131106f207fcSArik Nemtsov 	if (!(chan->flags & IEEE80211_CHAN_IR_CONCURRENT))
1312174e0cd2SIlan Peer 		return false;
1313174e0cd2SIlan Peer 
1314174e0cd2SIlan Peer 	/*
1315174e0cd2SIlan Peer 	 * Generally, it is possible to rely on another device/driver to allow
131606f207fcSArik Nemtsov 	 * the IR concurrent relaxation, however, since the device can further
1317174e0cd2SIlan Peer 	 * enforce the relaxation (by doing a similar verifications as this),
1318174e0cd2SIlan Peer 	 * and thus fail the GO instantiation, consider only the interfaces of
1319174e0cd2SIlan Peer 	 * the current registered device.
1320174e0cd2SIlan Peer 	 */
132153873f13SJohannes Berg 	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
13227b0a0e3cSJohannes Berg 		bool ret;
1323174e0cd2SIlan Peer 
1324be69c24aSAvraham Stern 		wdev_lock(wdev);
13257b0a0e3cSJohannes Berg 		ret = cfg80211_ir_permissive_check_wdev(iftype, wdev, chan);
1326be69c24aSAvraham Stern 		wdev_unlock(wdev);
1327174e0cd2SIlan Peer 
13287b0a0e3cSJohannes Berg 		if (ret)
13297b0a0e3cSJohannes Berg 			return ret;
133046d53724SIlan Peer 	}
1331174e0cd2SIlan Peer 
1332174e0cd2SIlan Peer 	return false;
1333174e0cd2SIlan Peer }
1334174e0cd2SIlan Peer 
_cfg80211_reg_can_beacon(struct wiphy * wiphy,struct cfg80211_chan_def * chandef,enum nl80211_iftype iftype,bool check_no_ir)1335923b352fSArik Nemtsov static bool _cfg80211_reg_can_beacon(struct wiphy *wiphy,
1336174e0cd2SIlan Peer 				     struct cfg80211_chan_def *chandef,
1337923b352fSArik Nemtsov 				     enum nl80211_iftype iftype,
1338923b352fSArik Nemtsov 				     bool check_no_ir)
1339174e0cd2SIlan Peer {
13409f5e8f6eSJohannes Berg 	bool res;
13416bc54fbcSJanusz Dziedzic 	u32 prohibited_flags = IEEE80211_CHAN_DISABLED |
13426bc54fbcSJanusz Dziedzic 			       IEEE80211_CHAN_RADAR;
13439f5e8f6eSJohannes Berg 
1344923b352fSArik Nemtsov 	trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype, check_no_ir);
1345174e0cd2SIlan Peer 
1346923b352fSArik Nemtsov 	if (check_no_ir)
1347174e0cd2SIlan Peer 		prohibited_flags |= IEEE80211_CHAN_NO_IR;
13489f5e8f6eSJohannes Berg 
134900ec75fcSLuciano Coelho 	if (cfg80211_chandef_dfs_required(wiphy, chandef, iftype) > 0 &&
13506bc54fbcSJanusz Dziedzic 	    cfg80211_chandef_dfs_available(wiphy, chandef)) {
13516bc54fbcSJanusz Dziedzic 		/* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */
13526bc54fbcSJanusz Dziedzic 		prohibited_flags = IEEE80211_CHAN_DISABLED;
13536bc54fbcSJanusz Dziedzic 	}
13546bc54fbcSJanusz Dziedzic 
13556bc54fbcSJanusz Dziedzic 	res = cfg80211_chandef_usable(wiphy, chandef, prohibited_flags);
13563d9d1d66SJohannes Berg 
13573d9d1d66SJohannes Berg 	trace_cfg80211_return_bool(res);
13583d9d1d66SJohannes Berg 	return res;
13599236d838SLuis R. Rodriguez }
1360923b352fSArik Nemtsov 
cfg80211_reg_can_beacon(struct wiphy * wiphy,struct cfg80211_chan_def * chandef,enum nl80211_iftype iftype)1361923b352fSArik Nemtsov bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
1362923b352fSArik Nemtsov 			     struct cfg80211_chan_def *chandef,
1363923b352fSArik Nemtsov 			     enum nl80211_iftype iftype)
1364923b352fSArik Nemtsov {
1365923b352fSArik Nemtsov 	return _cfg80211_reg_can_beacon(wiphy, chandef, iftype, true);
1366923b352fSArik Nemtsov }
1367683b6d3bSJohannes Berg EXPORT_SYMBOL(cfg80211_reg_can_beacon);
13689236d838SLuis R. Rodriguez 
cfg80211_reg_can_beacon_relax(struct wiphy * wiphy,struct cfg80211_chan_def * chandef,enum nl80211_iftype iftype)1369923b352fSArik Nemtsov bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy,
1370923b352fSArik Nemtsov 				   struct cfg80211_chan_def *chandef,
1371923b352fSArik Nemtsov 				   enum nl80211_iftype iftype)
1372923b352fSArik Nemtsov {
1373a05829a7SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
1374923b352fSArik Nemtsov 	bool check_no_ir;
1375923b352fSArik Nemtsov 
1376a05829a7SJohannes Berg 	lockdep_assert_held(&rdev->wiphy.mtx);
1377923b352fSArik Nemtsov 
1378923b352fSArik Nemtsov 	/*
1379923b352fSArik Nemtsov 	 * Under certain conditions suggested by some regulatory bodies a
1380923b352fSArik Nemtsov 	 * GO/STA can IR on channels marked with IEEE80211_NO_IR. Set this flag
1381923b352fSArik Nemtsov 	 * only if such relaxations are not enabled and the conditions are not
1382923b352fSArik Nemtsov 	 * met.
1383923b352fSArik Nemtsov 	 */
1384923b352fSArik Nemtsov 	check_no_ir = !cfg80211_ir_permissive_chan(wiphy, iftype,
1385923b352fSArik Nemtsov 						   chandef->chan);
1386923b352fSArik Nemtsov 
1387923b352fSArik Nemtsov 	return _cfg80211_reg_can_beacon(wiphy, chandef, iftype, check_no_ir);
1388923b352fSArik Nemtsov }
1389923b352fSArik Nemtsov EXPORT_SYMBOL(cfg80211_reg_can_beacon_relax);
1390923b352fSArik Nemtsov 
cfg80211_set_monitor_channel(struct cfg80211_registered_device * rdev,struct cfg80211_chan_def * chandef)1391e8c9bd5bSJohannes Berg int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
1392683b6d3bSJohannes Berg 				 struct cfg80211_chan_def *chandef)
139359bbb6f7SJohannes Berg {
1394e8c9bd5bSJohannes Berg 	if (!rdev->ops->set_monitor_channel)
139559bbb6f7SJohannes Berg 		return -EOPNOTSUPP;
13964f03c1edSMichal Kazior 	if (!cfg80211_has_monitors_only(rdev))
13974f03c1edSMichal Kazior 		return -EBUSY;
139859bbb6f7SJohannes Berg 
1399683b6d3bSJohannes Berg 	return rdev_set_monitor_channel(rdev, chandef);
140059bbb6f7SJohannes Berg }
140126ab9a0cSMichal Kazior 
cfg80211_any_usable_channels(struct wiphy * wiphy,unsigned long sband_mask,u32 prohibited_flags)1402be989891SJohannes Berg bool cfg80211_any_usable_channels(struct wiphy *wiphy,
1403be989891SJohannes Berg 				  unsigned long sband_mask,
1404be989891SJohannes Berg 				  u32 prohibited_flags)
1405be989891SJohannes Berg {
1406be989891SJohannes Berg 	int idx;
1407be989891SJohannes Berg 
1408be989891SJohannes Berg 	prohibited_flags |= IEEE80211_CHAN_DISABLED;
1409be989891SJohannes Berg 
1410be989891SJohannes Berg 	for_each_set_bit(idx, &sband_mask, NUM_NL80211_BANDS) {
1411be989891SJohannes Berg 		struct ieee80211_supported_band *sband = wiphy->bands[idx];
1412be989891SJohannes Berg 		int chanidx;
1413be989891SJohannes Berg 
1414be989891SJohannes Berg 		if (!sband)
1415be989891SJohannes Berg 			continue;
1416be989891SJohannes Berg 
1417be989891SJohannes Berg 		for (chanidx = 0; chanidx < sband->n_channels; chanidx++) {
1418be989891SJohannes Berg 			struct ieee80211_channel *chan;
1419be989891SJohannes Berg 
1420be989891SJohannes Berg 			chan = &sband->channels[chanidx];
1421be989891SJohannes Berg 
1422be989891SJohannes Berg 			if (chan->flags & prohibited_flags)
1423be989891SJohannes Berg 				continue;
1424be989891SJohannes Berg 
1425be989891SJohannes Berg 			return true;
1426be989891SJohannes Berg 		}
1427be989891SJohannes Berg 	}
1428be989891SJohannes Berg 
1429be989891SJohannes Berg 	return false;
1430be989891SJohannes Berg }
1431be989891SJohannes Berg EXPORT_SYMBOL(cfg80211_any_usable_channels);
14327b0a0e3cSJohannes Berg 
wdev_chandef(struct wireless_dev * wdev,unsigned int link_id)14337b0a0e3cSJohannes Berg struct cfg80211_chan_def *wdev_chandef(struct wireless_dev *wdev,
14347b0a0e3cSJohannes Berg 				       unsigned int link_id)
14357b0a0e3cSJohannes Berg {
143631177127SJohannes Berg 	/*
143731177127SJohannes Berg 	 * We need to sort out the locking here - in some cases
143831177127SJohannes Berg 	 * where we get here we really just don't care (yet)
143931177127SJohannes Berg 	 * about the valid links, but in others we do. But we
144031177127SJohannes Berg 	 * get here with various driver cases, so we cannot
144131177127SJohannes Berg 	 * easily require the wdev mutex.
144231177127SJohannes Berg 	 */
144331177127SJohannes Berg 	if (link_id || wdev->valid_links & BIT(0)) {
14447b0a0e3cSJohannes Berg 		ASSERT_WDEV_LOCK(wdev);
144531177127SJohannes Berg 		WARN_ON(!(wdev->valid_links & BIT(link_id)));
144631177127SJohannes Berg 	}
14477b0a0e3cSJohannes Berg 
14487b0a0e3cSJohannes Berg 	switch (wdev->iftype) {
14497b0a0e3cSJohannes Berg 	case NL80211_IFTYPE_MESH_POINT:
14507b0a0e3cSJohannes Berg 		return &wdev->u.mesh.chandef;
14517b0a0e3cSJohannes Berg 	case NL80211_IFTYPE_ADHOC:
14527b0a0e3cSJohannes Berg 		return &wdev->u.ibss.chandef;
14537b0a0e3cSJohannes Berg 	case NL80211_IFTYPE_OCB:
14547b0a0e3cSJohannes Berg 		return &wdev->u.ocb.chandef;
14557b0a0e3cSJohannes Berg 	case NL80211_IFTYPE_AP:
14567b0a0e3cSJohannes Berg 	case NL80211_IFTYPE_P2P_GO:
14577b0a0e3cSJohannes Berg 		return &wdev->links[link_id].ap.chandef;
14587b0a0e3cSJohannes Berg 	default:
14597b0a0e3cSJohannes Berg 		return NULL;
14607b0a0e3cSJohannes Berg 	}
14617b0a0e3cSJohannes Berg }
14627b0a0e3cSJohannes Berg EXPORT_SYMBOL(wdev_chandef);
1463*b25413feSAloka Dixit 
1464*b25413feSAloka Dixit struct cfg80211_per_bw_puncturing_values {
1465*b25413feSAloka Dixit 	u8 len;
1466*b25413feSAloka Dixit 	const u16 *valid_values;
1467*b25413feSAloka Dixit };
1468*b25413feSAloka Dixit 
1469*b25413feSAloka Dixit static const u16 puncturing_values_80mhz[] = {
1470*b25413feSAloka Dixit 	0x8, 0x4, 0x2, 0x1
1471*b25413feSAloka Dixit };
1472*b25413feSAloka Dixit 
1473*b25413feSAloka Dixit static const u16 puncturing_values_160mhz[] = {
1474*b25413feSAloka Dixit 	 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1, 0xc0, 0x30, 0xc, 0x3
1475*b25413feSAloka Dixit };
1476*b25413feSAloka Dixit 
1477*b25413feSAloka Dixit static const u16 puncturing_values_320mhz[] = {
1478*b25413feSAloka Dixit 	0xc000, 0x3000, 0xc00, 0x300, 0xc0, 0x30, 0xc, 0x3, 0xf000, 0xf00,
1479*b25413feSAloka Dixit 	0xf0, 0xf, 0xfc00, 0xf300, 0xf0c0, 0xf030, 0xf00c, 0xf003, 0xc00f,
1480*b25413feSAloka Dixit 	0x300f, 0xc0f, 0x30f, 0xcf, 0x3f
1481*b25413feSAloka Dixit };
1482*b25413feSAloka Dixit 
1483*b25413feSAloka Dixit #define CFG80211_PER_BW_VALID_PUNCTURING_VALUES(_bw) \
1484*b25413feSAloka Dixit 	{ \
1485*b25413feSAloka Dixit 		.len = ARRAY_SIZE(puncturing_values_ ## _bw ## mhz), \
1486*b25413feSAloka Dixit 		.valid_values = puncturing_values_ ## _bw ## mhz \
1487*b25413feSAloka Dixit 	}
1488*b25413feSAloka Dixit 
1489*b25413feSAloka Dixit static const struct cfg80211_per_bw_puncturing_values per_bw_puncturing[] = {
1490*b25413feSAloka Dixit 	CFG80211_PER_BW_VALID_PUNCTURING_VALUES(80),
1491*b25413feSAloka Dixit 	CFG80211_PER_BW_VALID_PUNCTURING_VALUES(160),
1492*b25413feSAloka Dixit 	CFG80211_PER_BW_VALID_PUNCTURING_VALUES(320)
1493*b25413feSAloka Dixit };
1494*b25413feSAloka Dixit 
cfg80211_valid_disable_subchannel_bitmap(u16 * bitmap,const struct cfg80211_chan_def * chandef)1495*b25413feSAloka Dixit bool cfg80211_valid_disable_subchannel_bitmap(u16 *bitmap,
1496*b25413feSAloka Dixit 					      const struct cfg80211_chan_def *chandef)
1497*b25413feSAloka Dixit {
1498*b25413feSAloka Dixit 	u32 idx, i, start_freq;
1499*b25413feSAloka Dixit 
1500*b25413feSAloka Dixit 	switch (chandef->width) {
1501*b25413feSAloka Dixit 	case NL80211_CHAN_WIDTH_80:
1502*b25413feSAloka Dixit 		idx = 0;
1503*b25413feSAloka Dixit 		start_freq = chandef->center_freq1 - 40;
1504*b25413feSAloka Dixit 		break;
1505*b25413feSAloka Dixit 	case NL80211_CHAN_WIDTH_160:
1506*b25413feSAloka Dixit 		idx = 1;
1507*b25413feSAloka Dixit 		start_freq = chandef->center_freq1 - 80;
1508*b25413feSAloka Dixit 		break;
1509*b25413feSAloka Dixit 	case NL80211_CHAN_WIDTH_320:
1510*b25413feSAloka Dixit 		idx = 2;
1511*b25413feSAloka Dixit 		start_freq = chandef->center_freq1 - 160;
1512*b25413feSAloka Dixit 		break;
1513*b25413feSAloka Dixit 	default:
1514*b25413feSAloka Dixit 		*bitmap = 0;
1515*b25413feSAloka Dixit 		break;
1516*b25413feSAloka Dixit 	}
1517*b25413feSAloka Dixit 
1518*b25413feSAloka Dixit 	if (!*bitmap)
1519*b25413feSAloka Dixit 		return true;
1520*b25413feSAloka Dixit 
1521*b25413feSAloka Dixit 	/* check if primary channel is punctured */
1522*b25413feSAloka Dixit 	if (*bitmap & (u16)BIT((chandef->chan->center_freq - start_freq) / 20))
1523*b25413feSAloka Dixit 		return false;
1524*b25413feSAloka Dixit 
1525*b25413feSAloka Dixit 	for (i = 0; i < per_bw_puncturing[idx].len; i++)
1526*b25413feSAloka Dixit 		if (per_bw_puncturing[idx].valid_values[i] == *bitmap)
1527*b25413feSAloka Dixit 			return true;
1528*b25413feSAloka Dixit 
1529*b25413feSAloka Dixit 	return false;
1530*b25413feSAloka Dixit }
1531*b25413feSAloka Dixit EXPORT_SYMBOL(cfg80211_valid_disable_subchannel_bitmap);
1532