xref: /onnv-gate/usr/src/uts/common/io/arn/arn_regd.c (revision 9999:d5e89571de4e)
1*9999SWang.Lin@Sun.COM /*
2*9999SWang.Lin@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3*9999SWang.Lin@Sun.COM  * Use is subject to license terms.
4*9999SWang.Lin@Sun.COM  */
5*9999SWang.Lin@Sun.COM 
6*9999SWang.Lin@Sun.COM /*
7*9999SWang.Lin@Sun.COM  * Copyright (c) 2008 Atheros Communications Inc.
8*9999SWang.Lin@Sun.COM  *
9*9999SWang.Lin@Sun.COM  * Permission to use, copy, modify, and/or distribute this software for any
10*9999SWang.Lin@Sun.COM  * purpose with or without fee is hereby granted, provided that the above
11*9999SWang.Lin@Sun.COM  * copyright notice and this permission notice appear in all copies.
12*9999SWang.Lin@Sun.COM  *
13*9999SWang.Lin@Sun.COM  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14*9999SWang.Lin@Sun.COM  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15*9999SWang.Lin@Sun.COM  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16*9999SWang.Lin@Sun.COM  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17*9999SWang.Lin@Sun.COM  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18*9999SWang.Lin@Sun.COM  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19*9999SWang.Lin@Sun.COM  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20*9999SWang.Lin@Sun.COM  */
21*9999SWang.Lin@Sun.COM 
22*9999SWang.Lin@Sun.COM #include "arn_core.h"
23*9999SWang.Lin@Sun.COM #include "arn_hw.h"
24*9999SWang.Lin@Sun.COM #include "arn_regd.h"
25*9999SWang.Lin@Sun.COM #include "arn_regd_common.h"
26*9999SWang.Lin@Sun.COM 
27*9999SWang.Lin@Sun.COM static int
ath9k_regd_chansort(const void * a,const void * b)28*9999SWang.Lin@Sun.COM ath9k_regd_chansort(const void *a, const void *b)
29*9999SWang.Lin@Sun.COM {
30*9999SWang.Lin@Sun.COM 	const struct ath9k_channel *ca = a;
31*9999SWang.Lin@Sun.COM 	const struct ath9k_channel *cb = b;
32*9999SWang.Lin@Sun.COM 
33*9999SWang.Lin@Sun.COM 	return (ca->channel == cb->channel) ?
34*9999SWang.Lin@Sun.COM 	    (ca->channelFlags & CHAN_FLAGS) -
35*9999SWang.Lin@Sun.COM 	    (cb->channelFlags & CHAN_FLAGS) : ca->channel - cb->channel;
36*9999SWang.Lin@Sun.COM }
37*9999SWang.Lin@Sun.COM 
38*9999SWang.Lin@Sun.COM static void
ath9k_regd_sort(void * a,uint32_t n,uint32_t size,ath_hal_cmp_t * cmp)39*9999SWang.Lin@Sun.COM ath9k_regd_sort(void *a, uint32_t n, uint32_t size, ath_hal_cmp_t *cmp)
40*9999SWang.Lin@Sun.COM {
41*9999SWang.Lin@Sun.COM 	uint8_t *aa = a;
42*9999SWang.Lin@Sun.COM 	uint8_t *ai, *t;
43*9999SWang.Lin@Sun.COM 
44*9999SWang.Lin@Sun.COM 	for (ai = aa + size; --n >= 1; ai += size)
45*9999SWang.Lin@Sun.COM 		for (t = ai; t > aa; t -= size) {
46*9999SWang.Lin@Sun.COM 			uint8_t *u = t - size;
47*9999SWang.Lin@Sun.COM 			if (cmp(u, t) <= 0)
48*9999SWang.Lin@Sun.COM 				break;
49*9999SWang.Lin@Sun.COM 			swap(u, t, size);
50*9999SWang.Lin@Sun.COM 		}
51*9999SWang.Lin@Sun.COM }
52*9999SWang.Lin@Sun.COM 
53*9999SWang.Lin@Sun.COM static uint16_t
ath9k_regd_get_eepromRD(struct ath_hal * ah)54*9999SWang.Lin@Sun.COM ath9k_regd_get_eepromRD(struct ath_hal *ah)
55*9999SWang.Lin@Sun.COM {
56*9999SWang.Lin@Sun.COM 	return (ah->ah_currentRD & ~WORLDWIDE_ROAMING_FLAG);
57*9999SWang.Lin@Sun.COM }
58*9999SWang.Lin@Sun.COM 
59*9999SWang.Lin@Sun.COM static boolean_t
ath9k_regd_is_chan_bm_zero(uint64_t * bitmask)60*9999SWang.Lin@Sun.COM ath9k_regd_is_chan_bm_zero(uint64_t *bitmask)
61*9999SWang.Lin@Sun.COM {
62*9999SWang.Lin@Sun.COM 	int i;
63*9999SWang.Lin@Sun.COM 
64*9999SWang.Lin@Sun.COM 	for (i = 0; i < BMLEN; i++) {
65*9999SWang.Lin@Sun.COM 		if (bitmask[i] != 0)
66*9999SWang.Lin@Sun.COM 			return (B_FALSE);
67*9999SWang.Lin@Sun.COM 	}
68*9999SWang.Lin@Sun.COM 	return (B_TRUE);
69*9999SWang.Lin@Sun.COM }
70*9999SWang.Lin@Sun.COM 
71*9999SWang.Lin@Sun.COM static boolean_t
ath9k_regd_is_eeprom_valid(struct ath_hal * ah)72*9999SWang.Lin@Sun.COM ath9k_regd_is_eeprom_valid(struct ath_hal *ah)
73*9999SWang.Lin@Sun.COM {
74*9999SWang.Lin@Sun.COM 	uint16_t rd = ath9k_regd_get_eepromRD(ah);
75*9999SWang.Lin@Sun.COM 	int i;
76*9999SWang.Lin@Sun.COM 
77*9999SWang.Lin@Sun.COM 	if (rd & COUNTRY_ERD_FLAG) {
78*9999SWang.Lin@Sun.COM 		uint16_t cc = rd & ~COUNTRY_ERD_FLAG;
79*9999SWang.Lin@Sun.COM 		for (i = 0; i < ARRAY_SIZE(allCountries); i++)
80*9999SWang.Lin@Sun.COM 			if (allCountries[i].countryCode == cc)
81*9999SWang.Lin@Sun.COM 				return (B_TRUE);
82*9999SWang.Lin@Sun.COM 	} else {
83*9999SWang.Lin@Sun.COM 		for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
84*9999SWang.Lin@Sun.COM 			if (regDomainPairs[i].regDmnEnum == rd)
85*9999SWang.Lin@Sun.COM 				return (B_TRUE);
86*9999SWang.Lin@Sun.COM 	}
87*9999SWang.Lin@Sun.COM 
88*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_REGULATORY,
89*9999SWang.Lin@Sun.COM 	    "%s: invalid regulatory domain/country code 0x%x\n",
90*9999SWang.Lin@Sun.COM 	    __func__, rd));
91*9999SWang.Lin@Sun.COM 
92*9999SWang.Lin@Sun.COM 	return (B_FALSE);
93*9999SWang.Lin@Sun.COM }
94*9999SWang.Lin@Sun.COM 
95*9999SWang.Lin@Sun.COM static boolean_t
ath9k_regd_is_fcc_midband_supported(struct ath_hal * ah)96*9999SWang.Lin@Sun.COM ath9k_regd_is_fcc_midband_supported(struct ath_hal *ah)
97*9999SWang.Lin@Sun.COM {
98*9999SWang.Lin@Sun.COM 	uint32_t regcap;
99*9999SWang.Lin@Sun.COM 
100*9999SWang.Lin@Sun.COM 	regcap = ah->ah_caps.reg_cap;
101*9999SWang.Lin@Sun.COM 
102*9999SWang.Lin@Sun.COM 	if (regcap & AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND)
103*9999SWang.Lin@Sun.COM 		return (B_TRUE);
104*9999SWang.Lin@Sun.COM 	else
105*9999SWang.Lin@Sun.COM 		return (B_FALSE);
106*9999SWang.Lin@Sun.COM }
107*9999SWang.Lin@Sun.COM 
108*9999SWang.Lin@Sun.COM static boolean_t
ath9k_regd_is_ccode_valid(struct ath_hal * ah,uint16_t cc)109*9999SWang.Lin@Sun.COM ath9k_regd_is_ccode_valid(struct ath_hal *ah, uint16_t cc)
110*9999SWang.Lin@Sun.COM {
111*9999SWang.Lin@Sun.COM 	uint16_t rd;
112*9999SWang.Lin@Sun.COM 	int i;
113*9999SWang.Lin@Sun.COM 
114*9999SWang.Lin@Sun.COM 	if (cc == CTRY_DEFAULT)
115*9999SWang.Lin@Sun.COM 		return (B_TRUE);
116*9999SWang.Lin@Sun.COM 	if (cc == CTRY_DEBUG)
117*9999SWang.Lin@Sun.COM 		return (B_TRUE);
118*9999SWang.Lin@Sun.COM 
119*9999SWang.Lin@Sun.COM 	rd = ath9k_regd_get_eepromRD(ah);
120*9999SWang.Lin@Sun.COM 
121*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_REGULATORY, "%s: EEPROM regdomain 0x%x\n",
122*9999SWang.Lin@Sun.COM 	    __func__, rd));
123*9999SWang.Lin@Sun.COM 
124*9999SWang.Lin@Sun.COM 	if (rd & COUNTRY_ERD_FLAG) {
125*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY,
126*9999SWang.Lin@Sun.COM 		    "%s: EEPROM setting is country code %u\n",
127*9999SWang.Lin@Sun.COM 		    __func__, rd & ~COUNTRY_ERD_FLAG));
128*9999SWang.Lin@Sun.COM 		return (cc == (rd & ~COUNTRY_ERD_FLAG));
129*9999SWang.Lin@Sun.COM 	}
130*9999SWang.Lin@Sun.COM 
131*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
132*9999SWang.Lin@Sun.COM 		if (cc == allCountries[i].countryCode) {
133*9999SWang.Lin@Sun.COM #ifdef ARN_SUPPORT_11D
134*9999SWang.Lin@Sun.COM 			if ((rd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)
135*9999SWang.Lin@Sun.COM 				return (B_TRUE);
136*9999SWang.Lin@Sun.COM #endif
137*9999SWang.Lin@Sun.COM 			if (allCountries[i].regDmnEnum == rd ||
138*9999SWang.Lin@Sun.COM 			    rd == DEBUG_REG_DMN || rd == NO_ENUMRD)
139*9999SWang.Lin@Sun.COM 				return (B_TRUE);
140*9999SWang.Lin@Sun.COM 		}
141*9999SWang.Lin@Sun.COM 	}
142*9999SWang.Lin@Sun.COM 	return (B_FALSE);
143*9999SWang.Lin@Sun.COM }
144*9999SWang.Lin@Sun.COM 
145*9999SWang.Lin@Sun.COM static void
ath9k_regd_get_wmodes_nreg(struct ath_hal * ah,struct country_code_to_enum_rd * country,struct regDomain * rd5GHz,uint8_t * modes_allowed)146*9999SWang.Lin@Sun.COM ath9k_regd_get_wmodes_nreg(struct ath_hal *ah,
147*9999SWang.Lin@Sun.COM     struct country_code_to_enum_rd *country,
148*9999SWang.Lin@Sun.COM     struct regDomain *rd5GHz,
149*9999SWang.Lin@Sun.COM     uint8_t *modes_allowed)
150*9999SWang.Lin@Sun.COM 
151*9999SWang.Lin@Sun.COM {
152*9999SWang.Lin@Sun.COM 	bcopy(ah->ah_caps.wireless_modes, modes_allowed,
153*9999SWang.Lin@Sun.COM 	    sizeof (ah->ah_caps.wireless_modes));
154*9999SWang.Lin@Sun.COM 
155*9999SWang.Lin@Sun.COM 	if (is_set(ATH9K_MODE_11G, ah->ah_caps.wireless_modes) &&
156*9999SWang.Lin@Sun.COM 	    (!country->allow11g))
157*9999SWang.Lin@Sun.COM 		clr_bit(ATH9K_MODE_11G, modes_allowed);
158*9999SWang.Lin@Sun.COM 
159*9999SWang.Lin@Sun.COM 	if (is_set(ATH9K_MODE_11A, ah->ah_caps.wireless_modes) &&
160*9999SWang.Lin@Sun.COM 	    (ath9k_regd_is_chan_bm_zero(rd5GHz->chan11a)))
161*9999SWang.Lin@Sun.COM 		clr_bit(ATH9K_MODE_11A, modes_allowed);
162*9999SWang.Lin@Sun.COM 
163*9999SWang.Lin@Sun.COM 	if (is_set(ATH9K_MODE_11NG_HT20, ah->ah_caps.wireless_modes) &&
164*9999SWang.Lin@Sun.COM 	    (!country->allow11ng20))
165*9999SWang.Lin@Sun.COM 		clr_bit(ATH9K_MODE_11NG_HT20, modes_allowed);
166*9999SWang.Lin@Sun.COM 
167*9999SWang.Lin@Sun.COM 	if (is_set(ATH9K_MODE_11NA_HT20, ah->ah_caps.wireless_modes) &&
168*9999SWang.Lin@Sun.COM 	    (!country->allow11na20))
169*9999SWang.Lin@Sun.COM 		clr_bit(ATH9K_MODE_11NA_HT20, modes_allowed);
170*9999SWang.Lin@Sun.COM 
171*9999SWang.Lin@Sun.COM 	if (is_set(ATH9K_MODE_11NG_HT40PLUS, ah->ah_caps.wireless_modes) &&
172*9999SWang.Lin@Sun.COM 	    (!country->allow11ng40))
173*9999SWang.Lin@Sun.COM 		clr_bit(ATH9K_MODE_11NG_HT40PLUS, modes_allowed);
174*9999SWang.Lin@Sun.COM 
175*9999SWang.Lin@Sun.COM 	if (is_set(ATH9K_MODE_11NG_HT40MINUS, ah->ah_caps.wireless_modes) &&
176*9999SWang.Lin@Sun.COM 	    (!country->allow11ng40))
177*9999SWang.Lin@Sun.COM 		clr_bit(ATH9K_MODE_11NG_HT40MINUS, modes_allowed);
178*9999SWang.Lin@Sun.COM 
179*9999SWang.Lin@Sun.COM 	if (is_set(ATH9K_MODE_11NA_HT40PLUS, ah->ah_caps.wireless_modes) &&
180*9999SWang.Lin@Sun.COM 	    (!country->allow11na40))
181*9999SWang.Lin@Sun.COM 		clr_bit(ATH9K_MODE_11NA_HT40PLUS, modes_allowed);
182*9999SWang.Lin@Sun.COM 
183*9999SWang.Lin@Sun.COM 	if (is_set(ATH9K_MODE_11NA_HT40MINUS, ah->ah_caps.wireless_modes) &&
184*9999SWang.Lin@Sun.COM 	    (!country->allow11na40))
185*9999SWang.Lin@Sun.COM 		clr_bit(ATH9K_MODE_11NA_HT40MINUS, modes_allowed);
186*9999SWang.Lin@Sun.COM }
187*9999SWang.Lin@Sun.COM 
188*9999SWang.Lin@Sun.COM boolean_t
ath9k_regd_is_public_safety_sku(struct ath_hal * ah)189*9999SWang.Lin@Sun.COM ath9k_regd_is_public_safety_sku(struct ath_hal *ah)
190*9999SWang.Lin@Sun.COM {
191*9999SWang.Lin@Sun.COM 	uint16_t rd;
192*9999SWang.Lin@Sun.COM 
193*9999SWang.Lin@Sun.COM 	rd = ath9k_regd_get_eepromRD(ah);
194*9999SWang.Lin@Sun.COM 
195*9999SWang.Lin@Sun.COM 	switch (rd) {
196*9999SWang.Lin@Sun.COM 	case FCC4_FCCA:
197*9999SWang.Lin@Sun.COM 	case (CTRY_UNITED_STATES_FCC49 | COUNTRY_ERD_FLAG):
198*9999SWang.Lin@Sun.COM 		return (B_TRUE);
199*9999SWang.Lin@Sun.COM 	case DEBUG_REG_DMN:
200*9999SWang.Lin@Sun.COM 	case NO_ENUMRD:
201*9999SWang.Lin@Sun.COM 		if (ah->ah_countryCode == CTRY_UNITED_STATES_FCC49)
202*9999SWang.Lin@Sun.COM 			return (B_TRUE);
203*9999SWang.Lin@Sun.COM 		break;
204*9999SWang.Lin@Sun.COM 	}
205*9999SWang.Lin@Sun.COM 	return (B_FALSE);
206*9999SWang.Lin@Sun.COM }
207*9999SWang.Lin@Sun.COM 
208*9999SWang.Lin@Sun.COM static struct country_code_to_enum_rd *
ath9k_regd_find_country(uint16_t countryCode)209*9999SWang.Lin@Sun.COM ath9k_regd_find_country(uint16_t countryCode)
210*9999SWang.Lin@Sun.COM {
211*9999SWang.Lin@Sun.COM 	int i;
212*9999SWang.Lin@Sun.COM 
213*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
214*9999SWang.Lin@Sun.COM 		if (allCountries[i].countryCode == countryCode)
215*9999SWang.Lin@Sun.COM 			return (&allCountries[i]);
216*9999SWang.Lin@Sun.COM 	}
217*9999SWang.Lin@Sun.COM 	return (NULL);
218*9999SWang.Lin@Sun.COM }
219*9999SWang.Lin@Sun.COM 
220*9999SWang.Lin@Sun.COM static uint16_t
ath9k_regd_get_default_country(struct ath_hal * ah)221*9999SWang.Lin@Sun.COM ath9k_regd_get_default_country(struct ath_hal *ah)
222*9999SWang.Lin@Sun.COM {
223*9999SWang.Lin@Sun.COM 	uint16_t rd;
224*9999SWang.Lin@Sun.COM 	int i;
225*9999SWang.Lin@Sun.COM 
226*9999SWang.Lin@Sun.COM 	rd = ath9k_regd_get_eepromRD(ah);
227*9999SWang.Lin@Sun.COM 	if (rd & COUNTRY_ERD_FLAG) {
228*9999SWang.Lin@Sun.COM 		struct country_code_to_enum_rd *country = NULL;
229*9999SWang.Lin@Sun.COM 		uint16_t cc = rd & ~COUNTRY_ERD_FLAG;
230*9999SWang.Lin@Sun.COM 
231*9999SWang.Lin@Sun.COM 		country = ath9k_regd_find_country(cc);
232*9999SWang.Lin@Sun.COM 		if (country != NULL)
233*9999SWang.Lin@Sun.COM 			return (cc);
234*9999SWang.Lin@Sun.COM 	}
235*9999SWang.Lin@Sun.COM 
236*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
237*9999SWang.Lin@Sun.COM 		if (regDomainPairs[i].regDmnEnum == rd) {
238*9999SWang.Lin@Sun.COM 			if (regDomainPairs[i].singleCC != 0)
239*9999SWang.Lin@Sun.COM 				return (regDomainPairs[i].singleCC);
240*9999SWang.Lin@Sun.COM 			else
241*9999SWang.Lin@Sun.COM 				i = ARRAY_SIZE(regDomainPairs);
242*9999SWang.Lin@Sun.COM 		}
243*9999SWang.Lin@Sun.COM 	return (CTRY_DEFAULT);
244*9999SWang.Lin@Sun.COM }
245*9999SWang.Lin@Sun.COM 
246*9999SWang.Lin@Sun.COM static boolean_t
ath9k_regd_is_valid_reg_domain(int regDmn,struct regDomain * rd)247*9999SWang.Lin@Sun.COM ath9k_regd_is_valid_reg_domain(int regDmn, struct regDomain *rd)
248*9999SWang.Lin@Sun.COM {
249*9999SWang.Lin@Sun.COM 	int i;
250*9999SWang.Lin@Sun.COM 
251*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(regDomains); i++) {
252*9999SWang.Lin@Sun.COM 		if (regDomains[i].regDmnEnum == regDmn) {
253*9999SWang.Lin@Sun.COM 			if (rd != NULL) {
254*9999SWang.Lin@Sun.COM 				(void) memcpy(rd, &regDomains[i],
255*9999SWang.Lin@Sun.COM 				    sizeof (struct regDomain));
256*9999SWang.Lin@Sun.COM 			}
257*9999SWang.Lin@Sun.COM 			return (B_TRUE);
258*9999SWang.Lin@Sun.COM 		}
259*9999SWang.Lin@Sun.COM 	}
260*9999SWang.Lin@Sun.COM 	return (B_FALSE);
261*9999SWang.Lin@Sun.COM }
262*9999SWang.Lin@Sun.COM 
263*9999SWang.Lin@Sun.COM static boolean_t
ath9k_regd_is_valid_reg_domainPair(int regDmnPair)264*9999SWang.Lin@Sun.COM ath9k_regd_is_valid_reg_domainPair(int regDmnPair)
265*9999SWang.Lin@Sun.COM {
266*9999SWang.Lin@Sun.COM 	int i;
267*9999SWang.Lin@Sun.COM 
268*9999SWang.Lin@Sun.COM 	if (regDmnPair == NO_ENUMRD)
269*9999SWang.Lin@Sun.COM 		return (B_FALSE);
270*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
271*9999SWang.Lin@Sun.COM 		if (regDomainPairs[i].regDmnEnum == regDmnPair)
272*9999SWang.Lin@Sun.COM 			return (B_TRUE);
273*9999SWang.Lin@Sun.COM 	}
274*9999SWang.Lin@Sun.COM 	return (B_FALSE);
275*9999SWang.Lin@Sun.COM }
276*9999SWang.Lin@Sun.COM 
277*9999SWang.Lin@Sun.COM static boolean_t
ath9k_regd_get_wmode_regdomain(struct ath_hal * ah,int regDmn,uint16_t channelFlag,struct regDomain * rd)278*9999SWang.Lin@Sun.COM ath9k_regd_get_wmode_regdomain(struct ath_hal *ah, int regDmn,
279*9999SWang.Lin@Sun.COM     uint16_t channelFlag, struct regDomain *rd)
280*9999SWang.Lin@Sun.COM {
281*9999SWang.Lin@Sun.COM 	int i, found;
282*9999SWang.Lin@Sun.COM 	uint64_t flags = NO_REQ;
283*9999SWang.Lin@Sun.COM 	struct reg_dmn_pair_mapping *regPair = NULL;
284*9999SWang.Lin@Sun.COM 	int regOrg;
285*9999SWang.Lin@Sun.COM 
286*9999SWang.Lin@Sun.COM 	regOrg = regDmn;
287*9999SWang.Lin@Sun.COM 	if (regDmn == CTRY_DEFAULT) {
288*9999SWang.Lin@Sun.COM 		uint16_t rdnum;
289*9999SWang.Lin@Sun.COM 		rdnum = ath9k_regd_get_eepromRD(ah);
290*9999SWang.Lin@Sun.COM 
291*9999SWang.Lin@Sun.COM 		if (!(rdnum & COUNTRY_ERD_FLAG)) {
292*9999SWang.Lin@Sun.COM 			if (ath9k_regd_is_valid_reg_domain(rdnum, NULL) ||
293*9999SWang.Lin@Sun.COM 			    ath9k_regd_is_valid_reg_domainPair(rdnum)) {
294*9999SWang.Lin@Sun.COM 				regDmn = rdnum;
295*9999SWang.Lin@Sun.COM 			}
296*9999SWang.Lin@Sun.COM 		}
297*9999SWang.Lin@Sun.COM 	}
298*9999SWang.Lin@Sun.COM 
299*9999SWang.Lin@Sun.COM 	if ((regDmn & MULTI_DOMAIN_MASK) == 0) {
300*9999SWang.Lin@Sun.COM 		for (i = 0, found = 0;
301*9999SWang.Lin@Sun.COM 		    (i < ARRAY_SIZE(regDomainPairs)) && (!found); i++) {
302*9999SWang.Lin@Sun.COM 			if (regDomainPairs[i].regDmnEnum == regDmn) {
303*9999SWang.Lin@Sun.COM 				regPair = &regDomainPairs[i];
304*9999SWang.Lin@Sun.COM 				found = 1;
305*9999SWang.Lin@Sun.COM 			}
306*9999SWang.Lin@Sun.COM 		}
307*9999SWang.Lin@Sun.COM 		if (!found) {
308*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_REGULATORY,
309*9999SWang.Lin@Sun.COM 			    "%s: Failed to find reg domain pair %u\n",
310*9999SWang.Lin@Sun.COM 			    __func__, regDmn));
311*9999SWang.Lin@Sun.COM 			return (B_FALSE);
312*9999SWang.Lin@Sun.COM 		}
313*9999SWang.Lin@Sun.COM 		if (!(channelFlag & CHANNEL_2GHZ)) {
314*9999SWang.Lin@Sun.COM 			regDmn = regPair->regDmn5GHz;
315*9999SWang.Lin@Sun.COM 			flags = regPair->flags5GHz;
316*9999SWang.Lin@Sun.COM 		}
317*9999SWang.Lin@Sun.COM 		if (channelFlag & CHANNEL_2GHZ) {
318*9999SWang.Lin@Sun.COM 			regDmn = regPair->regDmn2GHz;
319*9999SWang.Lin@Sun.COM 			flags = regPair->flags2GHz;
320*9999SWang.Lin@Sun.COM 		}
321*9999SWang.Lin@Sun.COM 	}
322*9999SWang.Lin@Sun.COM 
323*9999SWang.Lin@Sun.COM 	found = ath9k_regd_is_valid_reg_domain(regDmn, rd);
324*9999SWang.Lin@Sun.COM 	if (!found) {
325*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY,
326*9999SWang.Lin@Sun.COM 		    "%s: Failed to find unitary reg domain %u\n",
327*9999SWang.Lin@Sun.COM 		    __func__, regDmn));
328*9999SWang.Lin@Sun.COM 		return (B_FALSE);
329*9999SWang.Lin@Sun.COM 	} else {
330*9999SWang.Lin@Sun.COM 		rd->pscan &= regPair->pscanMask;
331*9999SWang.Lin@Sun.COM 		if (((regOrg & MULTI_DOMAIN_MASK) == 0) &&
332*9999SWang.Lin@Sun.COM 		    (flags != NO_REQ)) {
333*9999SWang.Lin@Sun.COM 			rd->flags = (uint32_t)flags; /* LINT */
334*9999SWang.Lin@Sun.COM 		}
335*9999SWang.Lin@Sun.COM 
336*9999SWang.Lin@Sun.COM 		rd->flags &= (channelFlag & CHANNEL_2GHZ) ?
337*9999SWang.Lin@Sun.COM 		    REG_DOMAIN_2GHZ_MASK : REG_DOMAIN_5GHZ_MASK;
338*9999SWang.Lin@Sun.COM 		return (B_TRUE);
339*9999SWang.Lin@Sun.COM 	}
340*9999SWang.Lin@Sun.COM }
341*9999SWang.Lin@Sun.COM 
342*9999SWang.Lin@Sun.COM static boolean_t
ath9k_regd_is_bit_set(int bit,uint64_t * bitmask)343*9999SWang.Lin@Sun.COM ath9k_regd_is_bit_set(int bit, uint64_t *bitmask)
344*9999SWang.Lin@Sun.COM {
345*9999SWang.Lin@Sun.COM 	int byteOffset, bitnum;
346*9999SWang.Lin@Sun.COM 	uint64_t val;
347*9999SWang.Lin@Sun.COM 
348*9999SWang.Lin@Sun.COM 	byteOffset = bit / 64;
349*9999SWang.Lin@Sun.COM 	bitnum = bit - byteOffset * 64;
350*9999SWang.Lin@Sun.COM 	val = ((uint64_t)1) << bitnum;
351*9999SWang.Lin@Sun.COM 	if (bitmask[byteOffset] & val)
352*9999SWang.Lin@Sun.COM 		return (B_TRUE);
353*9999SWang.Lin@Sun.COM 	else
354*9999SWang.Lin@Sun.COM 		return (B_FALSE);
355*9999SWang.Lin@Sun.COM }
356*9999SWang.Lin@Sun.COM 
357*9999SWang.Lin@Sun.COM static void
ath9k_regd_add_reg_classid(uint8_t * regclassids,uint32_t maxregids,uint32_t * nregids,uint8_t regclassid)358*9999SWang.Lin@Sun.COM ath9k_regd_add_reg_classid(uint8_t *regclassids, uint32_t maxregids,
359*9999SWang.Lin@Sun.COM     uint32_t *nregids, uint8_t regclassid)
360*9999SWang.Lin@Sun.COM {
361*9999SWang.Lin@Sun.COM 	int i;
362*9999SWang.Lin@Sun.COM 
363*9999SWang.Lin@Sun.COM 	if (regclassid == 0)
364*9999SWang.Lin@Sun.COM 		return;
365*9999SWang.Lin@Sun.COM 
366*9999SWang.Lin@Sun.COM 	for (i = 0; i < maxregids; i++) {
367*9999SWang.Lin@Sun.COM 		if (regclassids[i] == regclassid)
368*9999SWang.Lin@Sun.COM 			return;
369*9999SWang.Lin@Sun.COM 		if (regclassids[i] == 0)
370*9999SWang.Lin@Sun.COM 			break;
371*9999SWang.Lin@Sun.COM 	}
372*9999SWang.Lin@Sun.COM 
373*9999SWang.Lin@Sun.COM 	if (i == maxregids)
374*9999SWang.Lin@Sun.COM 		return;
375*9999SWang.Lin@Sun.COM 	else {
376*9999SWang.Lin@Sun.COM 		regclassids[i] = regclassid;
377*9999SWang.Lin@Sun.COM 		*nregids += 1;
378*9999SWang.Lin@Sun.COM 	}
379*9999SWang.Lin@Sun.COM }
380*9999SWang.Lin@Sun.COM 
381*9999SWang.Lin@Sun.COM static boolean_t
ath9k_regd_get_eeprom_reg_ext_bits(struct ath_hal * ah,enum reg_ext_bitmap bit)382*9999SWang.Lin@Sun.COM ath9k_regd_get_eeprom_reg_ext_bits(struct ath_hal *ah,
383*9999SWang.Lin@Sun.COM     enum reg_ext_bitmap bit)
384*9999SWang.Lin@Sun.COM {
385*9999SWang.Lin@Sun.COM 	return ((ah->ah_currentRDExt & (1 << bit)) ? B_TRUE : B_FALSE);
386*9999SWang.Lin@Sun.COM }
387*9999SWang.Lin@Sun.COM 
388*9999SWang.Lin@Sun.COM #ifdef ARN_NF_PER_CHAN
389*9999SWang.Lin@Sun.COM 
390*9999SWang.Lin@Sun.COM static void
ath9k_regd_init_rf_buffer(struct ath9k_channel * ichans,int nchans)391*9999SWang.Lin@Sun.COM ath9k_regd_init_rf_buffer(struct ath9k_channel *ichans, int nchans)
392*9999SWang.Lin@Sun.COM {
393*9999SWang.Lin@Sun.COM 	int i, j, next;
394*9999SWang.Lin@Sun.COM 
395*9999SWang.Lin@Sun.COM 	for (next = 0; next < nchans; next++) {
396*9999SWang.Lin@Sun.COM 		for (i = 0; i < NUM_NF_READINGS; i++) {
397*9999SWang.Lin@Sun.COM 			ichans[next].nfCalHist[i].currIndex = 0;
398*9999SWang.Lin@Sun.COM 			ichans[next].nfCalHist[i].privNF =
399*9999SWang.Lin@Sun.COM 			    AR_PHY_CCA_MAX_GOOD_VALUE;
400*9999SWang.Lin@Sun.COM 			ichans[next].nfCalHist[i].invalidNFcount =
401*9999SWang.Lin@Sun.COM 			    AR_PHY_CCA_FILTERWINDOW_LENGTH;
402*9999SWang.Lin@Sun.COM 			for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
403*9999SWang.Lin@Sun.COM 				ichans[next].nfCalHist[i].nfCalBuffer[j] =
404*9999SWang.Lin@Sun.COM 				    AR_PHY_CCA_MAX_GOOD_VALUE;
405*9999SWang.Lin@Sun.COM 			}
406*9999SWang.Lin@Sun.COM 		}
407*9999SWang.Lin@Sun.COM 	}
408*9999SWang.Lin@Sun.COM }
409*9999SWang.Lin@Sun.COM #endif
410*9999SWang.Lin@Sun.COM 
411*9999SWang.Lin@Sun.COM static int
ath9k_regd_is_chan_present(struct ath_hal * ah,uint16_t c)412*9999SWang.Lin@Sun.COM ath9k_regd_is_chan_present(struct ath_hal *ah, uint16_t c)
413*9999SWang.Lin@Sun.COM {
414*9999SWang.Lin@Sun.COM 	int i;
415*9999SWang.Lin@Sun.COM 
416*9999SWang.Lin@Sun.COM 	for (i = 0; i < 150; i++) {
417*9999SWang.Lin@Sun.COM 		if (!ah->ah_channels[i].channel)
418*9999SWang.Lin@Sun.COM 			return (-1);
419*9999SWang.Lin@Sun.COM 		else if (ah->ah_channels[i].channel == c)
420*9999SWang.Lin@Sun.COM 			return (i);
421*9999SWang.Lin@Sun.COM 	}
422*9999SWang.Lin@Sun.COM 
423*9999SWang.Lin@Sun.COM 	return (-1);
424*9999SWang.Lin@Sun.COM }
425*9999SWang.Lin@Sun.COM 
426*9999SWang.Lin@Sun.COM /* ARGSUSED */
427*9999SWang.Lin@Sun.COM static boolean_t
ath9k_regd_add_channel(struct ath_hal * ah,uint16_t c,uint16_t c_lo,uint16_t c_hi,uint16_t maxChan,uint8_t ctl,int pos,struct regDomain rd5GHz,struct RegDmnFreqBand * fband,struct regDomain * rd,const struct cmode * cm,struct ath9k_channel * ichans,boolean_t enableExtendedChannels)428*9999SWang.Lin@Sun.COM ath9k_regd_add_channel(
429*9999SWang.Lin@Sun.COM     struct ath_hal *ah,
430*9999SWang.Lin@Sun.COM     uint16_t c,
431*9999SWang.Lin@Sun.COM     uint16_t c_lo,
432*9999SWang.Lin@Sun.COM     uint16_t c_hi,
433*9999SWang.Lin@Sun.COM     uint16_t maxChan,
434*9999SWang.Lin@Sun.COM     uint8_t ctl,
435*9999SWang.Lin@Sun.COM     int pos,
436*9999SWang.Lin@Sun.COM     struct regDomain rd5GHz,
437*9999SWang.Lin@Sun.COM     struct RegDmnFreqBand *fband,
438*9999SWang.Lin@Sun.COM     struct regDomain *rd,
439*9999SWang.Lin@Sun.COM     const struct cmode *cm,
440*9999SWang.Lin@Sun.COM     struct ath9k_channel *ichans,
441*9999SWang.Lin@Sun.COM     boolean_t enableExtendedChannels)
442*9999SWang.Lin@Sun.COM {
443*9999SWang.Lin@Sun.COM 	struct ath9k_channel *chan;
444*9999SWang.Lin@Sun.COM 	int ret;
445*9999SWang.Lin@Sun.COM 	uint32_t channelFlags = 0;
446*9999SWang.Lin@Sun.COM 	uint8_t privFlags = 0;
447*9999SWang.Lin@Sun.COM 
448*9999SWang.Lin@Sun.COM 	if (!(c_lo <= c && c <= c_hi)) {
449*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY,
450*9999SWang.Lin@Sun.COM 		    "%s: c %u out of range [%u..%u]\n",
451*9999SWang.Lin@Sun.COM 		    __func__, c, c_lo, c_hi));
452*9999SWang.Lin@Sun.COM 		return (B_FALSE);
453*9999SWang.Lin@Sun.COM 	}
454*9999SWang.Lin@Sun.COM 	if ((fband->channelBW == CHANNEL_HALF_BW) &&
455*9999SWang.Lin@Sun.COM 	    !(ah->ah_caps.hw_caps & ATH9K_HW_CAP_CHAN_HALFRATE)) {
456*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY,
457*9999SWang.Lin@Sun.COM 		    "%s: Skipping %u half rate channel\n",
458*9999SWang.Lin@Sun.COM 		    __func__, c));
459*9999SWang.Lin@Sun.COM 		return (B_FALSE);
460*9999SWang.Lin@Sun.COM 	}
461*9999SWang.Lin@Sun.COM 
462*9999SWang.Lin@Sun.COM 	if ((fband->channelBW == CHANNEL_QUARTER_BW) &&
463*9999SWang.Lin@Sun.COM 	    !(ah->ah_caps.hw_caps & ATH9K_HW_CAP_CHAN_QUARTERRATE)) {
464*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY,
465*9999SWang.Lin@Sun.COM 		    "%s: Skipping %u quarter rate channel\n",
466*9999SWang.Lin@Sun.COM 		    __func__, c));
467*9999SWang.Lin@Sun.COM 		return (B_FALSE);
468*9999SWang.Lin@Sun.COM 	}
469*9999SWang.Lin@Sun.COM 
470*9999SWang.Lin@Sun.COM 	if (((c + fband->channelSep) / 2) > (maxChan + HALF_MAXCHANBW)) {
471*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY,
472*9999SWang.Lin@Sun.COM 		    "%s: c %u > maxChan %u\n",
473*9999SWang.Lin@Sun.COM 		    __func__, c, maxChan));
474*9999SWang.Lin@Sun.COM 		return (B_FALSE);
475*9999SWang.Lin@Sun.COM 	}
476*9999SWang.Lin@Sun.COM 
477*9999SWang.Lin@Sun.COM 	if ((fband->usePassScan & IS_ECM_CHAN) && !enableExtendedChannels) {
478*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY,
479*9999SWang.Lin@Sun.COM 		    "Skipping ecm channel\n"));
480*9999SWang.Lin@Sun.COM 		return (B_FALSE);
481*9999SWang.Lin@Sun.COM 	}
482*9999SWang.Lin@Sun.COM 
483*9999SWang.Lin@Sun.COM 	if ((rd->flags & NO_HOSTAP) && (ah->ah_opmode == ATH9K_M_HOSTAP)) {
484*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY,
485*9999SWang.Lin@Sun.COM 		    "Skipping HOSTAP channel\n"));
486*9999SWang.Lin@Sun.COM 		return (B_FALSE);
487*9999SWang.Lin@Sun.COM 	}
488*9999SWang.Lin@Sun.COM 
489*9999SWang.Lin@Sun.COM 	if (IS_HT40_MODE(cm->mode) &&
490*9999SWang.Lin@Sun.COM 	    !(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_FCC_DFS_HT40)) &&
491*9999SWang.Lin@Sun.COM 	    (fband->useDfs) &&
492*9999SWang.Lin@Sun.COM 	    (rd->conformanceTestLimit != MKK)) {
493*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY,
494*9999SWang.Lin@Sun.COM 		    "Skipping HT40 channel (en_fcc_dfs_ht40 = 0)\n"));
495*9999SWang.Lin@Sun.COM 		return (B_FALSE);
496*9999SWang.Lin@Sun.COM 	}
497*9999SWang.Lin@Sun.COM 
498*9999SWang.Lin@Sun.COM 	if (IS_HT40_MODE(cm->mode) &&
499*9999SWang.Lin@Sun.COM 	    !(ath9k_regd_get_eeprom_reg_ext_bits(ah,
500*9999SWang.Lin@Sun.COM 	    REG_EXT_JAPAN_NONDFS_HT40)) &&
501*9999SWang.Lin@Sun.COM 	    !(fband->useDfs) && (rd->conformanceTestLimit == MKK)) {
502*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY,
503*9999SWang.Lin@Sun.COM 		    "Skipping HT40 channel (en_jap_ht40 = 0)\n"));
504*9999SWang.Lin@Sun.COM 		return (B_FALSE);
505*9999SWang.Lin@Sun.COM 	}
506*9999SWang.Lin@Sun.COM 
507*9999SWang.Lin@Sun.COM 	if (IS_HT40_MODE(cm->mode) &&
508*9999SWang.Lin@Sun.COM 	    !(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_JAPAN_DFS_HT40)) &&
509*9999SWang.Lin@Sun.COM 	    (fband->useDfs) &&
510*9999SWang.Lin@Sun.COM 	    (rd->conformanceTestLimit == MKK)) {
511*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY,
512*9999SWang.Lin@Sun.COM 		    "Skipping HT40 channel (en_jap_dfs_ht40 = 0)\n"));
513*9999SWang.Lin@Sun.COM 		return (B_FALSE);
514*9999SWang.Lin@Sun.COM 	}
515*9999SWang.Lin@Sun.COM 
516*9999SWang.Lin@Sun.COM 	/* Calculate channel flags */
517*9999SWang.Lin@Sun.COM 
518*9999SWang.Lin@Sun.COM 	channelFlags = cm->flags;
519*9999SWang.Lin@Sun.COM 
520*9999SWang.Lin@Sun.COM 	switch (fband->channelBW) {
521*9999SWang.Lin@Sun.COM 	case CHANNEL_HALF_BW:
522*9999SWang.Lin@Sun.COM 		channelFlags |= CHANNEL_HALF;
523*9999SWang.Lin@Sun.COM 		break;
524*9999SWang.Lin@Sun.COM 	case CHANNEL_QUARTER_BW:
525*9999SWang.Lin@Sun.COM 		channelFlags |= CHANNEL_QUARTER;
526*9999SWang.Lin@Sun.COM 		break;
527*9999SWang.Lin@Sun.COM 	}
528*9999SWang.Lin@Sun.COM 
529*9999SWang.Lin@Sun.COM 	if (fband->usePassScan & rd->pscan)
530*9999SWang.Lin@Sun.COM 		channelFlags |= CHANNEL_PASSIVE;
531*9999SWang.Lin@Sun.COM 	else
532*9999SWang.Lin@Sun.COM 		channelFlags &= ~CHANNEL_PASSIVE;
533*9999SWang.Lin@Sun.COM 	if (fband->useDfs & rd->dfsMask)
534*9999SWang.Lin@Sun.COM 		privFlags = CHANNEL_DFS;
535*9999SWang.Lin@Sun.COM 	else
536*9999SWang.Lin@Sun.COM 		privFlags = 0;
537*9999SWang.Lin@Sun.COM 	if (rd->flags & LIMIT_FRAME_4MS)
538*9999SWang.Lin@Sun.COM 		privFlags |= CHANNEL_4MS_LIMIT;
539*9999SWang.Lin@Sun.COM 	if (privFlags & CHANNEL_DFS)
540*9999SWang.Lin@Sun.COM 		privFlags |= CHANNEL_DISALLOW_ADHOC;
541*9999SWang.Lin@Sun.COM 	if (rd->flags & ADHOC_PER_11D)
542*9999SWang.Lin@Sun.COM 		privFlags |= CHANNEL_PER_11D_ADHOC;
543*9999SWang.Lin@Sun.COM 
544*9999SWang.Lin@Sun.COM 	if (channelFlags & CHANNEL_PASSIVE) {
545*9999SWang.Lin@Sun.COM 		if ((c < 2412) || (c > 2462)) {
546*9999SWang.Lin@Sun.COM 			if (rd5GHz.regDmnEnum == MKK1 ||
547*9999SWang.Lin@Sun.COM 			    rd5GHz.regDmnEnum == MKK2) {
548*9999SWang.Lin@Sun.COM 				uint32_t regcap = ah->ah_caps.reg_cap;
549*9999SWang.Lin@Sun.COM 				if (!(regcap &
550*9999SWang.Lin@Sun.COM 				    (AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN |
551*9999SWang.Lin@Sun.COM 				    AR_EEPROM_EEREGCAP_EN_KK_U2 |
552*9999SWang.Lin@Sun.COM 				    AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) &&
553*9999SWang.Lin@Sun.COM 				    isUNII1OddChan(c)) {
554*9999SWang.Lin@Sun.COM 					channelFlags &= ~CHANNEL_PASSIVE;
555*9999SWang.Lin@Sun.COM 				} else {
556*9999SWang.Lin@Sun.COM 					privFlags |= CHANNEL_DISALLOW_ADHOC;
557*9999SWang.Lin@Sun.COM 				}
558*9999SWang.Lin@Sun.COM 			} else {
559*9999SWang.Lin@Sun.COM 				privFlags |= CHANNEL_DISALLOW_ADHOC;
560*9999SWang.Lin@Sun.COM 			}
561*9999SWang.Lin@Sun.COM 		}
562*9999SWang.Lin@Sun.COM 	}
563*9999SWang.Lin@Sun.COM 
564*9999SWang.Lin@Sun.COM 	if ((cm->mode == ATH9K_MODE_11A) ||
565*9999SWang.Lin@Sun.COM 	    (cm->mode == ATH9K_MODE_11NA_HT20) ||
566*9999SWang.Lin@Sun.COM 	    (cm->mode == ATH9K_MODE_11NA_HT40PLUS) ||
567*9999SWang.Lin@Sun.COM 	    (cm->mode == ATH9K_MODE_11NA_HT40MINUS)) {
568*9999SWang.Lin@Sun.COM 		if (rd->flags & (ADHOC_NO_11A | DISALLOW_ADHOC_11A))
569*9999SWang.Lin@Sun.COM 			privFlags |= CHANNEL_DISALLOW_ADHOC;
570*9999SWang.Lin@Sun.COM 	}
571*9999SWang.Lin@Sun.COM 
572*9999SWang.Lin@Sun.COM 	/* Fill in channel details */
573*9999SWang.Lin@Sun.COM 
574*9999SWang.Lin@Sun.COM 	ret = ath9k_regd_is_chan_present(ah, c);
575*9999SWang.Lin@Sun.COM 	if (ret == -1) {
576*9999SWang.Lin@Sun.COM 		chan = &ah->ah_channels[pos];
577*9999SWang.Lin@Sun.COM 		chan->channel = c;
578*9999SWang.Lin@Sun.COM 		chan->maxRegTxPower = fband->powerDfs;
579*9999SWang.Lin@Sun.COM 		chan->antennaMax = fband->antennaMax;
580*9999SWang.Lin@Sun.COM 		chan->regDmnFlags = rd->flags;
581*9999SWang.Lin@Sun.COM 		chan->maxTxPower = AR5416_MAX_RATE_POWER;
582*9999SWang.Lin@Sun.COM 		chan->minTxPower = AR5416_MAX_RATE_POWER;
583*9999SWang.Lin@Sun.COM 		chan->channelFlags = channelFlags;
584*9999SWang.Lin@Sun.COM 		chan->privFlags = privFlags;
585*9999SWang.Lin@Sun.COM 	} else {
586*9999SWang.Lin@Sun.COM 		chan = &ah->ah_channels[ret];
587*9999SWang.Lin@Sun.COM 		chan->channelFlags |= channelFlags;
588*9999SWang.Lin@Sun.COM 		chan->privFlags |= privFlags;
589*9999SWang.Lin@Sun.COM 	}
590*9999SWang.Lin@Sun.COM 
591*9999SWang.Lin@Sun.COM 	/* Set CTLs */
592*9999SWang.Lin@Sun.COM 
593*9999SWang.Lin@Sun.COM 	if ((cm->flags & CHANNEL_ALL) == CHANNEL_A)
594*9999SWang.Lin@Sun.COM 		chan->conformanceTestLimit[0] = ctl;
595*9999SWang.Lin@Sun.COM 	else if ((cm->flags & CHANNEL_ALL) == CHANNEL_B)
596*9999SWang.Lin@Sun.COM 		chan->conformanceTestLimit[1] = ctl;
597*9999SWang.Lin@Sun.COM 	else if ((cm->flags & CHANNEL_ALL) == CHANNEL_G)
598*9999SWang.Lin@Sun.COM 		chan->conformanceTestLimit[2] = ctl;
599*9999SWang.Lin@Sun.COM 
600*9999SWang.Lin@Sun.COM 	return ((ret == -1) ? B_TRUE : B_FALSE);
601*9999SWang.Lin@Sun.COM }
602*9999SWang.Lin@Sun.COM 
603*9999SWang.Lin@Sun.COM static boolean_t
ath9k_regd_japan_check(struct ath_hal * ah,int b,struct regDomain * rd5GHz)604*9999SWang.Lin@Sun.COM ath9k_regd_japan_check(struct ath_hal *ah, int b, struct regDomain *rd5GHz)
605*9999SWang.Lin@Sun.COM {
606*9999SWang.Lin@Sun.COM 	boolean_t skipband = B_FALSE;
607*9999SWang.Lin@Sun.COM 	int i;
608*9999SWang.Lin@Sun.COM 	uint32_t regcap;
609*9999SWang.Lin@Sun.COM 
610*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(j_bandcheck); i++) {
611*9999SWang.Lin@Sun.COM 		if (j_bandcheck[i].freqbandbit == b) {
612*9999SWang.Lin@Sun.COM 			regcap = ah->ah_caps.reg_cap;
613*9999SWang.Lin@Sun.COM 			if ((j_bandcheck[i].eepromflagtocheck & regcap) == 0) {
614*9999SWang.Lin@Sun.COM 				skipband = B_TRUE;
615*9999SWang.Lin@Sun.COM 			} else if ((regcap & AR_EEPROM_EEREGCAP_EN_KK_U2) ||
616*9999SWang.Lin@Sun.COM 			    (regcap & AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) {
617*9999SWang.Lin@Sun.COM 				rd5GHz->dfsMask |= DFS_MKK4;
618*9999SWang.Lin@Sun.COM 				rd5GHz->pscan |= PSCAN_MKK3;
619*9999SWang.Lin@Sun.COM 			}
620*9999SWang.Lin@Sun.COM 			break;
621*9999SWang.Lin@Sun.COM 		}
622*9999SWang.Lin@Sun.COM 	}
623*9999SWang.Lin@Sun.COM 
624*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_REGULATORY,
625*9999SWang.Lin@Sun.COM 	    "%s: Skipping %d freq band\n",
626*9999SWang.Lin@Sun.COM 	    __func__, j_bandcheck[i].freqbandbit));
627*9999SWang.Lin@Sun.COM 
628*9999SWang.Lin@Sun.COM 	return (skipband);
629*9999SWang.Lin@Sun.COM }
630*9999SWang.Lin@Sun.COM 
631*9999SWang.Lin@Sun.COM boolean_t
ath9k_regd_init_channels(struct ath_hal * ah,uint32_t maxchans,uint32_t * nchans,uint8_t * regclassids,uint32_t maxregids,uint32_t * nregids,uint16_t cc,boolean_t enableOutdoor,boolean_t enableExtendedChannels)632*9999SWang.Lin@Sun.COM ath9k_regd_init_channels(
633*9999SWang.Lin@Sun.COM     struct ath_hal *ah,
634*9999SWang.Lin@Sun.COM     uint32_t maxchans,
635*9999SWang.Lin@Sun.COM     uint32_t *nchans,
636*9999SWang.Lin@Sun.COM     uint8_t *regclassids,
637*9999SWang.Lin@Sun.COM     uint32_t maxregids,
638*9999SWang.Lin@Sun.COM     uint32_t *nregids,
639*9999SWang.Lin@Sun.COM     uint16_t cc,
640*9999SWang.Lin@Sun.COM     boolean_t enableOutdoor,
641*9999SWang.Lin@Sun.COM     boolean_t enableExtendedChannels)
642*9999SWang.Lin@Sun.COM {
643*9999SWang.Lin@Sun.COM 	uint16_t maxChan = 7000;
644*9999SWang.Lin@Sun.COM 	struct country_code_to_enum_rd *country = NULL;
645*9999SWang.Lin@Sun.COM 	struct regDomain rd5GHz, rd2GHz;
646*9999SWang.Lin@Sun.COM 	const struct cmode *cm;
647*9999SWang.Lin@Sun.COM 	struct ath9k_channel *ichans = &ah->ah_channels[0];
648*9999SWang.Lin@Sun.COM 	int next = 0, b;
649*9999SWang.Lin@Sun.COM 	uint8_t ctl;
650*9999SWang.Lin@Sun.COM 	int regdmn;
651*9999SWang.Lin@Sun.COM 	uint16_t chanSep;
652*9999SWang.Lin@Sun.COM 	uint8_t *modes_avail;
653*9999SWang.Lin@Sun.COM 	uint8_t modes_allowed[4];
654*9999SWang.Lin@Sun.COM 
655*9999SWang.Lin@Sun.COM 	(void) memset(modes_allowed, 0, sizeof (modes_allowed));
656*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_REGULATORY, "arn: ath9k_regd_init_channels(): "
657*9999SWang.Lin@Sun.COM 	    "cc %u %s %s\n",
658*9999SWang.Lin@Sun.COM 	    cc,
659*9999SWang.Lin@Sun.COM 	    enableOutdoor ? "Enable outdoor" : "",
660*9999SWang.Lin@Sun.COM 	    enableExtendedChannels ? "Enable ecm" : ""));
661*9999SWang.Lin@Sun.COM 
662*9999SWang.Lin@Sun.COM 	if (!ath9k_regd_is_ccode_valid(ah, cc)) {
663*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_XMIT, "arn: ath9k_regd_init_channels(): "
664*9999SWang.Lin@Sun.COM 		    "invalid country code %d\n", cc));
665*9999SWang.Lin@Sun.COM 		return (B_FALSE);
666*9999SWang.Lin@Sun.COM 	}
667*9999SWang.Lin@Sun.COM 
668*9999SWang.Lin@Sun.COM 	if (!ath9k_regd_is_eeprom_valid(ah)) {
669*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ANY, "arn: ath9k_regd_init_channels(): "
670*9999SWang.Lin@Sun.COM 		    "invalid EEPROM contents\n"));
671*9999SWang.Lin@Sun.COM 		return (B_FALSE);
672*9999SWang.Lin@Sun.COM 	}
673*9999SWang.Lin@Sun.COM 
674*9999SWang.Lin@Sun.COM 	ah->ah_countryCode = ath9k_regd_get_default_country(ah);
675*9999SWang.Lin@Sun.COM 
676*9999SWang.Lin@Sun.COM 	if (ah->ah_countryCode == CTRY_DEFAULT) {
677*9999SWang.Lin@Sun.COM 		ah->ah_countryCode = cc & COUNTRY_CODE_MASK;
678*9999SWang.Lin@Sun.COM 		if ((ah->ah_countryCode == CTRY_DEFAULT) &&
679*9999SWang.Lin@Sun.COM 		    (ath9k_regd_get_eepromRD(ah) == CTRY_DEFAULT)) {
680*9999SWang.Lin@Sun.COM 			ah->ah_countryCode = CTRY_UNITED_STATES;
681*9999SWang.Lin@Sun.COM 		}
682*9999SWang.Lin@Sun.COM 	}
683*9999SWang.Lin@Sun.COM 
684*9999SWang.Lin@Sun.COM #ifdef ARN_SUPPORT_11D
685*9999SWang.Lin@Sun.COM 	if (ah->ah_countryCode == CTRY_DEFAULT) {
686*9999SWang.Lin@Sun.COM 		regdmn = ath9k_regd_get_eepromRD(ah);
687*9999SWang.Lin@Sun.COM 		country = NULL;
688*9999SWang.Lin@Sun.COM 	} else {
689*9999SWang.Lin@Sun.COM #endif
690*9999SWang.Lin@Sun.COM 		country = ath9k_regd_find_country(ah->ah_countryCode);
691*9999SWang.Lin@Sun.COM 		if (country == NULL) {
692*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_REGULATORY,
693*9999SWang.Lin@Sun.COM 			    "arn: ath9k_regd_init_channels(): "
694*9999SWang.Lin@Sun.COM 			    "Country is NULL!!!!, cc= %d\n",
695*9999SWang.Lin@Sun.COM 			    ah->ah_countryCode));
696*9999SWang.Lin@Sun.COM 
697*9999SWang.Lin@Sun.COM 			return (B_FALSE);
698*9999SWang.Lin@Sun.COM 		} else {
699*9999SWang.Lin@Sun.COM 			regdmn = country->regDmnEnum;
700*9999SWang.Lin@Sun.COM #ifdef ARN_SUPPORT_11D
701*9999SWang.Lin@Sun.COM 			if (((ath9k_regd_get_eepromRD(ah) &
702*9999SWang.Lin@Sun.COM 			    WORLD_SKU_MASK) == WORLD_SKU_PREFIX) &&
703*9999SWang.Lin@Sun.COM 			    (cc == CTRY_UNITED_STATES)) {
704*9999SWang.Lin@Sun.COM 				if (!isWwrSKU_NoMidband(ah) &&
705*9999SWang.Lin@Sun.COM 				    ath9k_regd_is_fcc_midband_supported(ah))
706*9999SWang.Lin@Sun.COM 					regdmn = FCC3_FCCA;
707*9999SWang.Lin@Sun.COM 				else
708*9999SWang.Lin@Sun.COM 					regdmn = FCC1_FCCA;
709*9999SWang.Lin@Sun.COM 			}
710*9999SWang.Lin@Sun.COM #endif
711*9999SWang.Lin@Sun.COM 		}
712*9999SWang.Lin@Sun.COM #ifdef ARN_SUPPORT_11D
713*9999SWang.Lin@Sun.COM 	}
714*9999SWang.Lin@Sun.COM #endif
715*9999SWang.Lin@Sun.COM 	if (!ath9k_regd_get_wmode_regdomain(ah, regdmn,
716*9999SWang.Lin@Sun.COM 	    ~CHANNEL_2GHZ, &rd5GHz)) {
717*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY, "arn: ath9k_regd_init_channels(): "
718*9999SWang.Lin@Sun.COM 		    "couldn't find unitary "
719*9999SWang.Lin@Sun.COM 		    "5GHz reg domain for country %u\n",
720*9999SWang.Lin@Sun.COM 		    ah->ah_countryCode));
721*9999SWang.Lin@Sun.COM 		return (B_FALSE);
722*9999SWang.Lin@Sun.COM 	}
723*9999SWang.Lin@Sun.COM 	if (!ath9k_regd_get_wmode_regdomain(ah, regdmn,
724*9999SWang.Lin@Sun.COM 	    CHANNEL_2GHZ, &rd2GHz)) {
725*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY, "arn: ath9k_regd_init_channels(): "
726*9999SWang.Lin@Sun.COM 		    "couldn't find unitary 2GHz "
727*9999SWang.Lin@Sun.COM 		    "reg domain for country %u\n",
728*9999SWang.Lin@Sun.COM 		    ah->ah_countryCode));
729*9999SWang.Lin@Sun.COM 		return (B_FALSE);
730*9999SWang.Lin@Sun.COM 	}
731*9999SWang.Lin@Sun.COM 
732*9999SWang.Lin@Sun.COM 	if (!isWwrSKU(ah) && ((rd5GHz.regDmnEnum == FCC1) ||
733*9999SWang.Lin@Sun.COM 	    (rd5GHz.regDmnEnum == FCC2))) {
734*9999SWang.Lin@Sun.COM 		if (ath9k_regd_is_fcc_midband_supported(ah)) {
735*9999SWang.Lin@Sun.COM 			if (!ath9k_regd_get_wmode_regdomain(ah,
736*9999SWang.Lin@Sun.COM 			    FCC3_FCCA, ~CHANNEL_2GHZ, &rd5GHz)) {
737*9999SWang.Lin@Sun.COM 				ARN_DBG((ARN_DBG_REGULATORY,
738*9999SWang.Lin@Sun.COM 				    "arn: ath9k_regd_init_channels(): "
739*9999SWang.Lin@Sun.COM 				    "couldn't find unitary 5GHz "
740*9999SWang.Lin@Sun.COM 				    "reg domain for country %u\n",
741*9999SWang.Lin@Sun.COM 				    ah->ah_countryCode));
742*9999SWang.Lin@Sun.COM 				return (B_FALSE);
743*9999SWang.Lin@Sun.COM 			}
744*9999SWang.Lin@Sun.COM 		}
745*9999SWang.Lin@Sun.COM 	}
746*9999SWang.Lin@Sun.COM 
747*9999SWang.Lin@Sun.COM 	if (country == NULL) {
748*9999SWang.Lin@Sun.COM 		modes_avail = ah->ah_caps.wireless_modes;
749*9999SWang.Lin@Sun.COM 	} else {
750*9999SWang.Lin@Sun.COM 		ath9k_regd_get_wmodes_nreg(ah, country, &rd5GHz, modes_allowed);
751*9999SWang.Lin@Sun.COM 		modes_avail = modes_allowed;
752*9999SWang.Lin@Sun.COM 
753*9999SWang.Lin@Sun.COM 		if (!enableOutdoor)
754*9999SWang.Lin@Sun.COM 			maxChan = country->outdoorChanStart;
755*9999SWang.Lin@Sun.COM 	}
756*9999SWang.Lin@Sun.COM 
757*9999SWang.Lin@Sun.COM 	next = 0;
758*9999SWang.Lin@Sun.COM 
759*9999SWang.Lin@Sun.COM 	if (maxchans > ARRAY_SIZE(ah->ah_channels))
760*9999SWang.Lin@Sun.COM 		maxchans = ARRAY_SIZE(ah->ah_channels);
761*9999SWang.Lin@Sun.COM 
762*9999SWang.Lin@Sun.COM 	for (cm = modes; cm < &modes[ARRAY_SIZE(modes)]; cm++) {
763*9999SWang.Lin@Sun.COM 		uint16_t c, c_hi, c_lo;
764*9999SWang.Lin@Sun.COM 		uint64_t *channelBM = NULL;
765*9999SWang.Lin@Sun.COM 		struct regDomain *rd = NULL;
766*9999SWang.Lin@Sun.COM 		struct RegDmnFreqBand *fband = NULL, *freqs;
767*9999SWang.Lin@Sun.COM 		int8_t low_adj = 0, hi_adj = 0;
768*9999SWang.Lin@Sun.COM 
769*9999SWang.Lin@Sun.COM 		if (!is_set(cm->mode, modes_avail)) {
770*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_REGULATORY,
771*9999SWang.Lin@Sun.COM 			    "%s: !avail mode %d flags 0x%x\n",
772*9999SWang.Lin@Sun.COM 			    __func__, cm->mode, cm->flags));
773*9999SWang.Lin@Sun.COM 			continue;
774*9999SWang.Lin@Sun.COM 		}
775*9999SWang.Lin@Sun.COM 		if (!ath9k_get_channel_edges(ah, cm->flags, &c_lo, &c_hi)) {
776*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_REGULATORY,
777*9999SWang.Lin@Sun.COM 			    "arn: ath9k_regd_init_channels(): "
778*9999SWang.Lin@Sun.COM 			    "channels 0x%x not supported "
779*9999SWang.Lin@Sun.COM 			    "by hardware\n", cm->flags));
780*9999SWang.Lin@Sun.COM 			continue;
781*9999SWang.Lin@Sun.COM 		}
782*9999SWang.Lin@Sun.COM 
783*9999SWang.Lin@Sun.COM 		switch (cm->mode) {
784*9999SWang.Lin@Sun.COM 		case ATH9K_MODE_11A:
785*9999SWang.Lin@Sun.COM 		case ATH9K_MODE_11NA_HT20:
786*9999SWang.Lin@Sun.COM 		case ATH9K_MODE_11NA_HT40PLUS:
787*9999SWang.Lin@Sun.COM 		case ATH9K_MODE_11NA_HT40MINUS:
788*9999SWang.Lin@Sun.COM 			rd = &rd5GHz;
789*9999SWang.Lin@Sun.COM 			channelBM = rd->chan11a;
790*9999SWang.Lin@Sun.COM 			freqs = &regDmn5GhzFreq[0];
791*9999SWang.Lin@Sun.COM 			ctl = rd->conformanceTestLimit;
792*9999SWang.Lin@Sun.COM 			break;
793*9999SWang.Lin@Sun.COM 		case ATH9K_MODE_11B:
794*9999SWang.Lin@Sun.COM 			rd = &rd2GHz;
795*9999SWang.Lin@Sun.COM 			channelBM = rd->chan11b;
796*9999SWang.Lin@Sun.COM 			freqs = &regDmn2GhzFreq[0];
797*9999SWang.Lin@Sun.COM 			ctl = rd->conformanceTestLimit | CTL_11B;
798*9999SWang.Lin@Sun.COM 			break;
799*9999SWang.Lin@Sun.COM 		case ATH9K_MODE_11G:
800*9999SWang.Lin@Sun.COM 		case ATH9K_MODE_11NG_HT20:
801*9999SWang.Lin@Sun.COM 		case ATH9K_MODE_11NG_HT40PLUS:
802*9999SWang.Lin@Sun.COM 		case ATH9K_MODE_11NG_HT40MINUS:
803*9999SWang.Lin@Sun.COM 			rd = &rd2GHz;
804*9999SWang.Lin@Sun.COM 			channelBM = rd->chan11g;
805*9999SWang.Lin@Sun.COM 			freqs = &regDmn2Ghz11gFreq[0];
806*9999SWang.Lin@Sun.COM 			ctl = rd->conformanceTestLimit | CTL_11G;
807*9999SWang.Lin@Sun.COM 			break;
808*9999SWang.Lin@Sun.COM 		default:
809*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_REGULATORY,
810*9999SWang.Lin@Sun.COM 			    "arn: ath9k_regd_init_channels(): "
811*9999SWang.Lin@Sun.COM 			    "Unknown HAL mode 0x%x\n", cm->mode));
812*9999SWang.Lin@Sun.COM 			continue;
813*9999SWang.Lin@Sun.COM 		}
814*9999SWang.Lin@Sun.COM 
815*9999SWang.Lin@Sun.COM 		if (ath9k_regd_is_chan_bm_zero(channelBM))
816*9999SWang.Lin@Sun.COM 			continue;
817*9999SWang.Lin@Sun.COM 
818*9999SWang.Lin@Sun.COM 		if ((cm->mode == ATH9K_MODE_11NA_HT40PLUS) ||
819*9999SWang.Lin@Sun.COM 		    (cm->mode == ATH9K_MODE_11NG_HT40PLUS)) {
820*9999SWang.Lin@Sun.COM 			hi_adj = -20;
821*9999SWang.Lin@Sun.COM 		}
822*9999SWang.Lin@Sun.COM 
823*9999SWang.Lin@Sun.COM 		if ((cm->mode == ATH9K_MODE_11NA_HT40MINUS) ||
824*9999SWang.Lin@Sun.COM 		    (cm->mode == ATH9K_MODE_11NG_HT40MINUS)) {
825*9999SWang.Lin@Sun.COM 			low_adj = 20;
826*9999SWang.Lin@Sun.COM 		}
827*9999SWang.Lin@Sun.COM 
828*9999SWang.Lin@Sun.COM 		/* XXX: Add a helper here instead */
829*9999SWang.Lin@Sun.COM 		for (b = 0; b < 64 * BMLEN; b++) {
830*9999SWang.Lin@Sun.COM 			if (ath9k_regd_is_bit_set(b, channelBM)) {
831*9999SWang.Lin@Sun.COM 				fband = &freqs[b];
832*9999SWang.Lin@Sun.COM 				if (rd5GHz.regDmnEnum == MKK1 ||
833*9999SWang.Lin@Sun.COM 				    rd5GHz.regDmnEnum == MKK2) {
834*9999SWang.Lin@Sun.COM 					if (ath9k_regd_japan_check(ah,
835*9999SWang.Lin@Sun.COM 					    b, &rd5GHz))
836*9999SWang.Lin@Sun.COM 						continue;
837*9999SWang.Lin@Sun.COM 				}
838*9999SWang.Lin@Sun.COM 
839*9999SWang.Lin@Sun.COM 				ath9k_regd_add_reg_classid(regclassids,
840*9999SWang.Lin@Sun.COM 				    maxregids,
841*9999SWang.Lin@Sun.COM 				    nregids,
842*9999SWang.Lin@Sun.COM 				    fband->regClassId);
843*9999SWang.Lin@Sun.COM 
844*9999SWang.Lin@Sun.COM 				if (IS_HT40_MODE(cm->mode) && (rd == &rd5GHz)) {
845*9999SWang.Lin@Sun.COM 					chanSep = 40;
846*9999SWang.Lin@Sun.COM 					if (fband->lowChannel == 5280)
847*9999SWang.Lin@Sun.COM 						low_adj += 20;
848*9999SWang.Lin@Sun.COM 
849*9999SWang.Lin@Sun.COM 					if (fband->lowChannel == 5170)
850*9999SWang.Lin@Sun.COM 						continue;
851*9999SWang.Lin@Sun.COM 				} else
852*9999SWang.Lin@Sun.COM 					chanSep = fband->channelSep;
853*9999SWang.Lin@Sun.COM 
854*9999SWang.Lin@Sun.COM 				for (c = fband->lowChannel + low_adj;
855*9999SWang.Lin@Sun.COM 				    ((c <= (fband->highChannel + hi_adj)) &&
856*9999SWang.Lin@Sun.COM 				    (c >= (fband->lowChannel + low_adj)));
857*9999SWang.Lin@Sun.COM 				    c += chanSep) {
858*9999SWang.Lin@Sun.COM 					if (next >= maxchans) {
859*9999SWang.Lin@Sun.COM 						ARN_DBG((ARN_DBG_REGULATORY,
860*9999SWang.Lin@Sun.COM 						    "too many channels "
861*9999SWang.Lin@Sun.COM 						    "for channel table\n"));
862*9999SWang.Lin@Sun.COM 						goto done;
863*9999SWang.Lin@Sun.COM 					}
864*9999SWang.Lin@Sun.COM 					if (ath9k_regd_add_channel(ah,
865*9999SWang.Lin@Sun.COM 					    c, c_lo, c_hi,
866*9999SWang.Lin@Sun.COM 					    maxChan, ctl,
867*9999SWang.Lin@Sun.COM 					    next,
868*9999SWang.Lin@Sun.COM 					    rd5GHz,
869*9999SWang.Lin@Sun.COM 					    fband, rd, cm,
870*9999SWang.Lin@Sun.COM 					    ichans,
871*9999SWang.Lin@Sun.COM 					    enableExtendedChannels))
872*9999SWang.Lin@Sun.COM 						next++;
873*9999SWang.Lin@Sun.COM 				}
874*9999SWang.Lin@Sun.COM 				if (IS_HT40_MODE(cm->mode) &&
875*9999SWang.Lin@Sun.COM 				    (fband->lowChannel == 5280)) {
876*9999SWang.Lin@Sun.COM 					low_adj -= 20;
877*9999SWang.Lin@Sun.COM 				}
878*9999SWang.Lin@Sun.COM 			}
879*9999SWang.Lin@Sun.COM 		}
880*9999SWang.Lin@Sun.COM 	}
881*9999SWang.Lin@Sun.COM done:
882*9999SWang.Lin@Sun.COM 	if (next != 0) {
883*9999SWang.Lin@Sun.COM 		int i;
884*9999SWang.Lin@Sun.COM 
885*9999SWang.Lin@Sun.COM 		if (next > ARRAY_SIZE(ah->ah_channels)) {
886*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_REGULATORY,
887*9999SWang.Lin@Sun.COM 			    "arn: ath9k_regd_init_channels(): "
888*9999SWang.Lin@Sun.COM 			    "too many channels %u; truncating to %u\n",
889*9999SWang.Lin@Sun.COM 			    next, (int)ARRAY_SIZE(ah->ah_channels)));
890*9999SWang.Lin@Sun.COM 			next = ARRAY_SIZE(ah->ah_channels);
891*9999SWang.Lin@Sun.COM 		}
892*9999SWang.Lin@Sun.COM #ifdef ARN_NF_PER_CHAN
893*9999SWang.Lin@Sun.COM 		ath9k_regd_init_rf_buffer(ichans, next);
894*9999SWang.Lin@Sun.COM #endif
895*9999SWang.Lin@Sun.COM 		ath9k_regd_sort(ichans, next, sizeof (struct ath9k_channel),
896*9999SWang.Lin@Sun.COM 		    ath9k_regd_chansort);
897*9999SWang.Lin@Sun.COM 
898*9999SWang.Lin@Sun.COM 		ah->ah_nchan = next;
899*9999SWang.Lin@Sun.COM 
900*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY, "arn: ath9k_regd_init_channels(): "
901*9999SWang.Lin@Sun.COM 		    "Channel list:\n"));
902*9999SWang.Lin@Sun.COM 		for (i = 0; i < next; i++) {
903*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_REGULATORY, "arn: "
904*9999SWang.Lin@Sun.COM 			    "chan: %d flags: 0x%x\n",
905*9999SWang.Lin@Sun.COM 			    ah->ah_channels[i].channel,
906*9999SWang.Lin@Sun.COM 			    ah->ah_channels[i].channelFlags));
907*9999SWang.Lin@Sun.COM 		}
908*9999SWang.Lin@Sun.COM 	}
909*9999SWang.Lin@Sun.COM 	*nchans = next;
910*9999SWang.Lin@Sun.COM 
911*9999SWang.Lin@Sun.COM 	ah->ah_countryCode = ah->ah_countryCode;
912*9999SWang.Lin@Sun.COM 
913*9999SWang.Lin@Sun.COM 	ah->ah_currentRDInUse = (uint16_t)regdmn; /* LINT */
914*9999SWang.Lin@Sun.COM 	ah->ah_currentRD5G = rd5GHz.regDmnEnum;
915*9999SWang.Lin@Sun.COM 	ah->ah_currentRD2G = rd2GHz.regDmnEnum;
916*9999SWang.Lin@Sun.COM 	if (country == NULL) {
917*9999SWang.Lin@Sun.COM 		ah->ah_iso[0] = 0;
918*9999SWang.Lin@Sun.COM 		ah->ah_iso[1] = 0;
919*9999SWang.Lin@Sun.COM 	} else {
920*9999SWang.Lin@Sun.COM 		ah->ah_iso[0] = country->isoName[0];
921*9999SWang.Lin@Sun.COM 		ah->ah_iso[1] = country->isoName[1];
922*9999SWang.Lin@Sun.COM 	}
923*9999SWang.Lin@Sun.COM 
924*9999SWang.Lin@Sun.COM 	return (next != 0);
925*9999SWang.Lin@Sun.COM }
926*9999SWang.Lin@Sun.COM 
927*9999SWang.Lin@Sun.COM struct ath9k_channel *
ath9k_regd_check_channel(struct ath_hal * ah,const struct ath9k_channel * c)928*9999SWang.Lin@Sun.COM ath9k_regd_check_channel(struct ath_hal *ah, const struct ath9k_channel *c)
929*9999SWang.Lin@Sun.COM {
930*9999SWang.Lin@Sun.COM 	struct ath9k_channel *base, *cc;
931*9999SWang.Lin@Sun.COM 
932*9999SWang.Lin@Sun.COM 	int flags = c->channelFlags & CHAN_FLAGS;
933*9999SWang.Lin@Sun.COM 	int n, lim;
934*9999SWang.Lin@Sun.COM 
935*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_REGULATORY, "arn: "
936*9999SWang.Lin@Sun.COM 	    "%s: channel %u/0x%x (0x%x) requested\n", __func__,
937*9999SWang.Lin@Sun.COM 	    c->channel, c->channelFlags, flags));
938*9999SWang.Lin@Sun.COM 
939*9999SWang.Lin@Sun.COM 	cc = ah->ah_curchan;
940*9999SWang.Lin@Sun.COM 	if (cc != NULL && cc->channel == c->channel &&
941*9999SWang.Lin@Sun.COM 	    (cc->channelFlags & CHAN_FLAGS) == flags) {
942*9999SWang.Lin@Sun.COM 		if ((cc->privFlags & CHANNEL_INTERFERENCE) &&
943*9999SWang.Lin@Sun.COM 		    (cc->privFlags & CHANNEL_DFS))
944*9999SWang.Lin@Sun.COM 			return (NULL);
945*9999SWang.Lin@Sun.COM 		else
946*9999SWang.Lin@Sun.COM 			return (cc);
947*9999SWang.Lin@Sun.COM 	}
948*9999SWang.Lin@Sun.COM 
949*9999SWang.Lin@Sun.COM 	base = ah->ah_channels;
950*9999SWang.Lin@Sun.COM 	n = ah->ah_nchan;
951*9999SWang.Lin@Sun.COM 
952*9999SWang.Lin@Sun.COM 	for (lim = n; lim != 0; lim >>= 1) {
953*9999SWang.Lin@Sun.COM 		int d;
954*9999SWang.Lin@Sun.COM 		cc = &base[lim >> 1];
955*9999SWang.Lin@Sun.COM 		d = c->channel - cc->channel;
956*9999SWang.Lin@Sun.COM 		if (d == 0) {
957*9999SWang.Lin@Sun.COM 			if ((cc->channelFlags & CHAN_FLAGS) == flags) {
958*9999SWang.Lin@Sun.COM 				if ((cc->privFlags & CHANNEL_INTERFERENCE) &&
959*9999SWang.Lin@Sun.COM 				    (cc->privFlags & CHANNEL_DFS))
960*9999SWang.Lin@Sun.COM 					return (NULL);
961*9999SWang.Lin@Sun.COM 				else
962*9999SWang.Lin@Sun.COM 					return (cc);
963*9999SWang.Lin@Sun.COM 			}
964*9999SWang.Lin@Sun.COM 			d = flags - (cc->channelFlags & CHAN_FLAGS);
965*9999SWang.Lin@Sun.COM 		}
966*9999SWang.Lin@Sun.COM 
967*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_REGULATORY, "arn: "
968*9999SWang.Lin@Sun.COM 		    "%s: channel %u/0x%x d %d\n", __func__,
969*9999SWang.Lin@Sun.COM 		    cc->channel, cc->channelFlags, d));
970*9999SWang.Lin@Sun.COM 
971*9999SWang.Lin@Sun.COM 		if (d > 0) {
972*9999SWang.Lin@Sun.COM 			base = cc + 1;
973*9999SWang.Lin@Sun.COM 			lim--;
974*9999SWang.Lin@Sun.COM 		}
975*9999SWang.Lin@Sun.COM 	}
976*9999SWang.Lin@Sun.COM 
977*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_REGULATORY, "arn: "
978*9999SWang.Lin@Sun.COM 	    "%s: no match for %u/0x%x\n",
979*9999SWang.Lin@Sun.COM 	    __func__, c->channel, c->channelFlags));
980*9999SWang.Lin@Sun.COM 
981*9999SWang.Lin@Sun.COM 	return (NULL);
982*9999SWang.Lin@Sun.COM }
983*9999SWang.Lin@Sun.COM 
984*9999SWang.Lin@Sun.COM uint32_t
ath9k_regd_get_antenna_allowed(struct ath_hal * ah,struct ath9k_channel * chan)985*9999SWang.Lin@Sun.COM ath9k_regd_get_antenna_allowed(struct ath_hal *ah, struct ath9k_channel *chan)
986*9999SWang.Lin@Sun.COM {
987*9999SWang.Lin@Sun.COM 	struct ath9k_channel *ichan = NULL;
988*9999SWang.Lin@Sun.COM 
989*9999SWang.Lin@Sun.COM 	ichan = ath9k_regd_check_channel(ah, chan);
990*9999SWang.Lin@Sun.COM 	if (!ichan)
991*9999SWang.Lin@Sun.COM 		return (0);
992*9999SWang.Lin@Sun.COM 
993*9999SWang.Lin@Sun.COM 	return (ichan->antennaMax);
994*9999SWang.Lin@Sun.COM }
995*9999SWang.Lin@Sun.COM 
996*9999SWang.Lin@Sun.COM uint32_t
ath9k_regd_get_ctl(struct ath_hal * ah,struct ath9k_channel * chan)997*9999SWang.Lin@Sun.COM ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan)
998*9999SWang.Lin@Sun.COM {
999*9999SWang.Lin@Sun.COM 	uint32_t ctl = NO_CTL;
1000*9999SWang.Lin@Sun.COM 	struct ath9k_channel *ichan;
1001*9999SWang.Lin@Sun.COM 
1002*9999SWang.Lin@Sun.COM 	if (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)) {
1003*9999SWang.Lin@Sun.COM 		if (IS_CHAN_B(chan))
1004*9999SWang.Lin@Sun.COM 			ctl = SD_NO_CTL | CTL_11B;
1005*9999SWang.Lin@Sun.COM 		else if (IS_CHAN_G(chan))
1006*9999SWang.Lin@Sun.COM 			ctl = SD_NO_CTL | CTL_11G;
1007*9999SWang.Lin@Sun.COM 		else
1008*9999SWang.Lin@Sun.COM 			ctl = SD_NO_CTL | CTL_11A;
1009*9999SWang.Lin@Sun.COM 	} else {
1010*9999SWang.Lin@Sun.COM 		ichan = ath9k_regd_check_channel(ah, chan);
1011*9999SWang.Lin@Sun.COM 		if (ichan != NULL) {
1012*9999SWang.Lin@Sun.COM 			/* FIXME */
1013*9999SWang.Lin@Sun.COM 			if (IS_CHAN_A(ichan))
1014*9999SWang.Lin@Sun.COM 				ctl = ichan->conformanceTestLimit[0];
1015*9999SWang.Lin@Sun.COM 			else if (IS_CHAN_B(ichan))
1016*9999SWang.Lin@Sun.COM 				ctl = ichan->conformanceTestLimit[1];
1017*9999SWang.Lin@Sun.COM 			else if (IS_CHAN_G(ichan))
1018*9999SWang.Lin@Sun.COM 				ctl = ichan->conformanceTestLimit[2];
1019*9999SWang.Lin@Sun.COM 
1020*9999SWang.Lin@Sun.COM 			if (IS_CHAN_G(chan) && (ctl & 0xf) == CTL_11B)
1021*9999SWang.Lin@Sun.COM 				ctl = (ctl & ~0xf) | CTL_11G;
1022*9999SWang.Lin@Sun.COM 		}
1023*9999SWang.Lin@Sun.COM 	}
1024*9999SWang.Lin@Sun.COM 	return (ctl);
1025*9999SWang.Lin@Sun.COM }
1026*9999SWang.Lin@Sun.COM 
1027*9999SWang.Lin@Sun.COM void
ath9k_regd_get_current_country(struct ath_hal * ah,struct ath9k_country_entry * ctry)1028*9999SWang.Lin@Sun.COM ath9k_regd_get_current_country(struct ath_hal *ah,
1029*9999SWang.Lin@Sun.COM     struct ath9k_country_entry *ctry)
1030*9999SWang.Lin@Sun.COM {
1031*9999SWang.Lin@Sun.COM 	uint16_t rd = ath9k_regd_get_eepromRD(ah);
1032*9999SWang.Lin@Sun.COM 
1033*9999SWang.Lin@Sun.COM 	ctry->isMultidomain = B_FALSE;
1034*9999SWang.Lin@Sun.COM 	if (rd == CTRY_DEFAULT)
1035*9999SWang.Lin@Sun.COM 		ctry->isMultidomain = B_TRUE;
1036*9999SWang.Lin@Sun.COM 	else if (!(rd & COUNTRY_ERD_FLAG))
1037*9999SWang.Lin@Sun.COM 		ctry->isMultidomain = isWwrSKU(ah);
1038*9999SWang.Lin@Sun.COM 
1039*9999SWang.Lin@Sun.COM 	ctry->countryCode = ah->ah_countryCode;
1040*9999SWang.Lin@Sun.COM 	ctry->regDmnEnum = ah->ah_currentRD;
1041*9999SWang.Lin@Sun.COM 	ctry->regDmn5G = ah->ah_currentRD5G;
1042*9999SWang.Lin@Sun.COM 	ctry->regDmn2G = ah->ah_currentRD2G;
1043*9999SWang.Lin@Sun.COM 	ctry->iso[0] = ah->ah_iso[0];
1044*9999SWang.Lin@Sun.COM 	ctry->iso[1] = ah->ah_iso[1];
1045*9999SWang.Lin@Sun.COM 	ctry->iso[2] = ah->ah_iso[2];
1046*9999SWang.Lin@Sun.COM }
1047