xref: /freebsd/sys/dev/ath/ath_hal/ah_regdomain.c (revision 12d532388dd8263abac3b0a6f8b9ecf9708a5fc6)
114779705SSam Leffler /*
259efa8b5SSam Leffler  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
314779705SSam Leffler  * Copyright (c) 2005-2006 Atheros Communications, Inc.
414779705SSam Leffler  * All rights reserved.
514779705SSam Leffler  *
614779705SSam Leffler  * Permission to use, copy, modify, and/or distribute this software for any
714779705SSam Leffler  * purpose with or without fee is hereby granted, provided that the above
814779705SSam Leffler  * copyright notice and this permission notice appear in all copies.
914779705SSam Leffler  *
1014779705SSam Leffler  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1114779705SSam Leffler  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1214779705SSam Leffler  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1314779705SSam Leffler  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1414779705SSam Leffler  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1514779705SSam Leffler  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1614779705SSam Leffler  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1714779705SSam Leffler  *
185e36f058SSam Leffler  * $FreeBSD$
1914779705SSam Leffler  */
2014779705SSam Leffler #include "opt_ah.h"
2114779705SSam Leffler 
2214779705SSam Leffler #include "ah.h"
2359efa8b5SSam Leffler 
2459efa8b5SSam Leffler #include <net80211/_ieee80211.h>
2559efa8b5SSam Leffler #include <net80211/ieee80211_regdomain.h>
2659efa8b5SSam Leffler 
2714779705SSam Leffler #include "ah_internal.h"
2814779705SSam Leffler #include "ah_eeprom.h"
2914779705SSam Leffler #include "ah_devid.h"
3014779705SSam Leffler 
312836e2aeSAdrian Chadd #include "ah_regdomain.h"
322836e2aeSAdrian Chadd 
3314779705SSam Leffler /*
3414779705SSam Leffler  * XXX this code needs a audit+review
3514779705SSam Leffler  */
3614779705SSam Leffler 
3714779705SSam Leffler /* used throughout this file... */
38*12d53238SAndriy Voskoboinyk #define	N(a)		nitems(a)
3914779705SSam Leffler 
4014779705SSam Leffler #define HAL_MODE_11A_TURBO	HAL_MODE_108A
4114779705SSam Leffler #define HAL_MODE_11G_TURBO	HAL_MODE_108G
4214779705SSam Leffler 
4314779705SSam Leffler /*
4414779705SSam Leffler  * Mask to check whether a domain is a multidomain or a single domain
4514779705SSam Leffler  */
4614779705SSam Leffler #define MULTI_DOMAIN_MASK 0xFF00
4714779705SSam Leffler 
4814779705SSam Leffler /*
4914779705SSam Leffler  * Enumerated Regulatory Domain Information 8 bit values indicate that
5014779705SSam Leffler  * the regdomain is really a pair of unitary regdomains.  12 bit values
5114779705SSam Leffler  * are the real unitary regdomains and are the only ones which have the
5214779705SSam Leffler  * frequency bitmasks and flags set.
5314779705SSam Leffler  */
54c48e24c1SAdrian Chadd #include "ah_regdomain/ah_rd_regenum.h"
5514779705SSam Leffler 
5614779705SSam Leffler #define	WORLD_SKU_MASK		0x00F0
5714779705SSam Leffler #define	WORLD_SKU_PREFIX	0x0060
5814779705SSam Leffler 
5914779705SSam Leffler /*
6014779705SSam Leffler  * THE following table is the mapping of regdomain pairs specified by
6114779705SSam Leffler  * an 8 bit regdomain value to the individual unitary reg domains
6214779705SSam Leffler  */
63c48e24c1SAdrian Chadd #include "ah_regdomain/ah_rd_regmap.h"
6414779705SSam Leffler 
6514779705SSam Leffler /*
6614779705SSam Leffler  * The following tables are the master list for all different freqeuncy
6714779705SSam Leffler  * bands with the complete matrix of all possible flags and settings
6814779705SSam Leffler  * for each band if it is used in ANY reg domain.
6914779705SSam Leffler  */
7014779705SSam Leffler 
7114779705SSam Leffler #define	COUNTRY_ERD_FLAG        0x8000
7214779705SSam Leffler #define WORLDWIDE_ROAMING_FLAG  0x4000
7314779705SSam Leffler 
74c48e24c1SAdrian Chadd /*
75c48e24c1SAdrian Chadd  * This table maps country ISO codes from net80211 into regulatory
76c48e24c1SAdrian Chadd  * domains which the ath regulatory domain code understands.
77c48e24c1SAdrian Chadd  */
78c48e24c1SAdrian Chadd #include "ah_regdomain/ah_rd_ctry.h"
7914779705SSam Leffler 
8014779705SSam Leffler /*
81c48e24c1SAdrian Chadd  * The frequency band collections are a set of frequency ranges
82c48e24c1SAdrian Chadd  * with shared properties - max tx power, max antenna gain, channel width,
83c48e24c1SAdrian Chadd  * channel spacing, DFS requirements and passive scanning requirements.
8414779705SSam Leffler  *
85c48e24c1SAdrian Chadd  * These are represented as entries in a frequency band bitmask.
86c48e24c1SAdrian Chadd  * Each regulatory domain entry in ah_regdomain_domains.h uses one
87c48e24c1SAdrian Chadd  * or more frequency band entries for each of the channel modes
88c48e24c1SAdrian Chadd  * supported (11bg, 11a, half, quarter, turbo, etc.)
89c48e24c1SAdrian Chadd  *
9014779705SSam Leffler  */
91c48e24c1SAdrian Chadd #include "ah_regdomain/ah_rd_freqbands.h"
9214779705SSam Leffler 
9314779705SSam Leffler /*
94c48e24c1SAdrian Chadd  * This is the main regulatory database. It defines the supported
95c48e24c1SAdrian Chadd  * set of features and requirements for each of the defined regulatory
96c48e24c1SAdrian Chadd  * zones. It uses combinations of frequency ranges - represented in
97c48e24c1SAdrian Chadd  * a bitmask - to determine the requirements and limitations needed.
9814779705SSam Leffler  */
99c48e24c1SAdrian Chadd #include "ah_regdomain/ah_rd_domains.h"
10014779705SSam Leffler 
10114779705SSam Leffler static const struct cmode modes[] = {
102*12d53238SAndriy Voskoboinyk 	{ HAL_MODE_TURBO,	IEEE80211_CHAN_ST,	&regDmn5GhzTurboFreq[0] },
103*12d53238SAndriy Voskoboinyk 	{ HAL_MODE_11A,		IEEE80211_CHAN_A,	&regDmn5GhzFreq[0] },
104*12d53238SAndriy Voskoboinyk 	{ HAL_MODE_11B,		IEEE80211_CHAN_B,	&regDmn2GhzFreq[0] },
105*12d53238SAndriy Voskoboinyk 	{ HAL_MODE_11G,		IEEE80211_CHAN_G,	&regDmn2Ghz11gFreq[0] },
106*12d53238SAndriy Voskoboinyk 	{ HAL_MODE_11G_TURBO,	IEEE80211_CHAN_108G,	&regDmn2Ghz11gTurboFreq[0] },
107*12d53238SAndriy Voskoboinyk 	{ HAL_MODE_11A_TURBO,	IEEE80211_CHAN_108A,	&regDmn5GhzTurboFreq[0] },
10859efa8b5SSam Leffler 	{ HAL_MODE_11A_QUARTER_RATE,
109*12d53238SAndriy Voskoboinyk 	  IEEE80211_CHAN_A | IEEE80211_CHAN_QUARTER,	&regDmn5GhzFreq[0] },
11059efa8b5SSam Leffler 	{ HAL_MODE_11A_HALF_RATE,
111*12d53238SAndriy Voskoboinyk 	  IEEE80211_CHAN_A | IEEE80211_CHAN_HALF,	&regDmn5GhzFreq[0] },
11259efa8b5SSam Leffler 	{ HAL_MODE_11G_QUARTER_RATE,
113*12d53238SAndriy Voskoboinyk 	  IEEE80211_CHAN_G | IEEE80211_CHAN_QUARTER,	&regDmn2Ghz11gFreq[0] },
11459efa8b5SSam Leffler 	{ HAL_MODE_11G_HALF_RATE,
115*12d53238SAndriy Voskoboinyk 	  IEEE80211_CHAN_G | IEEE80211_CHAN_HALF,	&regDmn2Ghz11gFreq[0] },
116*12d53238SAndriy Voskoboinyk 	{ HAL_MODE_11NG_HT20,
117*12d53238SAndriy Voskoboinyk 	  IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,	&regDmn2Ghz11gFreq[0] },
11859efa8b5SSam Leffler 	{ HAL_MODE_11NG_HT40PLUS,
119*12d53238SAndriy Voskoboinyk 	  IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U,	&regDmn2Ghz11gFreq[0] },
12059efa8b5SSam Leffler 	{ HAL_MODE_11NG_HT40MINUS,
121*12d53238SAndriy Voskoboinyk 	  IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D,	&regDmn2Ghz11gFreq[0] },
122*12d53238SAndriy Voskoboinyk 	{ HAL_MODE_11NA_HT20,
123*12d53238SAndriy Voskoboinyk 	  IEEE80211_CHAN_A | IEEE80211_CHAN_HT20,	&regDmn5GhzFreq[0] },
12459efa8b5SSam Leffler 	{ HAL_MODE_11NA_HT40PLUS,
125*12d53238SAndriy Voskoboinyk 	  IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U,	&regDmn5GhzFreq[0] },
12659efa8b5SSam Leffler 	{ HAL_MODE_11NA_HT40MINUS,
127*12d53238SAndriy Voskoboinyk 	  IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D,	&regDmn5GhzFreq[0] },
12814779705SSam Leffler };
12914779705SSam Leffler 
1307dd4de13SAdrian Chadd static void ath_hal_update_dfsdomain(struct ath_hal *ah);
1317dd4de13SAdrian Chadd 
13259efa8b5SSam Leffler static OS_INLINE uint16_t
13314779705SSam Leffler getEepromRD(struct ath_hal *ah)
13414779705SSam Leffler {
13514779705SSam Leffler 	return AH_PRIVATE(ah)->ah_currentRD &~ WORLDWIDE_ROAMING_FLAG;
13614779705SSam Leffler }
13714779705SSam Leffler 
13814779705SSam Leffler /*
13914779705SSam Leffler  * Test to see if the bitmask array is all zeros
14014779705SSam Leffler  */
14114779705SSam Leffler static HAL_BOOL
14214779705SSam Leffler isChanBitMaskZero(const uint64_t *bitmask)
14314779705SSam Leffler {
14414779705SSam Leffler #if BMLEN > 2
14514779705SSam Leffler #error	"add more cases"
14614779705SSam Leffler #endif
14714779705SSam Leffler #if BMLEN > 1
14814779705SSam Leffler 	if (bitmask[1] != 0)
14914779705SSam Leffler 		return AH_FALSE;
15014779705SSam Leffler #endif
15114779705SSam Leffler 	return (bitmask[0] == 0);
15214779705SSam Leffler }
15314779705SSam Leffler 
15414779705SSam Leffler /*
15514779705SSam Leffler  * Return whether or not the regulatory domain/country in EEPROM
15614779705SSam Leffler  * is acceptable.
15714779705SSam Leffler  */
15814779705SSam Leffler static HAL_BOOL
15914779705SSam Leffler isEepromValid(struct ath_hal *ah)
16014779705SSam Leffler {
16114779705SSam Leffler 	uint16_t rd = getEepromRD(ah);
16214779705SSam Leffler 	int i;
16314779705SSam Leffler 
16414779705SSam Leffler 	if (rd & COUNTRY_ERD_FLAG) {
16514779705SSam Leffler 		uint16_t cc = rd &~ COUNTRY_ERD_FLAG;
16614779705SSam Leffler 		for (i = 0; i < N(allCountries); i++)
16714779705SSam Leffler 			if (allCountries[i].countryCode == cc)
16814779705SSam Leffler 				return AH_TRUE;
16914779705SSam Leffler 	} else {
17014779705SSam Leffler 		for (i = 0; i < N(regDomainPairs); i++)
17114779705SSam Leffler 			if (regDomainPairs[i].regDmnEnum == rd)
17214779705SSam Leffler 				return AH_TRUE;
17314779705SSam Leffler 	}
17492389b27SHiren Panchasara 
17592389b27SHiren Panchasara 	if (rd == FCC_UBNT) {
17692389b27SHiren Panchasara 		return AH_TRUE;
17792389b27SHiren Panchasara 	}
17892389b27SHiren Panchasara 
1790e56140aSAdrian Chadd 	HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
18014779705SSam Leffler 	    "%s: invalid regulatory domain/country code 0x%x\n", __func__, rd);
18114779705SSam Leffler 	return AH_FALSE;
18214779705SSam Leffler }
18314779705SSam Leffler 
18414779705SSam Leffler /*
18514779705SSam Leffler  * Find the pointer to the country element in the country table
18614779705SSam Leffler  * corresponding to the country code
18714779705SSam Leffler  */
18814779705SSam Leffler static COUNTRY_CODE_TO_ENUM_RD*
18914779705SSam Leffler findCountry(HAL_CTRY_CODE countryCode)
19014779705SSam Leffler {
19114779705SSam Leffler 	int i;
19214779705SSam Leffler 
19314779705SSam Leffler 	for (i = 0; i < N(allCountries); i++) {
19414779705SSam Leffler 		if (allCountries[i].countryCode == countryCode)
19514779705SSam Leffler 			return &allCountries[i];
19614779705SSam Leffler 	}
19759efa8b5SSam Leffler 	return AH_NULL;
19859efa8b5SSam Leffler }
19959efa8b5SSam Leffler 
20059efa8b5SSam Leffler static REG_DOMAIN *
20159efa8b5SSam Leffler findRegDmn(int regDmn)
20259efa8b5SSam Leffler {
20359efa8b5SSam Leffler 	int i;
20459efa8b5SSam Leffler 
20559efa8b5SSam Leffler 	for (i = 0; i < N(regDomains); i++) {
20659efa8b5SSam Leffler 		if (regDomains[i].regDmnEnum == regDmn)
20759efa8b5SSam Leffler 			return &regDomains[i];
20859efa8b5SSam Leffler 	}
20959efa8b5SSam Leffler 	return AH_NULL;
21059efa8b5SSam Leffler }
21159efa8b5SSam Leffler 
21259efa8b5SSam Leffler static REG_DMN_PAIR_MAPPING *
21359efa8b5SSam Leffler findRegDmnPair(int regDmnPair)
21459efa8b5SSam Leffler {
21559efa8b5SSam Leffler 	int i;
21659efa8b5SSam Leffler 
21759efa8b5SSam Leffler 	if (regDmnPair != NO_ENUMRD) {
21859efa8b5SSam Leffler 		for (i = 0; i < N(regDomainPairs); i++) {
21959efa8b5SSam Leffler 			if (regDomainPairs[i].regDmnEnum == regDmnPair)
22059efa8b5SSam Leffler 				return &regDomainPairs[i];
22159efa8b5SSam Leffler 		}
22259efa8b5SSam Leffler 	}
22359efa8b5SSam Leffler 	return AH_NULL;
22414779705SSam Leffler }
22514779705SSam Leffler 
22614779705SSam Leffler /*
22714779705SSam Leffler  * Calculate a default country based on the EEPROM setting.
22814779705SSam Leffler  */
22914779705SSam Leffler static HAL_CTRY_CODE
23014779705SSam Leffler getDefaultCountry(struct ath_hal *ah)
23114779705SSam Leffler {
23259efa8b5SSam Leffler 	REG_DMN_PAIR_MAPPING *regpair;
23314779705SSam Leffler 	uint16_t rd;
23414779705SSam Leffler 
23514779705SSam Leffler 	rd = getEepromRD(ah);
23614779705SSam Leffler 	if (rd & COUNTRY_ERD_FLAG) {
23759efa8b5SSam Leffler 		COUNTRY_CODE_TO_ENUM_RD *country;
23814779705SSam Leffler 		uint16_t cc = rd & ~COUNTRY_ERD_FLAG;
23914779705SSam Leffler 		country = findCountry(cc);
24014779705SSam Leffler 		if (country != AH_NULL)
24114779705SSam Leffler 			return cc;
24214779705SSam Leffler 	}
24314779705SSam Leffler 	/*
24414779705SSam Leffler 	 * Check reg domains that have only one country
24514779705SSam Leffler 	 */
24659efa8b5SSam Leffler 	regpair = findRegDmnPair(rd);
24759efa8b5SSam Leffler 	return (regpair != AH_NULL) ? regpair->singleCC : CTRY_DEFAULT;
24814779705SSam Leffler }
24914779705SSam Leffler 
25014779705SSam Leffler static HAL_BOOL
25114779705SSam Leffler IS_BIT_SET(int bit, const uint64_t bitmask[])
25214779705SSam Leffler {
25314779705SSam Leffler 	int byteOffset, bitnum;
25414779705SSam Leffler 	uint64_t val;
25514779705SSam Leffler 
25614779705SSam Leffler 	byteOffset = bit/64;
25714779705SSam Leffler 	bitnum = bit - byteOffset*64;
25814779705SSam Leffler 	val = ((uint64_t) 1) << bitnum;
25914779705SSam Leffler 	return (bitmask[byteOffset] & val) != 0;
26014779705SSam Leffler }
26114779705SSam Leffler 
26259efa8b5SSam Leffler static HAL_STATUS
26359efa8b5SSam Leffler getregstate(struct ath_hal *ah, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
26459efa8b5SSam Leffler     COUNTRY_CODE_TO_ENUM_RD **pcountry,
26559efa8b5SSam Leffler     REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz)
26614779705SSam Leffler {
26759efa8b5SSam Leffler 	COUNTRY_CODE_TO_ENUM_RD *country;
26859efa8b5SSam Leffler 	REG_DOMAIN *rd5GHz, *rd2GHz;
26914779705SSam Leffler 
27059efa8b5SSam Leffler 	if (cc == CTRY_DEFAULT && regDmn == SKU_NONE) {
27114779705SSam Leffler 		/*
27214779705SSam Leffler 		 * Validate the EEPROM setting and setup defaults
27314779705SSam Leffler 		 */
27414779705SSam Leffler 		if (!isEepromValid(ah)) {
27514779705SSam Leffler 			/*
27614779705SSam Leffler 			 * Don't return any channels if the EEPROM has an
27714779705SSam Leffler 			 * invalid regulatory domain/country code setting.
27814779705SSam Leffler 			 */
27914779705SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
28014779705SSam Leffler 			    "%s: invalid EEPROM contents\n",__func__);
28159efa8b5SSam Leffler 			return HAL_EEBADREG;
28214779705SSam Leffler 		}
28314779705SSam Leffler 
28459efa8b5SSam Leffler 		cc = getDefaultCountry(ah);
28559efa8b5SSam Leffler 		country = findCountry(cc);
28614779705SSam Leffler 		if (country == AH_NULL) {
28714779705SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
28859efa8b5SSam Leffler 			    "NULL Country!, cc %d\n", cc);
28959efa8b5SSam Leffler 			return HAL_EEBADCC;
29014779705SSam Leffler 		}
29159efa8b5SSam Leffler 		regDmn = country->regDmnEnum;
29259efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: EEPROM cc %u rd 0x%x\n",
29359efa8b5SSam Leffler 		    __func__, cc, regDmn);
29459efa8b5SSam Leffler 
29559efa8b5SSam Leffler 		if (country->countryCode == CTRY_DEFAULT) {
29659efa8b5SSam Leffler 			/*
29759efa8b5SSam Leffler 			 * Check EEPROM; SKU may be for a country, single
29859efa8b5SSam Leffler 			 * domain, or multiple domains (WWR).
29959efa8b5SSam Leffler 			 */
30059efa8b5SSam Leffler 			uint16_t rdnum = getEepromRD(ah);
30159efa8b5SSam Leffler 			if ((rdnum & COUNTRY_ERD_FLAG) == 0 &&
30259efa8b5SSam Leffler 			    (findRegDmn(rdnum) != AH_NULL ||
30359efa8b5SSam Leffler 			     findRegDmnPair(rdnum) != AH_NULL)) {
30459efa8b5SSam Leffler 				regDmn = rdnum;
30514779705SSam Leffler 				HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
30659efa8b5SSam Leffler 				    "%s: EEPROM rd 0x%x\n", __func__, rdnum);
30759efa8b5SSam Leffler 			}
30859efa8b5SSam Leffler 		}
30959efa8b5SSam Leffler 	} else {
31059efa8b5SSam Leffler 		country = findCountry(cc);
31159efa8b5SSam Leffler 		if (country == AH_NULL) {
31259efa8b5SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
31359efa8b5SSam Leffler 			    "unknown country, cc %d\n", cc);
31459efa8b5SSam Leffler 			return HAL_EINVAL;
31559efa8b5SSam Leffler 		}
31659efa8b5SSam Leffler 		if (regDmn == SKU_NONE)
31759efa8b5SSam Leffler 			regDmn = country->regDmnEnum;
31859efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u rd 0x%x\n",
31959efa8b5SSam Leffler 		    __func__, cc, regDmn);
32014779705SSam Leffler 	}
32114779705SSam Leffler 
32259efa8b5SSam Leffler 	/*
32359efa8b5SSam Leffler 	 * Setup per-band state.
32459efa8b5SSam Leffler 	 */
32559efa8b5SSam Leffler 	if ((regDmn & MULTI_DOMAIN_MASK) == 0) {
32659efa8b5SSam Leffler 		REG_DMN_PAIR_MAPPING *regpair = findRegDmnPair(regDmn);
32759efa8b5SSam Leffler 		if (regpair == AH_NULL) {
32859efa8b5SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
32959efa8b5SSam Leffler 			    "%s: no reg domain pair %u for country %u\n",
33059efa8b5SSam Leffler 			    __func__, regDmn, country->countryCode);
33159efa8b5SSam Leffler 			return HAL_EINVAL;
33259efa8b5SSam Leffler 		}
33359efa8b5SSam Leffler 		rd5GHz = findRegDmn(regpair->regDmn5GHz);
33459efa8b5SSam Leffler 		if (rd5GHz == AH_NULL) {
33559efa8b5SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
33659efa8b5SSam Leffler 			    "%s: no 5GHz reg domain %u for country %u\n",
33759efa8b5SSam Leffler 			    __func__, regpair->regDmn5GHz, country->countryCode);
33859efa8b5SSam Leffler 			return HAL_EINVAL;
33959efa8b5SSam Leffler 		}
34059efa8b5SSam Leffler 		rd2GHz = findRegDmn(regpair->regDmn2GHz);
34159efa8b5SSam Leffler 		if (rd2GHz == AH_NULL) {
34259efa8b5SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
34359efa8b5SSam Leffler 			    "%s: no 2GHz reg domain %u for country %u\n",
34459efa8b5SSam Leffler 			    __func__, regpair->regDmn2GHz, country->countryCode);
34559efa8b5SSam Leffler 			return HAL_EINVAL;
34659efa8b5SSam Leffler 		}
34759efa8b5SSam Leffler 	} else {
34859efa8b5SSam Leffler 		rd5GHz = rd2GHz = findRegDmn(regDmn);
34959efa8b5SSam Leffler 		if (rd2GHz == AH_NULL) {
35059efa8b5SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
35159efa8b5SSam Leffler 			    "%s: no unitary reg domain %u for country %u\n",
35259efa8b5SSam Leffler 			    __func__, regDmn, country->countryCode);
35359efa8b5SSam Leffler 			return HAL_EINVAL;
35459efa8b5SSam Leffler 		}
35559efa8b5SSam Leffler 	}
35659efa8b5SSam Leffler 	if (pcountry != AH_NULL)
35759efa8b5SSam Leffler 		*pcountry = country;
35859efa8b5SSam Leffler 	*prd2GHz = rd2GHz;
35959efa8b5SSam Leffler 	*prd5GHz = rd5GHz;
36059efa8b5SSam Leffler 	return HAL_OK;
36159efa8b5SSam Leffler }
36214779705SSam Leffler 
363*12d53238SAndriy Voskoboinyk static uint64_t *
364*12d53238SAndriy Voskoboinyk getchannelBM(u_int mode, REG_DOMAIN *rd)
365*12d53238SAndriy Voskoboinyk {
366*12d53238SAndriy Voskoboinyk 	switch (mode) {
367*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11B:
368*12d53238SAndriy Voskoboinyk 		return (rd->chan11b);
369*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11G_QUARTER_RATE:
370*12d53238SAndriy Voskoboinyk 		return (rd->chan11g_quarter);
371*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11G_HALF_RATE:
372*12d53238SAndriy Voskoboinyk 		return (rd->chan11g_half);
373*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11G:
374*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11NG_HT20:
375*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11NG_HT40PLUS:
376*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11NG_HT40MINUS:
377*12d53238SAndriy Voskoboinyk 		return (rd->chan11g);
378*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11G_TURBO:
379*12d53238SAndriy Voskoboinyk 		return (rd->chan11g_turbo);
380*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11A_QUARTER_RATE:
381*12d53238SAndriy Voskoboinyk 		return (rd->chan11a_quarter);
382*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11A_HALF_RATE:
383*12d53238SAndriy Voskoboinyk 		return (rd->chan11a_half);
384*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11A:
385*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11NA_HT20:
386*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11NA_HT40PLUS:
387*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11NA_HT40MINUS:
388*12d53238SAndriy Voskoboinyk 		return (rd->chan11a);
389*12d53238SAndriy Voskoboinyk 	case HAL_MODE_TURBO:
390*12d53238SAndriy Voskoboinyk 		return (rd->chan11a_turbo);
391*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11A_TURBO:
392*12d53238SAndriy Voskoboinyk 		return (rd->chan11a_dyn_turbo);
393*12d53238SAndriy Voskoboinyk 	default:
394*12d53238SAndriy Voskoboinyk 		return (AH_NULL);
395*12d53238SAndriy Voskoboinyk 	}
396*12d53238SAndriy Voskoboinyk }
397*12d53238SAndriy Voskoboinyk 
398*12d53238SAndriy Voskoboinyk static void
399*12d53238SAndriy Voskoboinyk setchannelflags(struct ieee80211_channel *c, REG_DMN_FREQ_BAND *fband,
400*12d53238SAndriy Voskoboinyk     REG_DOMAIN *rd)
401*12d53238SAndriy Voskoboinyk {
402*12d53238SAndriy Voskoboinyk 	if (fband->usePassScan & rd->pscan)
403*12d53238SAndriy Voskoboinyk 		c->ic_flags |= IEEE80211_CHAN_PASSIVE;
404*12d53238SAndriy Voskoboinyk 	if (fband->useDfs & rd->dfsMask)
405*12d53238SAndriy Voskoboinyk 		c->ic_flags |= IEEE80211_CHAN_DFS;
406*12d53238SAndriy Voskoboinyk 	if (IEEE80211_IS_CHAN_5GHZ(c) && (rd->flags & DISALLOW_ADHOC_11A))
407*12d53238SAndriy Voskoboinyk 		c->ic_flags |= IEEE80211_CHAN_NOADHOC;
408*12d53238SAndriy Voskoboinyk 	if (IEEE80211_IS_CHAN_TURBO(c) &&
409*12d53238SAndriy Voskoboinyk 	    (rd->flags & DISALLOW_ADHOC_11A_TURB))
410*12d53238SAndriy Voskoboinyk 		c->ic_flags |= IEEE80211_CHAN_NOADHOC;
411*12d53238SAndriy Voskoboinyk 	if (rd->flags & NO_HOSTAP)
412*12d53238SAndriy Voskoboinyk 		c->ic_flags |= IEEE80211_CHAN_NOHOSTAP;
413*12d53238SAndriy Voskoboinyk 	if (rd->flags & LIMIT_FRAME_4MS)
414*12d53238SAndriy Voskoboinyk 		c->ic_flags |= IEEE80211_CHAN_4MSXMIT;
415*12d53238SAndriy Voskoboinyk 	if (rd->flags & NEED_NFC)
416*12d53238SAndriy Voskoboinyk 		c->ic_flags |= CHANNEL_NFCREQUIRED;
417*12d53238SAndriy Voskoboinyk }
418*12d53238SAndriy Voskoboinyk 
419*12d53238SAndriy Voskoboinyk static int
420*12d53238SAndriy Voskoboinyk addchan(struct ath_hal *ah, struct ieee80211_channel chans[],
421*12d53238SAndriy Voskoboinyk     u_int maxchans, int *nchans, uint16_t freq, uint32_t flags,
422*12d53238SAndriy Voskoboinyk     REG_DMN_FREQ_BAND *fband, REG_DOMAIN *rd)
423*12d53238SAndriy Voskoboinyk {
424*12d53238SAndriy Voskoboinyk 	struct ieee80211_channel *c;
425*12d53238SAndriy Voskoboinyk 
426*12d53238SAndriy Voskoboinyk 	if (*nchans >= maxchans)
427*12d53238SAndriy Voskoboinyk 		return (ENOBUFS);
428*12d53238SAndriy Voskoboinyk 
429*12d53238SAndriy Voskoboinyk 	c = &chans[(*nchans)++];
430*12d53238SAndriy Voskoboinyk 	c->ic_freq = freq;
431*12d53238SAndriy Voskoboinyk 	c->ic_flags = flags;
432*12d53238SAndriy Voskoboinyk 	setchannelflags(c, fband, rd);
433*12d53238SAndriy Voskoboinyk 	c->ic_maxregpower = fband->powerDfs;
434*12d53238SAndriy Voskoboinyk 	ath_hal_getpowerlimits(ah, c);
435*12d53238SAndriy Voskoboinyk 	c->ic_maxantgain = fband->antennaMax;
436*12d53238SAndriy Voskoboinyk 
437*12d53238SAndriy Voskoboinyk 	return (0);
438*12d53238SAndriy Voskoboinyk }
439*12d53238SAndriy Voskoboinyk 
440*12d53238SAndriy Voskoboinyk static int
441*12d53238SAndriy Voskoboinyk copychan_prev(struct ath_hal *ah, struct ieee80211_channel chans[],
442*12d53238SAndriy Voskoboinyk     u_int maxchans, int *nchans, uint16_t freq)
443*12d53238SAndriy Voskoboinyk {
444*12d53238SAndriy Voskoboinyk 	struct ieee80211_channel *c;
445*12d53238SAndriy Voskoboinyk 
446*12d53238SAndriy Voskoboinyk 	KASSERT(*nchans > 0, ("channel list is empty\n"));
447*12d53238SAndriy Voskoboinyk 
448*12d53238SAndriy Voskoboinyk 	if (*nchans >= maxchans)
449*12d53238SAndriy Voskoboinyk 		return (ENOBUFS);
450*12d53238SAndriy Voskoboinyk 
451*12d53238SAndriy Voskoboinyk 	c = &chans[(*nchans)++];
452*12d53238SAndriy Voskoboinyk 	c[0] = c[-1];
453*12d53238SAndriy Voskoboinyk 	c->ic_freq = freq;
454*12d53238SAndriy Voskoboinyk 	/* XXX is it needed here? */
455*12d53238SAndriy Voskoboinyk 	ath_hal_getpowerlimits(ah, c);
456*12d53238SAndriy Voskoboinyk 
457*12d53238SAndriy Voskoboinyk 	return (0);
458*12d53238SAndriy Voskoboinyk }
459*12d53238SAndriy Voskoboinyk 
460*12d53238SAndriy Voskoboinyk static int
461*12d53238SAndriy Voskoboinyk add_chanlist_band(struct ath_hal *ah, struct ieee80211_channel chans[],
462*12d53238SAndriy Voskoboinyk     int maxchans, int *nchans, uint16_t freq_lo, uint16_t freq_hi, int step,
463*12d53238SAndriy Voskoboinyk     uint32_t flags, REG_DMN_FREQ_BAND *fband, REG_DOMAIN *rd)
464*12d53238SAndriy Voskoboinyk {
465*12d53238SAndriy Voskoboinyk 	uint16_t freq = freq_lo;
466*12d53238SAndriy Voskoboinyk 	int error;
467*12d53238SAndriy Voskoboinyk 
468*12d53238SAndriy Voskoboinyk 	if (freq_hi < freq_lo)
469*12d53238SAndriy Voskoboinyk 		return (0);
470*12d53238SAndriy Voskoboinyk 
471*12d53238SAndriy Voskoboinyk 	error = addchan(ah, chans, maxchans, nchans, freq, flags, fband, rd);
472*12d53238SAndriy Voskoboinyk 	for (freq += step; freq <= freq_hi && error == 0; freq += step)
473*12d53238SAndriy Voskoboinyk 		error = copychan_prev(ah, chans, maxchans, nchans, freq);
474*12d53238SAndriy Voskoboinyk 
475*12d53238SAndriy Voskoboinyk 	return (error);
476*12d53238SAndriy Voskoboinyk }
477*12d53238SAndriy Voskoboinyk 
478*12d53238SAndriy Voskoboinyk static void
479*12d53238SAndriy Voskoboinyk adj_freq_ht40(u_int mode, int *low_adj, int *hi_adj, int *channelSep)
480*12d53238SAndriy Voskoboinyk {
481*12d53238SAndriy Voskoboinyk 
482*12d53238SAndriy Voskoboinyk 	*low_adj = *hi_adj = *channelSep = 0;
483*12d53238SAndriy Voskoboinyk 	switch (mode) {
484*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11NA_HT40PLUS:
485*12d53238SAndriy Voskoboinyk 		*channelSep = 40;
486*12d53238SAndriy Voskoboinyk 		/* FALLTHROUGH */
487*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11NG_HT40PLUS:
488*12d53238SAndriy Voskoboinyk 		*hi_adj = -20;
489*12d53238SAndriy Voskoboinyk 		break;
490*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11NA_HT40MINUS:
491*12d53238SAndriy Voskoboinyk 		*channelSep = 40;
492*12d53238SAndriy Voskoboinyk 		/* FALLTHROUGH */
493*12d53238SAndriy Voskoboinyk 	case HAL_MODE_11NG_HT40MINUS:
494*12d53238SAndriy Voskoboinyk 		*low_adj = 20;
495*12d53238SAndriy Voskoboinyk 		break;
496*12d53238SAndriy Voskoboinyk 	}
497*12d53238SAndriy Voskoboinyk }
498*12d53238SAndriy Voskoboinyk 
499*12d53238SAndriy Voskoboinyk static void
500*12d53238SAndriy Voskoboinyk add_chanlist_mode(struct ath_hal *ah, struct ieee80211_channel chans[],
501*12d53238SAndriy Voskoboinyk     u_int maxchans, int *nchans, const struct cmode *cm, REG_DOMAIN *rd,
502*12d53238SAndriy Voskoboinyk     HAL_BOOL enableExtendedChannels)
503*12d53238SAndriy Voskoboinyk {
504*12d53238SAndriy Voskoboinyk 	uint64_t *channelBM;
505*12d53238SAndriy Voskoboinyk 	uint16_t freq_lo, freq_hi;
506*12d53238SAndriy Voskoboinyk 	int b, error, low_adj, hi_adj, channelSep;
507*12d53238SAndriy Voskoboinyk 
508*12d53238SAndriy Voskoboinyk 	if (!ath_hal_getChannelEdges(ah, cm->flags, &freq_lo, &freq_hi)) {
509*12d53238SAndriy Voskoboinyk 		/* channel not supported by hardware, skip it */
510*12d53238SAndriy Voskoboinyk 		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
511*12d53238SAndriy Voskoboinyk 		    "%s: channels 0x%x not supported by hardware\n",
512*12d53238SAndriy Voskoboinyk 		    __func__, cm->flags);
513*12d53238SAndriy Voskoboinyk 		return;
514*12d53238SAndriy Voskoboinyk 	}
515*12d53238SAndriy Voskoboinyk 
516*12d53238SAndriy Voskoboinyk 	channelBM = getchannelBM(cm->mode, rd);
517*12d53238SAndriy Voskoboinyk 	if (isChanBitMaskZero(channelBM))
518*12d53238SAndriy Voskoboinyk 		return;
519*12d53238SAndriy Voskoboinyk 
520*12d53238SAndriy Voskoboinyk 	/*
521*12d53238SAndriy Voskoboinyk 	 * Setup special handling for HT40 channels; e.g.
522*12d53238SAndriy Voskoboinyk 	 * 5G HT40 channels require 40Mhz channel separation.
523*12d53238SAndriy Voskoboinyk 	 */
524*12d53238SAndriy Voskoboinyk 	adj_freq_ht40(cm->mode, &low_adj, &hi_adj, &channelSep);
525*12d53238SAndriy Voskoboinyk 
526*12d53238SAndriy Voskoboinyk 	for (b = 0; b < 64*BMLEN; b++) {
527*12d53238SAndriy Voskoboinyk 		REG_DMN_FREQ_BAND *fband;
528*12d53238SAndriy Voskoboinyk 		uint16_t bfreq_lo, bfreq_hi;
529*12d53238SAndriy Voskoboinyk 		int step;
530*12d53238SAndriy Voskoboinyk 
531*12d53238SAndriy Voskoboinyk 		if (!IS_BIT_SET(b, channelBM))
532*12d53238SAndriy Voskoboinyk 			continue;
533*12d53238SAndriy Voskoboinyk 		fband = &cm->freqs[b];
534*12d53238SAndriy Voskoboinyk 
535*12d53238SAndriy Voskoboinyk 		if ((fband->usePassScan & IS_ECM_CHAN) &&
536*12d53238SAndriy Voskoboinyk 		    !enableExtendedChannels) {
537*12d53238SAndriy Voskoboinyk 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
538*12d53238SAndriy Voskoboinyk 			    "skip ecm channels\n");
539*12d53238SAndriy Voskoboinyk 			continue;
540*12d53238SAndriy Voskoboinyk 		}
541*12d53238SAndriy Voskoboinyk #if 0
542*12d53238SAndriy Voskoboinyk 		if ((fband->useDfs & rd->dfsMask) &&
543*12d53238SAndriy Voskoboinyk 		    (cm->flags & IEEE80211_CHAN_HT40)) {
544*12d53238SAndriy Voskoboinyk 			/* NB: DFS and HT40 don't mix */
545*12d53238SAndriy Voskoboinyk 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
546*12d53238SAndriy Voskoboinyk 			    "skip HT40 chan, DFS required\n");
547*12d53238SAndriy Voskoboinyk 			continue;
548*12d53238SAndriy Voskoboinyk 		}
549*12d53238SAndriy Voskoboinyk #endif
550*12d53238SAndriy Voskoboinyk 		bfreq_lo = MAX(fband->lowChannel + low_adj, freq_lo);
551*12d53238SAndriy Voskoboinyk 		bfreq_hi = MIN(fband->highChannel + hi_adj, freq_hi);
552*12d53238SAndriy Voskoboinyk 		if (fband->channelSep >= channelSep)
553*12d53238SAndriy Voskoboinyk 			step = fband->channelSep;
554*12d53238SAndriy Voskoboinyk 		else
555*12d53238SAndriy Voskoboinyk 			step = roundup(channelSep, fband->channelSep);
556*12d53238SAndriy Voskoboinyk 
557*12d53238SAndriy Voskoboinyk 		error = add_chanlist_band(ah, chans, maxchans, nchans,
558*12d53238SAndriy Voskoboinyk 		    bfreq_lo, bfreq_hi, step, cm->flags, fband, rd);
559*12d53238SAndriy Voskoboinyk 		if (error != 0)	{
560*12d53238SAndriy Voskoboinyk 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
561*12d53238SAndriy Voskoboinyk 			    "%s: too many channels for channel table\n",
562*12d53238SAndriy Voskoboinyk 			    __func__);
563*12d53238SAndriy Voskoboinyk 			return;
564*12d53238SAndriy Voskoboinyk 		}
565*12d53238SAndriy Voskoboinyk 	}
566*12d53238SAndriy Voskoboinyk }
567*12d53238SAndriy Voskoboinyk 
568*12d53238SAndriy Voskoboinyk static u_int
569*12d53238SAndriy Voskoboinyk getmodesmask(struct ath_hal *ah, REG_DOMAIN *rd5GHz, u_int modeSelect)
570*12d53238SAndriy Voskoboinyk {
571*12d53238SAndriy Voskoboinyk #define	HAL_MODE_11A_ALL \
572*12d53238SAndriy Voskoboinyk 	(HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \
573*12d53238SAndriy Voskoboinyk 	 HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE)
574*12d53238SAndriy Voskoboinyk 	u_int modesMask;
575*12d53238SAndriy Voskoboinyk 
576*12d53238SAndriy Voskoboinyk 	/* get modes that HW is capable of */
577*12d53238SAndriy Voskoboinyk 	modesMask = ath_hal_getWirelessModes(ah);
578*12d53238SAndriy Voskoboinyk 	modesMask &= modeSelect;
579*12d53238SAndriy Voskoboinyk 	/* optimize work below if no 11a channels */
580*12d53238SAndriy Voskoboinyk 	if (isChanBitMaskZero(rd5GHz->chan11a) &&
581*12d53238SAndriy Voskoboinyk 	    (modesMask & HAL_MODE_11A_ALL)) {
582*12d53238SAndriy Voskoboinyk 		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
583*12d53238SAndriy Voskoboinyk 		    "%s: disallow all 11a\n", __func__);
584*12d53238SAndriy Voskoboinyk 		modesMask &= ~HAL_MODE_11A_ALL;
585*12d53238SAndriy Voskoboinyk 	}
586*12d53238SAndriy Voskoboinyk 
587*12d53238SAndriy Voskoboinyk 	return (modesMask);
588*12d53238SAndriy Voskoboinyk #undef HAL_MODE_11A_ALL
589*12d53238SAndriy Voskoboinyk }
590*12d53238SAndriy Voskoboinyk 
59159efa8b5SSam Leffler /*
59259efa8b5SSam Leffler  * Construct the channel list for the specified regulatory config.
59359efa8b5SSam Leffler  */
59459efa8b5SSam Leffler static HAL_STATUS
59559efa8b5SSam Leffler getchannels(struct ath_hal *ah,
59659efa8b5SSam Leffler     struct ieee80211_channel chans[], u_int maxchans, int *nchans,
59759efa8b5SSam Leffler     u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
59859efa8b5SSam Leffler     HAL_BOOL enableExtendedChannels,
59959efa8b5SSam Leffler     COUNTRY_CODE_TO_ENUM_RD **pcountry,
60059efa8b5SSam Leffler     REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz)
60159efa8b5SSam Leffler {
60259efa8b5SSam Leffler 	REG_DOMAIN *rd5GHz, *rd2GHz;
603*12d53238SAndriy Voskoboinyk 	u_int modesMask;
60459efa8b5SSam Leffler 	const struct cmode *cm;
60559efa8b5SSam Leffler 	HAL_STATUS status;
60659efa8b5SSam Leffler 
60759efa8b5SSam Leffler 	HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u regDmn 0x%x mode 0x%x%s\n",
60859efa8b5SSam Leffler 	    __func__, cc, regDmn, modeSelect,
60959efa8b5SSam Leffler 	    enableExtendedChannels ? " ecm" : "");
61059efa8b5SSam Leffler 
61159efa8b5SSam Leffler 	status = getregstate(ah, cc, regDmn, pcountry, &rd2GHz, &rd5GHz);
61259efa8b5SSam Leffler 	if (status != HAL_OK)
61359efa8b5SSam Leffler 		return status;
61459efa8b5SSam Leffler 
615*12d53238SAndriy Voskoboinyk 	modesMask = getmodesmask(ah, rd5GHz, modeSelect);
616*12d53238SAndriy Voskoboinyk 	/* XXX error? */
617*12d53238SAndriy Voskoboinyk 	if (modesMask == 0)
618*12d53238SAndriy Voskoboinyk 		goto done;
61959efa8b5SSam Leffler 
62014779705SSam Leffler 	for (cm = modes; cm < &modes[N(modes)]; cm++) {
621*12d53238SAndriy Voskoboinyk 		REG_DOMAIN *rd;
62214779705SSam Leffler 
623*12d53238SAndriy Voskoboinyk 		if ((cm->mode & modesMask) == 0) {
62414779705SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
62514779705SSam Leffler 			    "%s: skip mode 0x%x flags 0x%x\n",
62614779705SSam Leffler 			    __func__, cm->mode, cm->flags);
62714779705SSam Leffler 			continue;
62814779705SSam Leffler 		}
62914779705SSam Leffler 
630*12d53238SAndriy Voskoboinyk 		if (cm->flags & IEEE80211_CHAN_5GHZ)
631*12d53238SAndriy Voskoboinyk 			rd = rd5GHz;
632*12d53238SAndriy Voskoboinyk 		else if (cm->flags & IEEE80211_CHAN_2GHZ)
633*12d53238SAndriy Voskoboinyk 			rd = rd2GHz;
634*12d53238SAndriy Voskoboinyk 		else {
635*12d53238SAndriy Voskoboinyk 			KASSERT(0, ("%s: Unkonwn HAL flags 0x%x\n",
636*12d53238SAndriy Voskoboinyk 			    __func__, cm->flags));
637*12d53238SAndriy Voskoboinyk 			return HAL_EINVAL;
63814779705SSam Leffler 		}
639*12d53238SAndriy Voskoboinyk 
640*12d53238SAndriy Voskoboinyk 		add_chanlist_mode(ah, chans, maxchans, nchans, cm,
641*12d53238SAndriy Voskoboinyk 		    rd, enableExtendedChannels);
642*12d53238SAndriy Voskoboinyk 		if (*nchans >= maxchans)
64314779705SSam Leffler 			goto done;
64414779705SSam Leffler 	}
64514779705SSam Leffler done:
64659efa8b5SSam Leffler 	/* NB: pcountry set above by getregstate */
64759efa8b5SSam Leffler 	if (prd2GHz != AH_NULL)
64859efa8b5SSam Leffler 		*prd2GHz = rd2GHz;
64959efa8b5SSam Leffler 	if (prd5GHz != AH_NULL)
65059efa8b5SSam Leffler 		*prd5GHz = rd5GHz;
65159efa8b5SSam Leffler 	return HAL_OK;
65214779705SSam Leffler }
65314779705SSam Leffler 
65414779705SSam Leffler /*
65559efa8b5SSam Leffler  * Retrieve a channel list without affecting runtime state.
65614779705SSam Leffler  */
65759efa8b5SSam Leffler HAL_STATUS
65859efa8b5SSam Leffler ath_hal_getchannels(struct ath_hal *ah,
65959efa8b5SSam Leffler     struct ieee80211_channel chans[], u_int maxchans, int *nchans,
66059efa8b5SSam Leffler     u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
66159efa8b5SSam Leffler     HAL_BOOL enableExtendedChannels)
66214779705SSam Leffler {
66359efa8b5SSam Leffler 	return getchannels(ah, chans, maxchans, nchans, modeSelect,
66459efa8b5SSam Leffler 	    cc, regDmn, enableExtendedChannels, AH_NULL, AH_NULL, AH_NULL);
66559efa8b5SSam Leffler }
66614779705SSam Leffler 
66714779705SSam Leffler /*
66859efa8b5SSam Leffler  * Handle frequency mapping from 900Mhz range to 2.4GHz range
66959efa8b5SSam Leffler  * for GSM radios.  This is done when we need the h/w frequency
67059efa8b5SSam Leffler  * and the channel is marked IEEE80211_CHAN_GSM.
67114779705SSam Leffler  */
67259efa8b5SSam Leffler static int
67359efa8b5SSam Leffler ath_hal_mapgsm(int sku, int freq)
67459efa8b5SSam Leffler {
67559efa8b5SSam Leffler 	if (sku == SKU_XR9)
67659efa8b5SSam Leffler 		return 1520 + freq;
67759efa8b5SSam Leffler 	if (sku == SKU_GZ901)
67859efa8b5SSam Leffler 		return 1544 + freq;
67959efa8b5SSam Leffler 	if (sku == SKU_SR9)
68059efa8b5SSam Leffler 		return 3344 - freq;
6812992cd22SAdrian Chadd 	if (sku == SKU_XC900M)
6822992cd22SAdrian Chadd 		return 1517 + freq;
6830e56140aSAdrian Chadd 	HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
68459efa8b5SSam Leffler 	    "%s: cannot map freq %u unknown gsm sku %u\n",
68559efa8b5SSam Leffler 	    __func__, freq, sku);
68659efa8b5SSam Leffler 	return freq;
68714779705SSam Leffler }
68814779705SSam Leffler 
68959efa8b5SSam Leffler /*
69059efa8b5SSam Leffler  * Setup the internal/private channel state given a table of
69159efa8b5SSam Leffler  * net80211 channels.  We collapse entries for the same frequency
69259efa8b5SSam Leffler  * and record the frequency for doing noise floor processing
69359efa8b5SSam Leffler  * where we don't have net80211 channel context.
69459efa8b5SSam Leffler  */
69559efa8b5SSam Leffler static HAL_BOOL
69659efa8b5SSam Leffler assignPrivateChannels(struct ath_hal *ah,
69759efa8b5SSam Leffler 	struct ieee80211_channel chans[], int nchans, int sku)
69859efa8b5SSam Leffler {
69959efa8b5SSam Leffler 	HAL_CHANNEL_INTERNAL *ic;
70059efa8b5SSam Leffler 	int i, j, next, freq;
70159efa8b5SSam Leffler 
70259efa8b5SSam Leffler 	next = 0;
70359efa8b5SSam Leffler 	for (i = 0; i < nchans; i++) {
70459efa8b5SSam Leffler 		struct ieee80211_channel *c = &chans[i];
70559efa8b5SSam Leffler 		for (j = i-1; j >= 0; j--)
70659efa8b5SSam Leffler 			if (chans[j].ic_freq == c->ic_freq) {
70759efa8b5SSam Leffler 				c->ic_devdata = chans[j].ic_devdata;
70859efa8b5SSam Leffler 				break;
70959efa8b5SSam Leffler 			}
71059efa8b5SSam Leffler 		if (j < 0) {
71159efa8b5SSam Leffler 			/* new entry, assign a private channel entry */
71259efa8b5SSam Leffler 			if (next >= N(AH_PRIVATE(ah)->ah_channels)) {
71359efa8b5SSam Leffler 				HALDEBUG(ah, HAL_DEBUG_ANY,
71450d5ad0eSSam Leffler 				    "%s: too many channels, max %zu\n",
71559efa8b5SSam Leffler 				    __func__, N(AH_PRIVATE(ah)->ah_channels));
71659efa8b5SSam Leffler 				return AH_FALSE;
71759efa8b5SSam Leffler 			}
71859efa8b5SSam Leffler 			/*
71959efa8b5SSam Leffler 			 * Handle frequency mapping for 900MHz devices.
72059efa8b5SSam Leffler 			 * The hardware uses 2.4GHz frequencies that are
72159efa8b5SSam Leffler 			 * down-converted.  The 802.11 layer uses the
72259efa8b5SSam Leffler 			 * true frequencies.
72359efa8b5SSam Leffler 			 */
72459efa8b5SSam Leffler 			freq = IEEE80211_IS_CHAN_GSM(c) ?
72559efa8b5SSam Leffler 			    ath_hal_mapgsm(sku, c->ic_freq) : c->ic_freq;
72659efa8b5SSam Leffler 
72759efa8b5SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
72859efa8b5SSam Leffler 			    "%s: private[%3u] %u/0x%x -> channel %u\n",
72959efa8b5SSam Leffler 			    __func__, next, c->ic_freq, c->ic_flags, freq);
73059efa8b5SSam Leffler 
73159efa8b5SSam Leffler 			ic = &AH_PRIVATE(ah)->ah_channels[next];
73259efa8b5SSam Leffler 			/*
73359efa8b5SSam Leffler 			 * NB: This clears privFlags which means ancillary
73459efa8b5SSam Leffler 			 *     code like ANI and IQ calibration will be
73559efa8b5SSam Leffler 			 *     restarted and re-setup any per-channel state.
73659efa8b5SSam Leffler 			 */
73759efa8b5SSam Leffler 			OS_MEMZERO(ic, sizeof(*ic));
73859efa8b5SSam Leffler 			ic->channel = freq;
73959efa8b5SSam Leffler 			c->ic_devdata = next;
74059efa8b5SSam Leffler 			next++;
74159efa8b5SSam Leffler 		}
74259efa8b5SSam Leffler 	}
74359efa8b5SSam Leffler 	AH_PRIVATE(ah)->ah_nchan = next;
74459efa8b5SSam Leffler 	HALDEBUG(ah, HAL_DEBUG_ANY, "%s: %u public, %u private channels\n",
74559efa8b5SSam Leffler 	    __func__, nchans, next);
74659efa8b5SSam Leffler 	return AH_TRUE;
74759efa8b5SSam Leffler }
74859efa8b5SSam Leffler 
74959efa8b5SSam Leffler /*
75059efa8b5SSam Leffler  * Setup the channel list based on the information in the EEPROM.
75159efa8b5SSam Leffler  */
75259efa8b5SSam Leffler HAL_STATUS
75359efa8b5SSam Leffler ath_hal_init_channels(struct ath_hal *ah,
75459efa8b5SSam Leffler     struct ieee80211_channel chans[], u_int maxchans, int *nchans,
75559efa8b5SSam Leffler     u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
75659efa8b5SSam Leffler     HAL_BOOL enableExtendedChannels)
75759efa8b5SSam Leffler {
75859efa8b5SSam Leffler 	COUNTRY_CODE_TO_ENUM_RD *country;
7597dd4de13SAdrian Chadd 	REG_DOMAIN *rd5GHz, *rd2GHz;
76059efa8b5SSam Leffler 	HAL_STATUS status;
76159efa8b5SSam Leffler 
76259efa8b5SSam Leffler 	status = getchannels(ah, chans, maxchans, nchans, modeSelect,
76359efa8b5SSam Leffler 	    cc, regDmn, enableExtendedChannels, &country, &rd2GHz, &rd5GHz);
76459efa8b5SSam Leffler 	if (status == HAL_OK &&
76559efa8b5SSam Leffler 	    assignPrivateChannels(ah, chans, *nchans, AH_PRIVATE(ah)->ah_currentRD)) {
76659efa8b5SSam Leffler 		AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz;
76759efa8b5SSam Leffler 		AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz;
76859efa8b5SSam Leffler 
76959efa8b5SSam Leffler 		ah->ah_countryCode = country->countryCode;
77059efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n",
77159efa8b5SSam Leffler 		    __func__, ah->ah_countryCode);
7727dd4de13SAdrian Chadd 
7737dd4de13SAdrian Chadd 		/* Update current DFS domain */
7747dd4de13SAdrian Chadd 		ath_hal_update_dfsdomain(ah);
77559efa8b5SSam Leffler 	} else
77659efa8b5SSam Leffler 		status = HAL_EINVAL;
7778db87e40SAdrian Chadd 
77859efa8b5SSam Leffler 	return status;
77959efa8b5SSam Leffler }
78059efa8b5SSam Leffler 
78159efa8b5SSam Leffler /*
78259efa8b5SSam Leffler  * Set the channel list.
78359efa8b5SSam Leffler  */
78459efa8b5SSam Leffler HAL_STATUS
78559efa8b5SSam Leffler ath_hal_set_channels(struct ath_hal *ah,
78659efa8b5SSam Leffler     struct ieee80211_channel chans[], int nchans,
78759efa8b5SSam Leffler     HAL_CTRY_CODE cc, HAL_REG_DOMAIN rd)
78859efa8b5SSam Leffler {
78959efa8b5SSam Leffler 	COUNTRY_CODE_TO_ENUM_RD *country;
79059efa8b5SSam Leffler 	REG_DOMAIN *rd5GHz, *rd2GHz;
79159efa8b5SSam Leffler 	HAL_STATUS status;
79259efa8b5SSam Leffler 
79359efa8b5SSam Leffler 	switch (rd) {
79459efa8b5SSam Leffler 	case SKU_SR9:
79559efa8b5SSam Leffler 	case SKU_XR9:
79659efa8b5SSam Leffler 	case SKU_GZ901:
7972992cd22SAdrian Chadd 	case SKU_XC900M:
79859efa8b5SSam Leffler 		/*
79959efa8b5SSam Leffler 		 * Map 900MHz sku's.  The frequencies will be mapped
80059efa8b5SSam Leffler 		 * according to the sku to compensate for the down-converter.
80159efa8b5SSam Leffler 		 * We use the FCC for these sku's as the mapped channel
80259efa8b5SSam Leffler 		 * list is known compatible (will need to change if/when
80359efa8b5SSam Leffler 		 * vendors do different mapping in different locales).
80459efa8b5SSam Leffler 		 */
80559efa8b5SSam Leffler 		status = getregstate(ah, CTRY_DEFAULT, SKU_FCC,
80659efa8b5SSam Leffler 		    &country, &rd2GHz, &rd5GHz);
80759efa8b5SSam Leffler 		break;
80859efa8b5SSam Leffler 	default:
80959efa8b5SSam Leffler 		status = getregstate(ah, cc, rd,
81059efa8b5SSam Leffler 		    &country, &rd2GHz, &rd5GHz);
81159efa8b5SSam Leffler 		rd = AH_PRIVATE(ah)->ah_currentRD;
81259efa8b5SSam Leffler 		break;
81359efa8b5SSam Leffler 	}
81459efa8b5SSam Leffler 	if (status == HAL_OK && assignPrivateChannels(ah, chans, nchans, rd)) {
81559efa8b5SSam Leffler 		AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz;
81659efa8b5SSam Leffler 		AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz;
81759efa8b5SSam Leffler 
81859efa8b5SSam Leffler 		ah->ah_countryCode = country->countryCode;
81959efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n",
82059efa8b5SSam Leffler 		    __func__, ah->ah_countryCode);
82159efa8b5SSam Leffler 	} else
82259efa8b5SSam Leffler 		status = HAL_EINVAL;
8237dd4de13SAdrian Chadd 
8247dd4de13SAdrian Chadd 	if (status == HAL_OK) {
8257dd4de13SAdrian Chadd 		/* Update current DFS domain */
8267dd4de13SAdrian Chadd 		(void) ath_hal_update_dfsdomain(ah);
8277dd4de13SAdrian Chadd 	}
82859efa8b5SSam Leffler 	return status;
82959efa8b5SSam Leffler }
83059efa8b5SSam Leffler 
83159efa8b5SSam Leffler #ifdef AH_DEBUG
83259efa8b5SSam Leffler /*
83359efa8b5SSam Leffler  * Return the internal channel corresponding to a public channel.
83459efa8b5SSam Leffler  * NB: normally this routine is inline'd (see ah_internal.h)
83559efa8b5SSam Leffler  */
83659efa8b5SSam Leffler HAL_CHANNEL_INTERNAL *
83759efa8b5SSam Leffler ath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c)
83859efa8b5SSam Leffler {
83959efa8b5SSam Leffler 	HAL_CHANNEL_INTERNAL *cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata];
84059efa8b5SSam Leffler 
84159efa8b5SSam Leffler 	if (c->ic_devdata < AH_PRIVATE(ah)->ah_nchan &&
84259efa8b5SSam Leffler 	    (c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)))
84314779705SSam Leffler 		return cc;
84459efa8b5SSam Leffler 	if (c->ic_devdata >= AH_PRIVATE(ah)->ah_nchan) {
84559efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_ANY,
84659efa8b5SSam Leffler 		    "%s: bad mapping, devdata %u nchans %u\n",
84759efa8b5SSam Leffler 		   __func__, c->ic_devdata, AH_PRIVATE(ah)->ah_nchan);
84859efa8b5SSam Leffler 		HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan);
84959efa8b5SSam Leffler 	} else {
85059efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_ANY,
85159efa8b5SSam Leffler 		    "%s: no match for %u/0x%x devdata %u channel %u\n",
85259efa8b5SSam Leffler 		   __func__, c->ic_freq, c->ic_flags, c->ic_devdata,
85359efa8b5SSam Leffler 		   cc->channel);
85459efa8b5SSam Leffler 		HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c));
85514779705SSam Leffler 	}
85614779705SSam Leffler 	return AH_NULL;
85759efa8b5SSam Leffler }
85859efa8b5SSam Leffler #endif /* AH_DEBUG */
85959efa8b5SSam Leffler 
86059efa8b5SSam Leffler #define isWwrSKU(_ah) \
86159efa8b5SSam Leffler 	((getEepromRD((_ah)) & WORLD_SKU_MASK) == WORLD_SKU_PREFIX || \
86259efa8b5SSam Leffler 	  getEepromRD(_ah) == WORLD)
86359efa8b5SSam Leffler 
86459efa8b5SSam Leffler /*
86559efa8b5SSam Leffler  * Return the test group for the specific channel based on
86659efa8b5SSam Leffler  * the current regulatory setup.
86759efa8b5SSam Leffler  */
86859efa8b5SSam Leffler u_int
86959efa8b5SSam Leffler ath_hal_getctl(struct ath_hal *ah, const struct ieee80211_channel *c)
87059efa8b5SSam Leffler {
87159efa8b5SSam Leffler 	u_int ctl;
87259efa8b5SSam Leffler 
87359efa8b5SSam Leffler 	if (AH_PRIVATE(ah)->ah_rd2GHz == AH_PRIVATE(ah)->ah_rd5GHz ||
87459efa8b5SSam Leffler 	    (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)))
87559efa8b5SSam Leffler 		ctl = SD_NO_CTL;
87659efa8b5SSam Leffler 	else if (IEEE80211_IS_CHAN_2GHZ(c))
87759efa8b5SSam Leffler 		ctl = AH_PRIVATE(ah)->ah_rd2GHz->conformanceTestLimit;
87859efa8b5SSam Leffler 	else
87959efa8b5SSam Leffler 		ctl = AH_PRIVATE(ah)->ah_rd5GHz->conformanceTestLimit;
88059efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_B(c))
88159efa8b5SSam Leffler 		return ctl | CTL_11B;
88259efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_G(c))
88359efa8b5SSam Leffler 		return ctl | CTL_11G;
88459efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_108G(c))
88559efa8b5SSam Leffler 		return ctl | CTL_108G;
88659efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_TURBO(c))
88759efa8b5SSam Leffler 		return ctl | CTL_TURBO;
88859efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_A(c))
88959efa8b5SSam Leffler 		return ctl | CTL_11A;
89059efa8b5SSam Leffler 	return ctl;
89114779705SSam Leffler }
89214779705SSam Leffler 
8937dd4de13SAdrian Chadd 
8947dd4de13SAdrian Chadd /*
8957dd4de13SAdrian Chadd  * Update the current dfsDomain setting based on the given
8967dd4de13SAdrian Chadd  * country code.
8977dd4de13SAdrian Chadd  *
8987dd4de13SAdrian Chadd  * Since FreeBSD/net80211 allows the channel set to change
8997dd4de13SAdrian Chadd  * after the card has been setup (via ath_hal_init_channels())
9007dd4de13SAdrian Chadd  * this function method is needed to update ah_dfsDomain.
9017dd4de13SAdrian Chadd  */
9027dd4de13SAdrian Chadd void
9037dd4de13SAdrian Chadd ath_hal_update_dfsdomain(struct ath_hal *ah)
9047dd4de13SAdrian Chadd {
9057dd4de13SAdrian Chadd 	const REG_DOMAIN *rd5GHz = AH_PRIVATE(ah)->ah_rd5GHz;
9067dd4de13SAdrian Chadd 	HAL_DFS_DOMAIN dfsDomain = HAL_DFS_UNINIT_DOMAIN;
9077dd4de13SAdrian Chadd 
9087dd4de13SAdrian Chadd 	if (rd5GHz->dfsMask & DFS_FCC3)
9097dd4de13SAdrian Chadd 		dfsDomain = HAL_DFS_FCC_DOMAIN;
9107dd4de13SAdrian Chadd 	if (rd5GHz->dfsMask & DFS_ETSI)
9117dd4de13SAdrian Chadd 		dfsDomain = HAL_DFS_ETSI_DOMAIN;
9127dd4de13SAdrian Chadd 	if (rd5GHz->dfsMask & DFS_MKK4)
9137dd4de13SAdrian Chadd 		dfsDomain = HAL_DFS_MKK4_DOMAIN;
9147dd4de13SAdrian Chadd 	AH_PRIVATE(ah)->ah_dfsDomain = dfsDomain;
9157dd4de13SAdrian Chadd 	HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s ah_dfsDomain: %d\n",
9167dd4de13SAdrian Chadd 	    __func__, AH_PRIVATE(ah)->ah_dfsDomain);
9177dd4de13SAdrian Chadd }
9187dd4de13SAdrian Chadd 
9197dd4de13SAdrian Chadd 
92014779705SSam Leffler /*
92114779705SSam Leffler  * Return the max allowed antenna gain and apply any regulatory
92214779705SSam Leffler  * domain specific changes.
92314779705SSam Leffler  *
92414779705SSam Leffler  * NOTE: a negative reduction is possible in RD's that only
92514779705SSam Leffler  * measure radiated power (e.g., ETSI) which would increase
92614779705SSam Leffler  * that actual conducted output power (though never beyond
92714779705SSam Leffler  * the calibrated target power).
92814779705SSam Leffler  */
92914779705SSam Leffler u_int
93059efa8b5SSam Leffler ath_hal_getantennareduction(struct ath_hal *ah,
93159efa8b5SSam Leffler     const struct ieee80211_channel *chan, u_int twiceGain)
93214779705SSam Leffler {
93359efa8b5SSam Leffler 	int8_t antennaMax = twiceGain - chan->ic_maxantgain*2;
93414779705SSam Leffler 	return (antennaMax < 0) ? 0 : antennaMax;
93514779705SSam Leffler }
936