[go: nahoru, domu]

regd.c revision 8a3b6c800a993bbc7559bcfa77a34e4fc633cf0e
1/*
2 * Copyright (c) 2008-2009 Atheros Communications Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
19#include <linux/kernel.h>
20#include <linux/export.h>
21#include <net/cfg80211.h>
22#include <net/mac80211.h>
23#include "regd.h"
24#include "regd_common.h"
25
26static int __ath_regd_init(struct ath_regulatory *reg);
27
28/*
29 * This is a set of common rules used by our world regulatory domains.
30 * We have 12 world regulatory domains. To save space we consolidate
31 * the regulatory domains in 5 structures by frequency and change
32 * the flags on our reg_notifier() on a case by case basis.
33 */
34
35/* Only these channels all allow active scan on all world regulatory domains */
36#define ATH9K_2GHZ_CH01_11	REG_RULE(2412-10, 2462+10, 40, 0, 20, 0)
37
38/* We enable active scan on these a case by case basis by regulatory domain */
39#define ATH9K_2GHZ_CH12_13	REG_RULE(2467-10, 2472+10, 40, 0, 20,\
40					NL80211_RRF_PASSIVE_SCAN)
41#define ATH9K_2GHZ_CH14		REG_RULE(2484-10, 2484+10, 40, 0, 20,\
42				NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM)
43
44/* We allow IBSS on these on a case by case basis by regulatory domain */
45#define ATH9K_5GHZ_5150_5350	REG_RULE(5150-10, 5350+10, 80, 0, 30,\
46				NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
47#define ATH9K_5GHZ_5470_5850	REG_RULE(5470-10, 5850+10, 80, 0, 30,\
48				NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
49#define ATH9K_5GHZ_5725_5850	REG_RULE(5725-10, 5850+10, 80, 0, 30,\
50				NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
51
52#define ATH9K_2GHZ_ALL		ATH9K_2GHZ_CH01_11, \
53				ATH9K_2GHZ_CH12_13, \
54				ATH9K_2GHZ_CH14
55
56#define ATH9K_5GHZ_ALL		ATH9K_5GHZ_5150_5350, \
57				ATH9K_5GHZ_5470_5850
58
59/* This one skips what we call "mid band" */
60#define ATH9K_5GHZ_NO_MIDBAND	ATH9K_5GHZ_5150_5350, \
61				ATH9K_5GHZ_5725_5850
62
63/* Can be used for:
64 * 0x60, 0x61, 0x62 */
65static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = {
66	.n_reg_rules = 5,
67	.alpha2 =  "99",
68	.reg_rules = {
69		ATH9K_2GHZ_ALL,
70		ATH9K_5GHZ_ALL,
71	}
72};
73
74/* Can be used by 0x63 and 0x65 */
75static const struct ieee80211_regdomain ath_world_regdom_63_65 = {
76	.n_reg_rules = 4,
77	.alpha2 =  "99",
78	.reg_rules = {
79		ATH9K_2GHZ_CH01_11,
80		ATH9K_2GHZ_CH12_13,
81		ATH9K_5GHZ_NO_MIDBAND,
82	}
83};
84
85/* Can be used by 0x64 only */
86static const struct ieee80211_regdomain ath_world_regdom_64 = {
87	.n_reg_rules = 3,
88	.alpha2 =  "99",
89	.reg_rules = {
90		ATH9K_2GHZ_CH01_11,
91		ATH9K_5GHZ_NO_MIDBAND,
92	}
93};
94
95/* Can be used by 0x66 and 0x69 */
96static const struct ieee80211_regdomain ath_world_regdom_66_69 = {
97	.n_reg_rules = 3,
98	.alpha2 =  "99",
99	.reg_rules = {
100		ATH9K_2GHZ_CH01_11,
101		ATH9K_5GHZ_ALL,
102	}
103};
104
105/* Can be used by 0x67, 0x68, 0x6A and 0x6C */
106static const struct ieee80211_regdomain ath_world_regdom_67_68_6A_6C = {
107	.n_reg_rules = 4,
108	.alpha2 =  "99",
109	.reg_rules = {
110		ATH9K_2GHZ_CH01_11,
111		ATH9K_2GHZ_CH12_13,
112		ATH9K_5GHZ_ALL,
113	}
114};
115
116static inline bool is_wwr_sku(u16 regd)
117{
118	return ((regd & COUNTRY_ERD_FLAG) != COUNTRY_ERD_FLAG) &&
119		(((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) ||
120		(regd == WORLD));
121}
122
123static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
124{
125	return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
126}
127
128bool ath_is_world_regd(struct ath_regulatory *reg)
129{
130	return is_wwr_sku(ath_regd_get_eepromRD(reg));
131}
132EXPORT_SYMBOL(ath_is_world_regd);
133
134static const struct ieee80211_regdomain *ath_default_world_regdomain(void)
135{
136	/* this is the most restrictive */
137	return &ath_world_regdom_64;
138}
139
140static const struct
141ieee80211_regdomain *ath_world_regdomain(struct ath_regulatory *reg)
142{
143	switch (reg->regpair->regDmnEnum) {
144	case 0x60:
145	case 0x61:
146	case 0x62:
147		return &ath_world_regdom_60_61_62;
148	case 0x63:
149	case 0x65:
150		return &ath_world_regdom_63_65;
151	case 0x64:
152		return &ath_world_regdom_64;
153	case 0x66:
154	case 0x69:
155		return &ath_world_regdom_66_69;
156	case 0x67:
157	case 0x68:
158	case 0x6A:
159	case 0x6C:
160		return &ath_world_regdom_67_68_6A_6C;
161	default:
162		WARN_ON(1);
163		return ath_default_world_regdomain();
164	}
165}
166
167bool ath_is_49ghz_allowed(u16 regdomain)
168{
169	/* possibly more */
170	return regdomain == MKK9_MKKC;
171}
172EXPORT_SYMBOL(ath_is_49ghz_allowed);
173
174/* Frequency is one where radar detection is required */
175static bool ath_is_radar_freq(u16 center_freq)
176{
177	return (center_freq >= 5260 && center_freq <= 5700);
178}
179
180/*
181 * N.B: These exception rules do not apply radar freqs.
182 *
183 * - We enable adhoc (or beaconing) if allowed by 11d
184 * - We enable active scan if the channel is allowed by 11d
185 * - If no country IE has been processed and a we determine we have
186 *   received a beacon on a channel we can enable active scan and
187 *   adhoc (or beaconing).
188 */
189static void
190ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
191			      enum nl80211_reg_initiator initiator)
192{
193	enum ieee80211_band band;
194	struct ieee80211_supported_band *sband;
195	const struct ieee80211_reg_rule *reg_rule;
196	struct ieee80211_channel *ch;
197	unsigned int i;
198
199	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
200
201		if (!wiphy->bands[band])
202			continue;
203
204		sband = wiphy->bands[band];
205
206		for (i = 0; i < sband->n_channels; i++) {
207
208			ch = &sband->channels[i];
209
210			if (ath_is_radar_freq(ch->center_freq) ||
211			    (ch->flags & IEEE80211_CHAN_RADAR))
212				continue;
213
214			if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
215				reg_rule = freq_reg_info(wiphy, ch->center_freq);
216				if (IS_ERR(reg_rule))
217					continue;
218				/*
219				 * If 11d had a rule for this channel ensure
220				 * we enable adhoc/beaconing if it allows us to
221				 * use it. Note that we would have disabled it
222				 * by applying our static world regdomain by
223				 * default during init, prior to calling our
224				 * regulatory_hint().
225				 */
226				if (!(reg_rule->flags &
227				    NL80211_RRF_NO_IBSS))
228					ch->flags &=
229					  ~IEEE80211_CHAN_NO_IBSS;
230				if (!(reg_rule->flags &
231				    NL80211_RRF_PASSIVE_SCAN))
232					ch->flags &=
233					  ~IEEE80211_CHAN_PASSIVE_SCAN;
234			} else {
235				if (ch->beacon_found)
236					ch->flags &= ~(IEEE80211_CHAN_NO_IBSS |
237					  IEEE80211_CHAN_PASSIVE_SCAN);
238			}
239		}
240	}
241
242}
243
244/* Allows active scan scan on Ch 12 and 13 */
245static void
246ath_reg_apply_active_scan_flags(struct wiphy *wiphy,
247				enum nl80211_reg_initiator initiator)
248{
249	struct ieee80211_supported_band *sband;
250	struct ieee80211_channel *ch;
251	const struct ieee80211_reg_rule *reg_rule;
252
253	sband = wiphy->bands[IEEE80211_BAND_2GHZ];
254	if (!sband)
255		return;
256
257	/*
258	 * If no country IE has been received always enable active scan
259	 * on these channels. This is only done for specific regulatory SKUs
260	 */
261	if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
262		ch = &sband->channels[11]; /* CH 12 */
263		if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
264			ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
265		ch = &sband->channels[12]; /* CH 13 */
266		if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
267			ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
268		return;
269	}
270
271	/*
272	 * If a country IE has been received check its rule for this
273	 * channel first before enabling active scan. The passive scan
274	 * would have been enforced by the initial processing of our
275	 * custom regulatory domain.
276	 */
277
278	ch = &sband->channels[11]; /* CH 12 */
279	reg_rule = freq_reg_info(wiphy, ch->center_freq);
280	if (!IS_ERR(reg_rule)) {
281		if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
282			if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
283				ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
284	}
285
286	ch = &sband->channels[12]; /* CH 13 */
287	reg_rule = freq_reg_info(wiphy, ch->center_freq);
288	if (!IS_ERR(reg_rule)) {
289		if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
290			if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
291				ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
292	}
293}
294
295/* Always apply Radar/DFS rules on freq range 5260 MHz - 5700 MHz */
296static void ath_reg_apply_radar_flags(struct wiphy *wiphy)
297{
298	struct ieee80211_supported_band *sband;
299	struct ieee80211_channel *ch;
300	unsigned int i;
301
302	if (!wiphy->bands[IEEE80211_BAND_5GHZ])
303		return;
304
305	sband = wiphy->bands[IEEE80211_BAND_5GHZ];
306
307	for (i = 0; i < sband->n_channels; i++) {
308		ch = &sband->channels[i];
309		if (!ath_is_radar_freq(ch->center_freq))
310			continue;
311		/* We always enable radar detection/DFS on this
312		 * frequency range. Additionally we also apply on
313		 * this frequency range:
314		 * - If STA mode does not yet have DFS supports disable
315		 *   active scanning
316		 * - If adhoc mode does not support DFS yet then
317		 *   disable adhoc in the frequency.
318		 * - If AP mode does not yet support radar detection/DFS
319		 *   do not allow AP mode
320		 */
321		if (!(ch->flags & IEEE80211_CHAN_DISABLED))
322			ch->flags |= IEEE80211_CHAN_RADAR |
323				     IEEE80211_CHAN_NO_IBSS |
324				     IEEE80211_CHAN_PASSIVE_SCAN;
325	}
326}
327
328static void ath_reg_apply_world_flags(struct wiphy *wiphy,
329				      enum nl80211_reg_initiator initiator,
330				      struct ath_regulatory *reg)
331{
332	switch (reg->regpair->regDmnEnum) {
333	case 0x60:
334	case 0x63:
335	case 0x66:
336	case 0x67:
337	case 0x6C:
338		ath_reg_apply_beaconing_flags(wiphy, initiator);
339		break;
340	case 0x68:
341		ath_reg_apply_beaconing_flags(wiphy, initiator);
342		ath_reg_apply_active_scan_flags(wiphy, initiator);
343		break;
344	}
345}
346
347static u16 ath_regd_find_country_by_name(char *alpha2)
348{
349	unsigned int i;
350
351	for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
352		if (!memcmp(allCountries[i].isoName, alpha2, 2))
353			return allCountries[i].countryCode;
354	}
355
356	return -1;
357}
358
359static int __ath_reg_dyn_country(struct wiphy *wiphy,
360				 struct ath_regulatory *reg,
361				 struct regulatory_request *request)
362{
363	u16 country_code;
364
365	if (!ath_is_world_regd(reg))
366		return -EINVAL;
367
368	country_code = ath_regd_find_country_by_name(request->alpha2);
369	if (country_code == (u16) -1)
370		return -EINVAL;
371
372	reg->current_rd = COUNTRY_ERD_FLAG;
373	reg->current_rd |= country_code;
374
375	__ath_regd_init(reg);
376
377	ath_reg_apply_world_flags(wiphy, request->initiator, reg);
378
379	return 0;
380}
381
382static void ath_reg_dyn_country(struct wiphy *wiphy,
383				struct ath_regulatory *reg,
384				struct regulatory_request *request)
385{
386	if (__ath_reg_dyn_country(wiphy, reg, request))
387		return;
388
389	printk(KERN_DEBUG "ath: regdomain 0x%0x "
390			  "dynamically updated by %s\n",
391	       reg->current_rd,
392	       reg_initiator_name(request->initiator));
393}
394
395void ath_reg_notifier_apply(struct wiphy *wiphy,
396			    struct regulatory_request *request,
397			    struct ath_regulatory *reg)
398{
399	struct ath_common *common = container_of(reg, struct ath_common,
400						 regulatory);
401	/* We always apply this */
402	ath_reg_apply_radar_flags(wiphy);
403
404	/*
405	 * This would happen when we have sent a custom regulatory request
406	 * a world regulatory domain and the scheduler hasn't yet processed
407	 * any pending requests in the queue.
408	 */
409	if (!request)
410		return;
411
412	switch (request->initiator) {
413	case NL80211_REGDOM_SET_BY_CORE:
414		/*
415		 * If common->reg_world_copy is world roaming it means we *were*
416		 * world roaming... so we now have to restore that data.
417		 */
418		if (!ath_is_world_regd(&common->reg_world_copy))
419			break;
420
421		memcpy(reg, &common->reg_world_copy,
422		       sizeof(struct ath_regulatory));
423		break;
424	case NL80211_REGDOM_SET_BY_DRIVER:
425	case NL80211_REGDOM_SET_BY_USER:
426		break;
427	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
428		ath_reg_dyn_country(wiphy, reg, request);
429		break;
430	}
431}
432EXPORT_SYMBOL(ath_reg_notifier_apply);
433
434static bool ath_regd_is_eeprom_valid(struct ath_regulatory *reg)
435{
436	u16 rd = ath_regd_get_eepromRD(reg);
437	int i;
438
439	if (rd & COUNTRY_ERD_FLAG) {
440		/* EEPROM value is a country code */
441		u16 cc = rd & ~COUNTRY_ERD_FLAG;
442		printk(KERN_DEBUG
443		       "ath: EEPROM indicates we should expect "
444			"a country code\n");
445		for (i = 0; i < ARRAY_SIZE(allCountries); i++)
446			if (allCountries[i].countryCode == cc)
447				return true;
448	} else {
449		/* EEPROM value is a regpair value */
450		if (rd != CTRY_DEFAULT)
451			printk(KERN_DEBUG "ath: EEPROM indicates we "
452			       "should expect a direct regpair map\n");
453		for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
454			if (regDomainPairs[i].regDmnEnum == rd)
455				return true;
456	}
457	printk(KERN_DEBUG
458		 "ath: invalid regulatory domain/country code 0x%x\n", rd);
459	return false;
460}
461
462/* EEPROM country code to regpair mapping */
463static struct country_code_to_enum_rd*
464ath_regd_find_country(u16 countryCode)
465{
466	int i;
467
468	for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
469		if (allCountries[i].countryCode == countryCode)
470			return &allCountries[i];
471	}
472	return NULL;
473}
474
475/* EEPROM rd code to regpair mapping */
476static struct country_code_to_enum_rd*
477ath_regd_find_country_by_rd(int regdmn)
478{
479	int i;
480
481	for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
482		if (allCountries[i].regDmnEnum == regdmn)
483			return &allCountries[i];
484	}
485	return NULL;
486}
487
488/* Returns the map of the EEPROM set RD to a country code */
489static u16 ath_regd_get_default_country(u16 rd)
490{
491	if (rd & COUNTRY_ERD_FLAG) {
492		struct country_code_to_enum_rd *country = NULL;
493		u16 cc = rd & ~COUNTRY_ERD_FLAG;
494
495		country = ath_regd_find_country(cc);
496		if (country != NULL)
497			return cc;
498	}
499
500	return CTRY_DEFAULT;
501}
502
503static struct reg_dmn_pair_mapping*
504ath_get_regpair(int regdmn)
505{
506	int i;
507
508	if (regdmn == NO_ENUMRD)
509		return NULL;
510	for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
511		if (regDomainPairs[i].regDmnEnum == regdmn)
512			return &regDomainPairs[i];
513	}
514	return NULL;
515}
516
517static int
518ath_regd_init_wiphy(struct ath_regulatory *reg,
519		    struct wiphy *wiphy,
520		    void (*reg_notifier)(struct wiphy *wiphy,
521					 struct regulatory_request *request))
522{
523	const struct ieee80211_regdomain *regd;
524
525	wiphy->reg_notifier = reg_notifier;
526	wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
527
528	if (ath_is_world_regd(reg)) {
529		/*
530		 * Anything applied here (prior to wiphy registration) gets
531		 * saved on the wiphy orig_* parameters
532		 */
533		regd = ath_world_regdomain(reg);
534		wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
535	} else {
536		/*
537		 * This gets applied in the case of the absence of CRDA,
538		 * it's our own custom world regulatory domain, similar to
539		 * cfg80211's but we enable passive scanning.
540		 */
541		regd = ath_default_world_regdomain();
542	}
543	wiphy_apply_custom_regulatory(wiphy, regd);
544	ath_reg_apply_radar_flags(wiphy);
545	ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg);
546	return 0;
547}
548
549/*
550 * Some users have reported their EEPROM programmed with
551 * 0x8000 set, this is not a supported regulatory domain
552 * but since we have more than one user with it we need
553 * a solution for them. We default to 0x64, which is the
554 * default Atheros world regulatory domain.
555 */
556static void ath_regd_sanitize(struct ath_regulatory *reg)
557{
558	if (reg->current_rd != COUNTRY_ERD_FLAG)
559		return;
560	printk(KERN_DEBUG "ath: EEPROM regdomain sanitized\n");
561	reg->current_rd = 0x64;
562}
563
564static int __ath_regd_init(struct ath_regulatory *reg)
565{
566	struct country_code_to_enum_rd *country = NULL;
567	u16 regdmn;
568
569	if (!reg)
570		return -EINVAL;
571
572	ath_regd_sanitize(reg);
573
574	printk(KERN_DEBUG "ath: EEPROM regdomain: 0x%0x\n", reg->current_rd);
575
576	if (!ath_regd_is_eeprom_valid(reg)) {
577		pr_err("Invalid EEPROM contents\n");
578		return -EINVAL;
579	}
580
581	regdmn = ath_regd_get_eepromRD(reg);
582	reg->country_code = ath_regd_get_default_country(regdmn);
583
584	if (reg->country_code == CTRY_DEFAULT &&
585	    regdmn == CTRY_DEFAULT) {
586		printk(KERN_DEBUG "ath: EEPROM indicates default "
587		       "country code should be used\n");
588		reg->country_code = CTRY_UNITED_STATES;
589	}
590
591	if (reg->country_code == CTRY_DEFAULT) {
592		country = NULL;
593	} else {
594		printk(KERN_DEBUG "ath: doing EEPROM country->regdmn "
595		       "map search\n");
596		country = ath_regd_find_country(reg->country_code);
597		if (country == NULL) {
598			printk(KERN_DEBUG
599				"ath: no valid country maps found for "
600				"country code: 0x%0x\n",
601				reg->country_code);
602			return -EINVAL;
603		} else {
604			regdmn = country->regDmnEnum;
605			printk(KERN_DEBUG "ath: country maps to "
606			       "regdmn code: 0x%0x\n",
607			       regdmn);
608		}
609	}
610
611	reg->regpair = ath_get_regpair(regdmn);
612
613	if (!reg->regpair) {
614		printk(KERN_DEBUG "ath: "
615			"No regulatory domain pair found, cannot continue\n");
616		return -EINVAL;
617	}
618
619	if (!country)
620		country = ath_regd_find_country_by_rd(regdmn);
621
622	if (country) {
623		reg->alpha2[0] = country->isoName[0];
624		reg->alpha2[1] = country->isoName[1];
625	} else {
626		reg->alpha2[0] = '0';
627		reg->alpha2[1] = '0';
628	}
629
630	printk(KERN_DEBUG "ath: Country alpha2 being used: %c%c\n",
631		reg->alpha2[0], reg->alpha2[1]);
632	printk(KERN_DEBUG "ath: Regpair used: 0x%0x\n",
633		reg->regpair->regDmnEnum);
634
635	return 0;
636}
637
638int
639ath_regd_init(struct ath_regulatory *reg,
640	      struct wiphy *wiphy,
641	      void (*reg_notifier)(struct wiphy *wiphy,
642				   struct regulatory_request *request))
643{
644	struct ath_common *common = container_of(reg, struct ath_common,
645						 regulatory);
646	int r;
647
648	r = __ath_regd_init(reg);
649	if (r)
650		return r;
651
652	if (ath_is_world_regd(reg))
653		memcpy(&common->reg_world_copy, reg,
654		       sizeof(struct ath_regulatory));
655
656	ath_regd_init_wiphy(reg, wiphy, reg_notifier);
657
658	return 0;
659}
660EXPORT_SYMBOL(ath_regd_init);
661
662u32 ath_regd_get_band_ctl(struct ath_regulatory *reg,
663			  enum ieee80211_band band)
664{
665	if (!reg->regpair ||
666	    (reg->country_code == CTRY_DEFAULT &&
667	     is_wwr_sku(ath_regd_get_eepromRD(reg)))) {
668		return SD_NO_CTL;
669	}
670
671	switch (band) {
672	case IEEE80211_BAND_2GHZ:
673		return reg->regpair->reg_2ghz_ctl;
674	case IEEE80211_BAND_5GHZ:
675		return reg->regpair->reg_5ghz_ctl;
676	default:
677		return NO_CTL;
678	}
679}
680EXPORT_SYMBOL(ath_regd_get_band_ctl);
681