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