xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/ath/regd.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright (c) 2008-2009 Atheros Communications Inc.
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Permission to use, copy, modify, and/or distribute this software for any
5*4882a593Smuzhiyun  * purpose with or without fee is hereby granted, provided that the above
6*4882a593Smuzhiyun  * copyright notice and this permission notice appear in all copies.
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9*4882a593Smuzhiyun  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10*4882a593Smuzhiyun  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11*4882a593Smuzhiyun  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12*4882a593Smuzhiyun  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13*4882a593Smuzhiyun  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14*4882a593Smuzhiyun  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15*4882a593Smuzhiyun  */
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #include <linux/kernel.h>
20*4882a593Smuzhiyun #include <linux/export.h>
21*4882a593Smuzhiyun #include <net/cfg80211.h>
22*4882a593Smuzhiyun #include <net/mac80211.h>
23*4882a593Smuzhiyun #include "regd.h"
24*4882a593Smuzhiyun #include "regd_common.h"
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun static int __ath_regd_init(struct ath_regulatory *reg);
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun /*
29*4882a593Smuzhiyun  * This is a set of common rules used by our world regulatory domains.
30*4882a593Smuzhiyun  * We have 12 world regulatory domains. To save space we consolidate
31*4882a593Smuzhiyun  * the regulatory domains in 5 structures by frequency and change
32*4882a593Smuzhiyun  * the flags on our reg_notifier() on a case by case basis.
33*4882a593Smuzhiyun  */
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun /* Only these channels all allow active scan on all world regulatory domains */
36*4882a593Smuzhiyun #define ATH_2GHZ_CH01_11	REG_RULE(2412-10, 2462+10, 40, 0, 20, 0)
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun /* We enable active scan on these a case by case basis by regulatory domain */
39*4882a593Smuzhiyun #define ATH_2GHZ_CH12_13	REG_RULE(2467-10, 2472+10, 40, 0, 20,\
40*4882a593Smuzhiyun 					 NL80211_RRF_NO_IR)
41*4882a593Smuzhiyun #define ATH_2GHZ_CH14		REG_RULE(2484-10, 2484+10, 40, 0, 20,\
42*4882a593Smuzhiyun 					 NL80211_RRF_NO_IR | \
43*4882a593Smuzhiyun 					 NL80211_RRF_NO_OFDM)
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun /* We allow IBSS on these on a case by case basis by regulatory domain */
46*4882a593Smuzhiyun #define ATH_5GHZ_5150_5350	REG_RULE(5150-10, 5350+10, 80, 0, 30,\
47*4882a593Smuzhiyun 					 NL80211_RRF_NO_IR)
48*4882a593Smuzhiyun #define ATH_5GHZ_5470_5850	REG_RULE(5470-10, 5850+10, 80, 0, 30,\
49*4882a593Smuzhiyun 					 NL80211_RRF_NO_IR)
50*4882a593Smuzhiyun #define ATH_5GHZ_5725_5850	REG_RULE(5725-10, 5850+10, 80, 0, 30,\
51*4882a593Smuzhiyun 					 NL80211_RRF_NO_IR)
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun #define ATH_2GHZ_ALL		ATH_2GHZ_CH01_11, \
54*4882a593Smuzhiyun 				ATH_2GHZ_CH12_13, \
55*4882a593Smuzhiyun 				ATH_2GHZ_CH14
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun #define ATH_5GHZ_ALL		ATH_5GHZ_5150_5350, \
58*4882a593Smuzhiyun 				ATH_5GHZ_5470_5850
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun /* This one skips what we call "mid band" */
61*4882a593Smuzhiyun #define ATH_5GHZ_NO_MIDBAND	ATH_5GHZ_5150_5350, \
62*4882a593Smuzhiyun 				ATH_5GHZ_5725_5850
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun /* Can be used for:
65*4882a593Smuzhiyun  * 0x60, 0x61, 0x62 */
66*4882a593Smuzhiyun static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = {
67*4882a593Smuzhiyun 	.n_reg_rules = 5,
68*4882a593Smuzhiyun 	.alpha2 =  "99",
69*4882a593Smuzhiyun 	.reg_rules = {
70*4882a593Smuzhiyun 		ATH_2GHZ_ALL,
71*4882a593Smuzhiyun 		ATH_5GHZ_ALL,
72*4882a593Smuzhiyun 	}
73*4882a593Smuzhiyun };
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun /* Can be used by 0x63 and 0x65 */
76*4882a593Smuzhiyun static const struct ieee80211_regdomain ath_world_regdom_63_65 = {
77*4882a593Smuzhiyun 	.n_reg_rules = 4,
78*4882a593Smuzhiyun 	.alpha2 =  "99",
79*4882a593Smuzhiyun 	.reg_rules = {
80*4882a593Smuzhiyun 		ATH_2GHZ_CH01_11,
81*4882a593Smuzhiyun 		ATH_2GHZ_CH12_13,
82*4882a593Smuzhiyun 		ATH_5GHZ_NO_MIDBAND,
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun };
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun /* Can be used by 0x64 only */
87*4882a593Smuzhiyun static const struct ieee80211_regdomain ath_world_regdom_64 = {
88*4882a593Smuzhiyun 	.n_reg_rules = 3,
89*4882a593Smuzhiyun 	.alpha2 =  "99",
90*4882a593Smuzhiyun 	.reg_rules = {
91*4882a593Smuzhiyun 		ATH_2GHZ_CH01_11,
92*4882a593Smuzhiyun 		ATH_5GHZ_NO_MIDBAND,
93*4882a593Smuzhiyun 	}
94*4882a593Smuzhiyun };
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun /* Can be used by 0x66 and 0x69 */
97*4882a593Smuzhiyun static const struct ieee80211_regdomain ath_world_regdom_66_69 = {
98*4882a593Smuzhiyun 	.n_reg_rules = 3,
99*4882a593Smuzhiyun 	.alpha2 =  "99",
100*4882a593Smuzhiyun 	.reg_rules = {
101*4882a593Smuzhiyun 		ATH_2GHZ_CH01_11,
102*4882a593Smuzhiyun 		ATH_5GHZ_ALL,
103*4882a593Smuzhiyun 	}
104*4882a593Smuzhiyun };
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun /* Can be used by 0x67, 0x68, 0x6A and 0x6C */
107*4882a593Smuzhiyun static const struct ieee80211_regdomain ath_world_regdom_67_68_6A_6C = {
108*4882a593Smuzhiyun 	.n_reg_rules = 4,
109*4882a593Smuzhiyun 	.alpha2 =  "99",
110*4882a593Smuzhiyun 	.reg_rules = {
111*4882a593Smuzhiyun 		ATH_2GHZ_CH01_11,
112*4882a593Smuzhiyun 		ATH_2GHZ_CH12_13,
113*4882a593Smuzhiyun 		ATH_5GHZ_ALL,
114*4882a593Smuzhiyun 	}
115*4882a593Smuzhiyun };
116*4882a593Smuzhiyun 
dynamic_country_user_possible(struct ath_regulatory * reg)117*4882a593Smuzhiyun static bool dynamic_country_user_possible(struct ath_regulatory *reg)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun 	if (IS_ENABLED(CONFIG_ATH_REG_DYNAMIC_USER_CERT_TESTING))
120*4882a593Smuzhiyun 		return true;
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	switch (reg->country_code) {
123*4882a593Smuzhiyun 	case CTRY_UNITED_STATES:
124*4882a593Smuzhiyun 	case CTRY_JAPAN1:
125*4882a593Smuzhiyun 	case CTRY_JAPAN2:
126*4882a593Smuzhiyun 	case CTRY_JAPAN3:
127*4882a593Smuzhiyun 	case CTRY_JAPAN4:
128*4882a593Smuzhiyun 	case CTRY_JAPAN5:
129*4882a593Smuzhiyun 	case CTRY_JAPAN6:
130*4882a593Smuzhiyun 	case CTRY_JAPAN7:
131*4882a593Smuzhiyun 	case CTRY_JAPAN8:
132*4882a593Smuzhiyun 	case CTRY_JAPAN9:
133*4882a593Smuzhiyun 	case CTRY_JAPAN10:
134*4882a593Smuzhiyun 	case CTRY_JAPAN11:
135*4882a593Smuzhiyun 	case CTRY_JAPAN12:
136*4882a593Smuzhiyun 	case CTRY_JAPAN13:
137*4882a593Smuzhiyun 	case CTRY_JAPAN14:
138*4882a593Smuzhiyun 	case CTRY_JAPAN15:
139*4882a593Smuzhiyun 	case CTRY_JAPAN16:
140*4882a593Smuzhiyun 	case CTRY_JAPAN17:
141*4882a593Smuzhiyun 	case CTRY_JAPAN18:
142*4882a593Smuzhiyun 	case CTRY_JAPAN19:
143*4882a593Smuzhiyun 	case CTRY_JAPAN20:
144*4882a593Smuzhiyun 	case CTRY_JAPAN21:
145*4882a593Smuzhiyun 	case CTRY_JAPAN22:
146*4882a593Smuzhiyun 	case CTRY_JAPAN23:
147*4882a593Smuzhiyun 	case CTRY_JAPAN24:
148*4882a593Smuzhiyun 	case CTRY_JAPAN25:
149*4882a593Smuzhiyun 	case CTRY_JAPAN26:
150*4882a593Smuzhiyun 	case CTRY_JAPAN27:
151*4882a593Smuzhiyun 	case CTRY_JAPAN28:
152*4882a593Smuzhiyun 	case CTRY_JAPAN29:
153*4882a593Smuzhiyun 	case CTRY_JAPAN30:
154*4882a593Smuzhiyun 	case CTRY_JAPAN31:
155*4882a593Smuzhiyun 	case CTRY_JAPAN32:
156*4882a593Smuzhiyun 	case CTRY_JAPAN33:
157*4882a593Smuzhiyun 	case CTRY_JAPAN34:
158*4882a593Smuzhiyun 	case CTRY_JAPAN35:
159*4882a593Smuzhiyun 	case CTRY_JAPAN36:
160*4882a593Smuzhiyun 	case CTRY_JAPAN37:
161*4882a593Smuzhiyun 	case CTRY_JAPAN38:
162*4882a593Smuzhiyun 	case CTRY_JAPAN39:
163*4882a593Smuzhiyun 	case CTRY_JAPAN40:
164*4882a593Smuzhiyun 	case CTRY_JAPAN41:
165*4882a593Smuzhiyun 	case CTRY_JAPAN42:
166*4882a593Smuzhiyun 	case CTRY_JAPAN43:
167*4882a593Smuzhiyun 	case CTRY_JAPAN44:
168*4882a593Smuzhiyun 	case CTRY_JAPAN45:
169*4882a593Smuzhiyun 	case CTRY_JAPAN46:
170*4882a593Smuzhiyun 	case CTRY_JAPAN47:
171*4882a593Smuzhiyun 	case CTRY_JAPAN48:
172*4882a593Smuzhiyun 	case CTRY_JAPAN49:
173*4882a593Smuzhiyun 	case CTRY_JAPAN50:
174*4882a593Smuzhiyun 	case CTRY_JAPAN51:
175*4882a593Smuzhiyun 	case CTRY_JAPAN52:
176*4882a593Smuzhiyun 	case CTRY_JAPAN53:
177*4882a593Smuzhiyun 	case CTRY_JAPAN54:
178*4882a593Smuzhiyun 	case CTRY_JAPAN55:
179*4882a593Smuzhiyun 	case CTRY_JAPAN56:
180*4882a593Smuzhiyun 	case CTRY_JAPAN57:
181*4882a593Smuzhiyun 	case CTRY_JAPAN58:
182*4882a593Smuzhiyun 	case CTRY_JAPAN59:
183*4882a593Smuzhiyun 		return false;
184*4882a593Smuzhiyun 	}
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	return true;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun 
ath_reg_dyn_country_user_allow(struct ath_regulatory * reg)189*4882a593Smuzhiyun static bool ath_reg_dyn_country_user_allow(struct ath_regulatory *reg)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun 	if (!IS_ENABLED(CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS))
192*4882a593Smuzhiyun 		return false;
193*4882a593Smuzhiyun 	if (!dynamic_country_user_possible(reg))
194*4882a593Smuzhiyun 		return false;
195*4882a593Smuzhiyun 	return true;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun 
is_wwr_sku(u16 regd)198*4882a593Smuzhiyun static inline bool is_wwr_sku(u16 regd)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun 	return ((regd & COUNTRY_ERD_FLAG) != COUNTRY_ERD_FLAG) &&
201*4882a593Smuzhiyun 		(((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) ||
202*4882a593Smuzhiyun 		(regd == WORLD));
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun 
ath_regd_get_eepromRD(struct ath_regulatory * reg)205*4882a593Smuzhiyun static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
206*4882a593Smuzhiyun {
207*4882a593Smuzhiyun 	return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun 
ath_is_world_regd(struct ath_regulatory * reg)210*4882a593Smuzhiyun bool ath_is_world_regd(struct ath_regulatory *reg)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun 	return is_wwr_sku(ath_regd_get_eepromRD(reg));
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun EXPORT_SYMBOL(ath_is_world_regd);
215*4882a593Smuzhiyun 
ath_default_world_regdomain(void)216*4882a593Smuzhiyun static const struct ieee80211_regdomain *ath_default_world_regdomain(void)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun 	/* this is the most restrictive */
219*4882a593Smuzhiyun 	return &ath_world_regdom_64;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun static const struct
ath_world_regdomain(struct ath_regulatory * reg)223*4882a593Smuzhiyun ieee80211_regdomain *ath_world_regdomain(struct ath_regulatory *reg)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun 	switch (reg->regpair->reg_domain) {
226*4882a593Smuzhiyun 	case 0x60:
227*4882a593Smuzhiyun 	case 0x61:
228*4882a593Smuzhiyun 	case 0x62:
229*4882a593Smuzhiyun 		return &ath_world_regdom_60_61_62;
230*4882a593Smuzhiyun 	case 0x63:
231*4882a593Smuzhiyun 	case 0x65:
232*4882a593Smuzhiyun 		return &ath_world_regdom_63_65;
233*4882a593Smuzhiyun 	case 0x64:
234*4882a593Smuzhiyun 		return &ath_world_regdom_64;
235*4882a593Smuzhiyun 	case 0x66:
236*4882a593Smuzhiyun 	case 0x69:
237*4882a593Smuzhiyun 		return &ath_world_regdom_66_69;
238*4882a593Smuzhiyun 	case 0x67:
239*4882a593Smuzhiyun 	case 0x68:
240*4882a593Smuzhiyun 	case 0x6A:
241*4882a593Smuzhiyun 	case 0x6C:
242*4882a593Smuzhiyun 		return &ath_world_regdom_67_68_6A_6C;
243*4882a593Smuzhiyun 	default:
244*4882a593Smuzhiyun 		WARN_ON(1);
245*4882a593Smuzhiyun 		return ath_default_world_regdomain();
246*4882a593Smuzhiyun 	}
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun 
ath_is_49ghz_allowed(u16 regdomain)249*4882a593Smuzhiyun bool ath_is_49ghz_allowed(u16 regdomain)
250*4882a593Smuzhiyun {
251*4882a593Smuzhiyun 	/* possibly more */
252*4882a593Smuzhiyun 	return regdomain == MKK9_MKKC;
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun EXPORT_SYMBOL(ath_is_49ghz_allowed);
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun /* Frequency is one where radar detection is required */
ath_is_radar_freq(u16 center_freq,struct ath_regulatory * reg)257*4882a593Smuzhiyun static bool ath_is_radar_freq(u16 center_freq,
258*4882a593Smuzhiyun 			      struct ath_regulatory *reg)
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun {
261*4882a593Smuzhiyun 	if (reg->country_code == CTRY_INDIA)
262*4882a593Smuzhiyun 		return (center_freq >= 5500 && center_freq <= 5700);
263*4882a593Smuzhiyun 	return (center_freq >= 5260 && center_freq <= 5700);
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun 
ath_force_clear_no_ir_chan(struct wiphy * wiphy,struct ieee80211_channel * ch)266*4882a593Smuzhiyun static void ath_force_clear_no_ir_chan(struct wiphy *wiphy,
267*4882a593Smuzhiyun 				       struct ieee80211_channel *ch)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun 	const struct ieee80211_reg_rule *reg_rule;
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(ch->center_freq));
272*4882a593Smuzhiyun 	if (IS_ERR(reg_rule))
273*4882a593Smuzhiyun 		return;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	if (!(reg_rule->flags & NL80211_RRF_NO_IR))
276*4882a593Smuzhiyun 		if (ch->flags & IEEE80211_CHAN_NO_IR)
277*4882a593Smuzhiyun 			ch->flags &= ~IEEE80211_CHAN_NO_IR;
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun 
ath_force_clear_no_ir_freq(struct wiphy * wiphy,u16 center_freq)280*4882a593Smuzhiyun static void ath_force_clear_no_ir_freq(struct wiphy *wiphy, u16 center_freq)
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun 	struct ieee80211_channel *ch;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	ch = ieee80211_get_channel(wiphy, center_freq);
285*4882a593Smuzhiyun 	if (!ch)
286*4882a593Smuzhiyun 		return;
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	ath_force_clear_no_ir_chan(wiphy, ch);
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun 
ath_force_no_ir_chan(struct ieee80211_channel * ch)291*4882a593Smuzhiyun static void ath_force_no_ir_chan(struct ieee80211_channel *ch)
292*4882a593Smuzhiyun {
293*4882a593Smuzhiyun 	ch->flags |= IEEE80211_CHAN_NO_IR;
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun 
ath_force_no_ir_freq(struct wiphy * wiphy,u16 center_freq)296*4882a593Smuzhiyun static void ath_force_no_ir_freq(struct wiphy *wiphy, u16 center_freq)
297*4882a593Smuzhiyun {
298*4882a593Smuzhiyun 	struct ieee80211_channel *ch;
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	ch = ieee80211_get_channel(wiphy, center_freq);
301*4882a593Smuzhiyun 	if (!ch)
302*4882a593Smuzhiyun 		return;
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	ath_force_no_ir_chan(ch);
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun static void
__ath_reg_apply_beaconing_flags(struct wiphy * wiphy,struct ath_regulatory * reg,enum nl80211_reg_initiator initiator,struct ieee80211_channel * ch)308*4882a593Smuzhiyun __ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
309*4882a593Smuzhiyun 				struct ath_regulatory *reg,
310*4882a593Smuzhiyun 				enum nl80211_reg_initiator initiator,
311*4882a593Smuzhiyun 				struct ieee80211_channel *ch)
312*4882a593Smuzhiyun {
313*4882a593Smuzhiyun 	if (ath_is_radar_freq(ch->center_freq, reg) ||
314*4882a593Smuzhiyun 	    (ch->flags & IEEE80211_CHAN_RADAR))
315*4882a593Smuzhiyun 		return;
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	switch (initiator) {
318*4882a593Smuzhiyun 	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
319*4882a593Smuzhiyun 		ath_force_clear_no_ir_chan(wiphy, ch);
320*4882a593Smuzhiyun 		break;
321*4882a593Smuzhiyun 	case NL80211_REGDOM_SET_BY_USER:
322*4882a593Smuzhiyun 		if (ath_reg_dyn_country_user_allow(reg))
323*4882a593Smuzhiyun 			ath_force_clear_no_ir_chan(wiphy, ch);
324*4882a593Smuzhiyun 		break;
325*4882a593Smuzhiyun 	default:
326*4882a593Smuzhiyun 		if (ch->beacon_found)
327*4882a593Smuzhiyun 			ch->flags &= ~IEEE80211_CHAN_NO_IR;
328*4882a593Smuzhiyun 	}
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun /*
332*4882a593Smuzhiyun  * These exception rules do not apply radar frequencies.
333*4882a593Smuzhiyun  *
334*4882a593Smuzhiyun  * - We enable initiating radiation if the country IE says its fine:
335*4882a593Smuzhiyun  * - If no country IE has been processed and a we determine we have
336*4882a593Smuzhiyun  *   received a beacon on a channel we can enable initiating radiation.
337*4882a593Smuzhiyun  */
338*4882a593Smuzhiyun static void
ath_reg_apply_beaconing_flags(struct wiphy * wiphy,struct ath_regulatory * reg,enum nl80211_reg_initiator initiator)339*4882a593Smuzhiyun ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
340*4882a593Smuzhiyun 			      struct ath_regulatory *reg,
341*4882a593Smuzhiyun 			      enum nl80211_reg_initiator initiator)
342*4882a593Smuzhiyun {
343*4882a593Smuzhiyun 	enum nl80211_band band;
344*4882a593Smuzhiyun 	struct ieee80211_supported_band *sband;
345*4882a593Smuzhiyun 	struct ieee80211_channel *ch;
346*4882a593Smuzhiyun 	unsigned int i;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
349*4882a593Smuzhiyun 		if (!wiphy->bands[band])
350*4882a593Smuzhiyun 			continue;
351*4882a593Smuzhiyun 		sband = wiphy->bands[band];
352*4882a593Smuzhiyun 		for (i = 0; i < sband->n_channels; i++) {
353*4882a593Smuzhiyun 			ch = &sband->channels[i];
354*4882a593Smuzhiyun 			__ath_reg_apply_beaconing_flags(wiphy, reg,
355*4882a593Smuzhiyun 							initiator, ch);
356*4882a593Smuzhiyun 		}
357*4882a593Smuzhiyun 	}
358*4882a593Smuzhiyun }
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun /**
361*4882a593Smuzhiyun  * ath_reg_apply_ir_flags()
362*4882a593Smuzhiyun  * @wiphy: the wiphy to use
363*4882a593Smuzhiyun  * @initiator: the regulatory hint initiator
364*4882a593Smuzhiyun  *
365*4882a593Smuzhiyun  * If no country IE has been received always enable passive scan
366*4882a593Smuzhiyun  * and no-ibss on these channels. This is only done for specific
367*4882a593Smuzhiyun  * regulatory SKUs.
368*4882a593Smuzhiyun  *
369*4882a593Smuzhiyun  * If a country IE has been received check its rule for this
370*4882a593Smuzhiyun  * channel first before enabling active scan. The passive scan
371*4882a593Smuzhiyun  * would have been enforced by the initial processing of our
372*4882a593Smuzhiyun  * custom regulatory domain.
373*4882a593Smuzhiyun  */
374*4882a593Smuzhiyun static void
ath_reg_apply_ir_flags(struct wiphy * wiphy,struct ath_regulatory * reg,enum nl80211_reg_initiator initiator)375*4882a593Smuzhiyun ath_reg_apply_ir_flags(struct wiphy *wiphy,
376*4882a593Smuzhiyun 		       struct ath_regulatory *reg,
377*4882a593Smuzhiyun 		       enum nl80211_reg_initiator initiator)
378*4882a593Smuzhiyun {
379*4882a593Smuzhiyun 	struct ieee80211_supported_band *sband;
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	sband = wiphy->bands[NL80211_BAND_2GHZ];
382*4882a593Smuzhiyun 	if (!sband)
383*4882a593Smuzhiyun 		return;
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	switch(initiator) {
386*4882a593Smuzhiyun 	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
387*4882a593Smuzhiyun 		ath_force_clear_no_ir_freq(wiphy, 2467);
388*4882a593Smuzhiyun 		ath_force_clear_no_ir_freq(wiphy, 2472);
389*4882a593Smuzhiyun 		break;
390*4882a593Smuzhiyun 	case NL80211_REGDOM_SET_BY_USER:
391*4882a593Smuzhiyun 		if (!ath_reg_dyn_country_user_allow(reg))
392*4882a593Smuzhiyun 			break;
393*4882a593Smuzhiyun 		ath_force_clear_no_ir_freq(wiphy, 2467);
394*4882a593Smuzhiyun 		ath_force_clear_no_ir_freq(wiphy, 2472);
395*4882a593Smuzhiyun 		break;
396*4882a593Smuzhiyun 	default:
397*4882a593Smuzhiyun 		ath_force_no_ir_freq(wiphy, 2467);
398*4882a593Smuzhiyun 		ath_force_no_ir_freq(wiphy, 2472);
399*4882a593Smuzhiyun 	}
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun /* Always apply Radar/DFS rules on freq range 5500 MHz - 5700 MHz */
ath_reg_apply_radar_flags(struct wiphy * wiphy,struct ath_regulatory * reg)403*4882a593Smuzhiyun static void ath_reg_apply_radar_flags(struct wiphy *wiphy,
404*4882a593Smuzhiyun 				      struct ath_regulatory *reg)
405*4882a593Smuzhiyun {
406*4882a593Smuzhiyun 	struct ieee80211_supported_band *sband;
407*4882a593Smuzhiyun 	struct ieee80211_channel *ch;
408*4882a593Smuzhiyun 	unsigned int i;
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	if (!wiphy->bands[NL80211_BAND_5GHZ])
411*4882a593Smuzhiyun 		return;
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun 	sband = wiphy->bands[NL80211_BAND_5GHZ];
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	for (i = 0; i < sband->n_channels; i++) {
416*4882a593Smuzhiyun 		ch = &sband->channels[i];
417*4882a593Smuzhiyun 		if (!ath_is_radar_freq(ch->center_freq, reg))
418*4882a593Smuzhiyun 			continue;
419*4882a593Smuzhiyun 		/* We always enable radar detection/DFS on this
420*4882a593Smuzhiyun 		 * frequency range. Additionally we also apply on
421*4882a593Smuzhiyun 		 * this frequency range:
422*4882a593Smuzhiyun 		 * - If STA mode does not yet have DFS supports disable
423*4882a593Smuzhiyun 		 *   active scanning
424*4882a593Smuzhiyun 		 * - If adhoc mode does not support DFS yet then
425*4882a593Smuzhiyun 		 *   disable adhoc in the frequency.
426*4882a593Smuzhiyun 		 * - If AP mode does not yet support radar detection/DFS
427*4882a593Smuzhiyun 		 *   do not allow AP mode
428*4882a593Smuzhiyun 		 */
429*4882a593Smuzhiyun 		if (!(ch->flags & IEEE80211_CHAN_DISABLED))
430*4882a593Smuzhiyun 			ch->flags |= IEEE80211_CHAN_RADAR |
431*4882a593Smuzhiyun 				     IEEE80211_CHAN_NO_IR;
432*4882a593Smuzhiyun 	}
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun 
ath_reg_apply_world_flags(struct wiphy * wiphy,enum nl80211_reg_initiator initiator,struct ath_regulatory * reg)435*4882a593Smuzhiyun static void ath_reg_apply_world_flags(struct wiphy *wiphy,
436*4882a593Smuzhiyun 				      enum nl80211_reg_initiator initiator,
437*4882a593Smuzhiyun 				      struct ath_regulatory *reg)
438*4882a593Smuzhiyun {
439*4882a593Smuzhiyun 	switch (reg->regpair->reg_domain) {
440*4882a593Smuzhiyun 	case 0x60:
441*4882a593Smuzhiyun 	case 0x63:
442*4882a593Smuzhiyun 	case 0x66:
443*4882a593Smuzhiyun 	case 0x67:
444*4882a593Smuzhiyun 	case 0x6C:
445*4882a593Smuzhiyun 		ath_reg_apply_beaconing_flags(wiphy, reg, initiator);
446*4882a593Smuzhiyun 		break;
447*4882a593Smuzhiyun 	case 0x68:
448*4882a593Smuzhiyun 		ath_reg_apply_beaconing_flags(wiphy, reg, initiator);
449*4882a593Smuzhiyun 		ath_reg_apply_ir_flags(wiphy, reg, initiator);
450*4882a593Smuzhiyun 		break;
451*4882a593Smuzhiyun 	default:
452*4882a593Smuzhiyun 		if (ath_reg_dyn_country_user_allow(reg))
453*4882a593Smuzhiyun 			ath_reg_apply_beaconing_flags(wiphy, reg, initiator);
454*4882a593Smuzhiyun 	}
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun 
ath_regd_find_country_by_name(char * alpha2)457*4882a593Smuzhiyun u16 ath_regd_find_country_by_name(char *alpha2)
458*4882a593Smuzhiyun {
459*4882a593Smuzhiyun 	unsigned int i;
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
462*4882a593Smuzhiyun 		if (!memcmp(allCountries[i].isoName, alpha2, 2))
463*4882a593Smuzhiyun 			return allCountries[i].countryCode;
464*4882a593Smuzhiyun 	}
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	return -1;
467*4882a593Smuzhiyun }
468*4882a593Smuzhiyun EXPORT_SYMBOL(ath_regd_find_country_by_name);
469*4882a593Smuzhiyun 
__ath_reg_dyn_country(struct wiphy * wiphy,struct ath_regulatory * reg,struct regulatory_request * request)470*4882a593Smuzhiyun static int __ath_reg_dyn_country(struct wiphy *wiphy,
471*4882a593Smuzhiyun 				 struct ath_regulatory *reg,
472*4882a593Smuzhiyun 				 struct regulatory_request *request)
473*4882a593Smuzhiyun {
474*4882a593Smuzhiyun 	u16 country_code;
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 	if (request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
477*4882a593Smuzhiyun 	    !ath_is_world_regd(reg))
478*4882a593Smuzhiyun 		return -EINVAL;
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	country_code = ath_regd_find_country_by_name(request->alpha2);
481*4882a593Smuzhiyun 	if (country_code == (u16) -1)
482*4882a593Smuzhiyun 		return -EINVAL;
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	reg->current_rd = COUNTRY_ERD_FLAG;
485*4882a593Smuzhiyun 	reg->current_rd |= country_code;
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun 	__ath_regd_init(reg);
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 	ath_reg_apply_world_flags(wiphy, request->initiator, reg);
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	return 0;
492*4882a593Smuzhiyun }
493*4882a593Smuzhiyun 
ath_reg_dyn_country(struct wiphy * wiphy,struct ath_regulatory * reg,struct regulatory_request * request)494*4882a593Smuzhiyun static void ath_reg_dyn_country(struct wiphy *wiphy,
495*4882a593Smuzhiyun 				struct ath_regulatory *reg,
496*4882a593Smuzhiyun 				struct regulatory_request *request)
497*4882a593Smuzhiyun {
498*4882a593Smuzhiyun 	if (__ath_reg_dyn_country(wiphy, reg, request))
499*4882a593Smuzhiyun 		return;
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 	printk(KERN_DEBUG "ath: regdomain 0x%0x "
502*4882a593Smuzhiyun 			  "dynamically updated by %s\n",
503*4882a593Smuzhiyun 	       reg->current_rd,
504*4882a593Smuzhiyun 	       reg_initiator_name(request->initiator));
505*4882a593Smuzhiyun }
506*4882a593Smuzhiyun 
ath_reg_notifier_apply(struct wiphy * wiphy,struct regulatory_request * request,struct ath_regulatory * reg)507*4882a593Smuzhiyun void ath_reg_notifier_apply(struct wiphy *wiphy,
508*4882a593Smuzhiyun 			    struct regulatory_request *request,
509*4882a593Smuzhiyun 			    struct ath_regulatory *reg)
510*4882a593Smuzhiyun {
511*4882a593Smuzhiyun 	struct ath_common *common = container_of(reg, struct ath_common,
512*4882a593Smuzhiyun 						 regulatory);
513*4882a593Smuzhiyun 	/* We always apply this */
514*4882a593Smuzhiyun 	ath_reg_apply_radar_flags(wiphy, reg);
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 	/*
517*4882a593Smuzhiyun 	 * This would happen when we have sent a custom regulatory request
518*4882a593Smuzhiyun 	 * a world regulatory domain and the scheduler hasn't yet processed
519*4882a593Smuzhiyun 	 * any pending requests in the queue.
520*4882a593Smuzhiyun 	 */
521*4882a593Smuzhiyun 	if (!request)
522*4882a593Smuzhiyun 		return;
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	reg->region = request->dfs_region;
525*4882a593Smuzhiyun 	switch (request->initiator) {
526*4882a593Smuzhiyun 	case NL80211_REGDOM_SET_BY_CORE:
527*4882a593Smuzhiyun 		/*
528*4882a593Smuzhiyun 		 * If common->reg_world_copy is world roaming it means we *were*
529*4882a593Smuzhiyun 		 * world roaming... so we now have to restore that data.
530*4882a593Smuzhiyun 		 */
531*4882a593Smuzhiyun 		if (!ath_is_world_regd(&common->reg_world_copy))
532*4882a593Smuzhiyun 			break;
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 		memcpy(reg, &common->reg_world_copy,
535*4882a593Smuzhiyun 		       sizeof(struct ath_regulatory));
536*4882a593Smuzhiyun 		break;
537*4882a593Smuzhiyun 	case NL80211_REGDOM_SET_BY_DRIVER:
538*4882a593Smuzhiyun 		break;
539*4882a593Smuzhiyun 	case NL80211_REGDOM_SET_BY_USER:
540*4882a593Smuzhiyun 		if (ath_reg_dyn_country_user_allow(reg))
541*4882a593Smuzhiyun 			ath_reg_dyn_country(wiphy, reg, request);
542*4882a593Smuzhiyun 		break;
543*4882a593Smuzhiyun 	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
544*4882a593Smuzhiyun 		ath_reg_dyn_country(wiphy, reg, request);
545*4882a593Smuzhiyun 		break;
546*4882a593Smuzhiyun 	}
547*4882a593Smuzhiyun }
548*4882a593Smuzhiyun EXPORT_SYMBOL(ath_reg_notifier_apply);
549*4882a593Smuzhiyun 
ath_regd_is_eeprom_valid(struct ath_regulatory * reg)550*4882a593Smuzhiyun static bool ath_regd_is_eeprom_valid(struct ath_regulatory *reg)
551*4882a593Smuzhiyun {
552*4882a593Smuzhiyun 	u16 rd = ath_regd_get_eepromRD(reg);
553*4882a593Smuzhiyun 	int i;
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	if (rd & COUNTRY_ERD_FLAG) {
556*4882a593Smuzhiyun 		/* EEPROM value is a country code */
557*4882a593Smuzhiyun 		u16 cc = rd & ~COUNTRY_ERD_FLAG;
558*4882a593Smuzhiyun 		printk(KERN_DEBUG
559*4882a593Smuzhiyun 		       "ath: EEPROM indicates we should expect "
560*4882a593Smuzhiyun 			"a country code\n");
561*4882a593Smuzhiyun 		for (i = 0; i < ARRAY_SIZE(allCountries); i++)
562*4882a593Smuzhiyun 			if (allCountries[i].countryCode == cc)
563*4882a593Smuzhiyun 				return true;
564*4882a593Smuzhiyun 	} else {
565*4882a593Smuzhiyun 		/* EEPROM value is a regpair value */
566*4882a593Smuzhiyun 		if (rd != CTRY_DEFAULT)
567*4882a593Smuzhiyun 			printk(KERN_DEBUG "ath: EEPROM indicates we "
568*4882a593Smuzhiyun 			       "should expect a direct regpair map\n");
569*4882a593Smuzhiyun 		for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
570*4882a593Smuzhiyun 			if (regDomainPairs[i].reg_domain == rd)
571*4882a593Smuzhiyun 				return true;
572*4882a593Smuzhiyun 	}
573*4882a593Smuzhiyun 	printk(KERN_DEBUG
574*4882a593Smuzhiyun 		 "ath: invalid regulatory domain/country code 0x%x\n", rd);
575*4882a593Smuzhiyun 	return false;
576*4882a593Smuzhiyun }
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun /* EEPROM country code to regpair mapping */
579*4882a593Smuzhiyun static struct country_code_to_enum_rd*
ath_regd_find_country(u16 countryCode)580*4882a593Smuzhiyun ath_regd_find_country(u16 countryCode)
581*4882a593Smuzhiyun {
582*4882a593Smuzhiyun 	int i;
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
585*4882a593Smuzhiyun 		if (allCountries[i].countryCode == countryCode)
586*4882a593Smuzhiyun 			return &allCountries[i];
587*4882a593Smuzhiyun 	}
588*4882a593Smuzhiyun 	return NULL;
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun /* EEPROM rd code to regpair mapping */
592*4882a593Smuzhiyun static struct country_code_to_enum_rd*
ath_regd_find_country_by_rd(int regdmn)593*4882a593Smuzhiyun ath_regd_find_country_by_rd(int regdmn)
594*4882a593Smuzhiyun {
595*4882a593Smuzhiyun 	int i;
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
598*4882a593Smuzhiyun 		if (allCountries[i].regDmnEnum == regdmn)
599*4882a593Smuzhiyun 			return &allCountries[i];
600*4882a593Smuzhiyun 	}
601*4882a593Smuzhiyun 	return NULL;
602*4882a593Smuzhiyun }
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun /* Returns the map of the EEPROM set RD to a country code */
ath_regd_get_default_country(u16 rd)605*4882a593Smuzhiyun static u16 ath_regd_get_default_country(u16 rd)
606*4882a593Smuzhiyun {
607*4882a593Smuzhiyun 	if (rd & COUNTRY_ERD_FLAG) {
608*4882a593Smuzhiyun 		struct country_code_to_enum_rd *country = NULL;
609*4882a593Smuzhiyun 		u16 cc = rd & ~COUNTRY_ERD_FLAG;
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 		country = ath_regd_find_country(cc);
612*4882a593Smuzhiyun 		if (country != NULL)
613*4882a593Smuzhiyun 			return cc;
614*4882a593Smuzhiyun 	}
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 	return CTRY_DEFAULT;
617*4882a593Smuzhiyun }
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun static struct reg_dmn_pair_mapping*
ath_get_regpair(int regdmn)620*4882a593Smuzhiyun ath_get_regpair(int regdmn)
621*4882a593Smuzhiyun {
622*4882a593Smuzhiyun 	int i;
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun 	if (regdmn == NO_ENUMRD)
625*4882a593Smuzhiyun 		return NULL;
626*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
627*4882a593Smuzhiyun 		if (regDomainPairs[i].reg_domain == regdmn)
628*4882a593Smuzhiyun 			return &regDomainPairs[i];
629*4882a593Smuzhiyun 	}
630*4882a593Smuzhiyun 	return NULL;
631*4882a593Smuzhiyun }
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun static int
ath_regd_init_wiphy(struct ath_regulatory * reg,struct wiphy * wiphy,void (* reg_notifier)(struct wiphy * wiphy,struct regulatory_request * request))634*4882a593Smuzhiyun ath_regd_init_wiphy(struct ath_regulatory *reg,
635*4882a593Smuzhiyun 		    struct wiphy *wiphy,
636*4882a593Smuzhiyun 		    void (*reg_notifier)(struct wiphy *wiphy,
637*4882a593Smuzhiyun 					 struct regulatory_request *request))
638*4882a593Smuzhiyun {
639*4882a593Smuzhiyun 	const struct ieee80211_regdomain *regd;
640*4882a593Smuzhiyun 
641*4882a593Smuzhiyun 	wiphy->reg_notifier = reg_notifier;
642*4882a593Smuzhiyun 	wiphy->regulatory_flags |= REGULATORY_STRICT_REG |
643*4882a593Smuzhiyun 				   REGULATORY_CUSTOM_REG;
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun 	if (ath_is_world_regd(reg)) {
646*4882a593Smuzhiyun 		/*
647*4882a593Smuzhiyun 		 * Anything applied here (prior to wiphy registration) gets
648*4882a593Smuzhiyun 		 * saved on the wiphy orig_* parameters
649*4882a593Smuzhiyun 		 */
650*4882a593Smuzhiyun 		regd = ath_world_regdomain(reg);
651*4882a593Smuzhiyun 		wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_FOLLOW_POWER;
652*4882a593Smuzhiyun 	} else {
653*4882a593Smuzhiyun 		/*
654*4882a593Smuzhiyun 		 * This gets applied in the case of the absence of CRDA,
655*4882a593Smuzhiyun 		 * it's our own custom world regulatory domain, similar to
656*4882a593Smuzhiyun 		 * cfg80211's but we enable passive scanning.
657*4882a593Smuzhiyun 		 */
658*4882a593Smuzhiyun 		regd = ath_default_world_regdomain();
659*4882a593Smuzhiyun 	}
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun 	wiphy_apply_custom_regulatory(wiphy, regd);
662*4882a593Smuzhiyun 	ath_reg_apply_radar_flags(wiphy, reg);
663*4882a593Smuzhiyun 	ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg);
664*4882a593Smuzhiyun 	return 0;
665*4882a593Smuzhiyun }
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun /*
668*4882a593Smuzhiyun  * Some users have reported their EEPROM programmed with
669*4882a593Smuzhiyun  * 0x8000 set, this is not a supported regulatory domain
670*4882a593Smuzhiyun  * but since we have more than one user with it we need
671*4882a593Smuzhiyun  * a solution for them. We default to 0x64, which is the
672*4882a593Smuzhiyun  * default Atheros world regulatory domain.
673*4882a593Smuzhiyun  */
ath_regd_sanitize(struct ath_regulatory * reg)674*4882a593Smuzhiyun static void ath_regd_sanitize(struct ath_regulatory *reg)
675*4882a593Smuzhiyun {
676*4882a593Smuzhiyun 	if (reg->current_rd != COUNTRY_ERD_FLAG)
677*4882a593Smuzhiyun 		return;
678*4882a593Smuzhiyun 	printk(KERN_DEBUG "ath: EEPROM regdomain sanitized\n");
679*4882a593Smuzhiyun 	reg->current_rd = 0x64;
680*4882a593Smuzhiyun }
681*4882a593Smuzhiyun 
__ath_regd_init(struct ath_regulatory * reg)682*4882a593Smuzhiyun static int __ath_regd_init(struct ath_regulatory *reg)
683*4882a593Smuzhiyun {
684*4882a593Smuzhiyun 	struct country_code_to_enum_rd *country = NULL;
685*4882a593Smuzhiyun 	u16 regdmn;
686*4882a593Smuzhiyun 
687*4882a593Smuzhiyun 	if (!reg)
688*4882a593Smuzhiyun 		return -EINVAL;
689*4882a593Smuzhiyun 
690*4882a593Smuzhiyun 	ath_regd_sanitize(reg);
691*4882a593Smuzhiyun 
692*4882a593Smuzhiyun 	printk(KERN_DEBUG "ath: EEPROM regdomain: 0x%0x\n", reg->current_rd);
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun 	if (!ath_regd_is_eeprom_valid(reg)) {
695*4882a593Smuzhiyun 		pr_err("Invalid EEPROM contents\n");
696*4882a593Smuzhiyun 		return -EINVAL;
697*4882a593Smuzhiyun 	}
698*4882a593Smuzhiyun 
699*4882a593Smuzhiyun 	regdmn = ath_regd_get_eepromRD(reg);
700*4882a593Smuzhiyun 	reg->country_code = ath_regd_get_default_country(regdmn);
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun 	if (reg->country_code == CTRY_DEFAULT &&
703*4882a593Smuzhiyun 	    regdmn == CTRY_DEFAULT) {
704*4882a593Smuzhiyun 		printk(KERN_DEBUG "ath: EEPROM indicates default "
705*4882a593Smuzhiyun 		       "country code should be used\n");
706*4882a593Smuzhiyun 		reg->country_code = CTRY_UNITED_STATES;
707*4882a593Smuzhiyun 	}
708*4882a593Smuzhiyun 
709*4882a593Smuzhiyun 	if (reg->country_code == CTRY_DEFAULT) {
710*4882a593Smuzhiyun 		country = NULL;
711*4882a593Smuzhiyun 	} else {
712*4882a593Smuzhiyun 		printk(KERN_DEBUG "ath: doing EEPROM country->regdmn "
713*4882a593Smuzhiyun 		       "map search\n");
714*4882a593Smuzhiyun 		country = ath_regd_find_country(reg->country_code);
715*4882a593Smuzhiyun 		if (country == NULL) {
716*4882a593Smuzhiyun 			printk(KERN_DEBUG
717*4882a593Smuzhiyun 				"ath: no valid country maps found for "
718*4882a593Smuzhiyun 				"country code: 0x%0x\n",
719*4882a593Smuzhiyun 				reg->country_code);
720*4882a593Smuzhiyun 			return -EINVAL;
721*4882a593Smuzhiyun 		} else {
722*4882a593Smuzhiyun 			regdmn = country->regDmnEnum;
723*4882a593Smuzhiyun 			printk(KERN_DEBUG "ath: country maps to "
724*4882a593Smuzhiyun 			       "regdmn code: 0x%0x\n",
725*4882a593Smuzhiyun 			       regdmn);
726*4882a593Smuzhiyun 		}
727*4882a593Smuzhiyun 	}
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun 	reg->regpair = ath_get_regpair(regdmn);
730*4882a593Smuzhiyun 
731*4882a593Smuzhiyun 	if (!reg->regpair) {
732*4882a593Smuzhiyun 		printk(KERN_DEBUG "ath: "
733*4882a593Smuzhiyun 			"No regulatory domain pair found, cannot continue\n");
734*4882a593Smuzhiyun 		return -EINVAL;
735*4882a593Smuzhiyun 	}
736*4882a593Smuzhiyun 
737*4882a593Smuzhiyun 	if (!country)
738*4882a593Smuzhiyun 		country = ath_regd_find_country_by_rd(regdmn);
739*4882a593Smuzhiyun 
740*4882a593Smuzhiyun 	if (country) {
741*4882a593Smuzhiyun 		reg->alpha2[0] = country->isoName[0];
742*4882a593Smuzhiyun 		reg->alpha2[1] = country->isoName[1];
743*4882a593Smuzhiyun 	} else {
744*4882a593Smuzhiyun 		reg->alpha2[0] = '0';
745*4882a593Smuzhiyun 		reg->alpha2[1] = '0';
746*4882a593Smuzhiyun 	}
747*4882a593Smuzhiyun 
748*4882a593Smuzhiyun 	printk(KERN_DEBUG "ath: Country alpha2 being used: %c%c\n",
749*4882a593Smuzhiyun 		reg->alpha2[0], reg->alpha2[1]);
750*4882a593Smuzhiyun 	printk(KERN_DEBUG "ath: Regpair used: 0x%0x\n",
751*4882a593Smuzhiyun 		reg->regpair->reg_domain);
752*4882a593Smuzhiyun 
753*4882a593Smuzhiyun 	return 0;
754*4882a593Smuzhiyun }
755*4882a593Smuzhiyun 
756*4882a593Smuzhiyun int
ath_regd_init(struct ath_regulatory * reg,struct wiphy * wiphy,void (* reg_notifier)(struct wiphy * wiphy,struct regulatory_request * request))757*4882a593Smuzhiyun ath_regd_init(struct ath_regulatory *reg,
758*4882a593Smuzhiyun 	      struct wiphy *wiphy,
759*4882a593Smuzhiyun 	      void (*reg_notifier)(struct wiphy *wiphy,
760*4882a593Smuzhiyun 				   struct regulatory_request *request))
761*4882a593Smuzhiyun {
762*4882a593Smuzhiyun 	struct ath_common *common = container_of(reg, struct ath_common,
763*4882a593Smuzhiyun 						 regulatory);
764*4882a593Smuzhiyun 	int r;
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun 	r = __ath_regd_init(reg);
767*4882a593Smuzhiyun 	if (r)
768*4882a593Smuzhiyun 		return r;
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun 	if (ath_is_world_regd(reg))
771*4882a593Smuzhiyun 		memcpy(&common->reg_world_copy, reg,
772*4882a593Smuzhiyun 		       sizeof(struct ath_regulatory));
773*4882a593Smuzhiyun 
774*4882a593Smuzhiyun 	ath_regd_init_wiphy(reg, wiphy, reg_notifier);
775*4882a593Smuzhiyun 
776*4882a593Smuzhiyun 	return 0;
777*4882a593Smuzhiyun }
778*4882a593Smuzhiyun EXPORT_SYMBOL(ath_regd_init);
779*4882a593Smuzhiyun 
ath_regd_get_band_ctl(struct ath_regulatory * reg,enum nl80211_band band)780*4882a593Smuzhiyun u32 ath_regd_get_band_ctl(struct ath_regulatory *reg,
781*4882a593Smuzhiyun 			  enum nl80211_band band)
782*4882a593Smuzhiyun {
783*4882a593Smuzhiyun 	if (!reg->regpair ||
784*4882a593Smuzhiyun 	    (reg->country_code == CTRY_DEFAULT &&
785*4882a593Smuzhiyun 	     is_wwr_sku(ath_regd_get_eepromRD(reg)))) {
786*4882a593Smuzhiyun 		return SD_NO_CTL;
787*4882a593Smuzhiyun 	}
788*4882a593Smuzhiyun 
789*4882a593Smuzhiyun 	if (ath_regd_get_eepromRD(reg) == CTRY_DEFAULT) {
790*4882a593Smuzhiyun 		switch (reg->region) {
791*4882a593Smuzhiyun 		case NL80211_DFS_FCC:
792*4882a593Smuzhiyun 			return CTL_FCC;
793*4882a593Smuzhiyun 		case NL80211_DFS_ETSI:
794*4882a593Smuzhiyun 			return CTL_ETSI;
795*4882a593Smuzhiyun 		case NL80211_DFS_JP:
796*4882a593Smuzhiyun 			return CTL_MKK;
797*4882a593Smuzhiyun 		default:
798*4882a593Smuzhiyun 			break;
799*4882a593Smuzhiyun 		}
800*4882a593Smuzhiyun 	}
801*4882a593Smuzhiyun 
802*4882a593Smuzhiyun 	switch (band) {
803*4882a593Smuzhiyun 	case NL80211_BAND_2GHZ:
804*4882a593Smuzhiyun 		return reg->regpair->reg_2ghz_ctl;
805*4882a593Smuzhiyun 	case NL80211_BAND_5GHZ:
806*4882a593Smuzhiyun 		return reg->regpair->reg_5ghz_ctl;
807*4882a593Smuzhiyun 	default:
808*4882a593Smuzhiyun 		return NO_CTL;
809*4882a593Smuzhiyun 	}
810*4882a593Smuzhiyun }
811*4882a593Smuzhiyun EXPORT_SYMBOL(ath_regd_get_band_ctl);
812