xref: /freebsd/sys/dev/ath/ath_hal/ah_regdomain.c (revision 7dd4de1301ca19b1e92b7cc611017cd67b7ea234)
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... */
3814779705SSam Leffler #define	N(a)	(sizeof (a) / sizeof (a[0]))
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[] = {
10259efa8b5SSam Leffler 	{ HAL_MODE_TURBO,	IEEE80211_CHAN_ST },
10359efa8b5SSam Leffler 	{ HAL_MODE_11A,		IEEE80211_CHAN_A },
10459efa8b5SSam Leffler 	{ HAL_MODE_11B,		IEEE80211_CHAN_B },
10559efa8b5SSam Leffler 	{ HAL_MODE_11G,		IEEE80211_CHAN_G },
10659efa8b5SSam Leffler 	{ HAL_MODE_11G_TURBO,	IEEE80211_CHAN_108G },
10759efa8b5SSam Leffler 	{ HAL_MODE_11A_TURBO,	IEEE80211_CHAN_108A },
10859efa8b5SSam Leffler 	{ HAL_MODE_11A_QUARTER_RATE,
10959efa8b5SSam Leffler 	  IEEE80211_CHAN_A | IEEE80211_CHAN_QUARTER },
11059efa8b5SSam Leffler 	{ HAL_MODE_11A_HALF_RATE,
11159efa8b5SSam Leffler 	  IEEE80211_CHAN_A | IEEE80211_CHAN_HALF },
11259efa8b5SSam Leffler 	{ HAL_MODE_11G_QUARTER_RATE,
11359efa8b5SSam Leffler 	  IEEE80211_CHAN_G | IEEE80211_CHAN_QUARTER },
11459efa8b5SSam Leffler 	{ HAL_MODE_11G_HALF_RATE,
11559efa8b5SSam Leffler 	  IEEE80211_CHAN_G | IEEE80211_CHAN_HALF },
116b2e73ce9SSam Leffler 	{ HAL_MODE_11NG_HT20,	IEEE80211_CHAN_G | IEEE80211_CHAN_HT20 },
11759efa8b5SSam Leffler 	{ HAL_MODE_11NG_HT40PLUS,
118b2e73ce9SSam Leffler 	  IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U },
11959efa8b5SSam Leffler 	{ HAL_MODE_11NG_HT40MINUS,
120b2e73ce9SSam Leffler 	  IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D },
121b2e73ce9SSam Leffler 	{ HAL_MODE_11NA_HT20,	IEEE80211_CHAN_A | IEEE80211_CHAN_HT20 },
12259efa8b5SSam Leffler 	{ HAL_MODE_11NA_HT40PLUS,
123b2e73ce9SSam Leffler 	  IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U },
12459efa8b5SSam Leffler 	{ HAL_MODE_11NA_HT40MINUS,
125b2e73ce9SSam Leffler 	  IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D },
12614779705SSam Leffler };
12714779705SSam Leffler 
128*7dd4de13SAdrian Chadd static void ath_hal_update_dfsdomain(struct ath_hal *ah);
129*7dd4de13SAdrian Chadd 
13059efa8b5SSam Leffler static OS_INLINE uint16_t
13114779705SSam Leffler getEepromRD(struct ath_hal *ah)
13214779705SSam Leffler {
13314779705SSam Leffler 	return AH_PRIVATE(ah)->ah_currentRD &~ WORLDWIDE_ROAMING_FLAG;
13414779705SSam Leffler }
13514779705SSam Leffler 
13614779705SSam Leffler /*
13714779705SSam Leffler  * Test to see if the bitmask array is all zeros
13814779705SSam Leffler  */
13914779705SSam Leffler static HAL_BOOL
14014779705SSam Leffler isChanBitMaskZero(const uint64_t *bitmask)
14114779705SSam Leffler {
14214779705SSam Leffler #if BMLEN > 2
14314779705SSam Leffler #error	"add more cases"
14414779705SSam Leffler #endif
14514779705SSam Leffler #if BMLEN > 1
14614779705SSam Leffler 	if (bitmask[1] != 0)
14714779705SSam Leffler 		return AH_FALSE;
14814779705SSam Leffler #endif
14914779705SSam Leffler 	return (bitmask[0] == 0);
15014779705SSam Leffler }
15114779705SSam Leffler 
15214779705SSam Leffler /*
15314779705SSam Leffler  * Return whether or not the regulatory domain/country in EEPROM
15414779705SSam Leffler  * is acceptable.
15514779705SSam Leffler  */
15614779705SSam Leffler static HAL_BOOL
15714779705SSam Leffler isEepromValid(struct ath_hal *ah)
15814779705SSam Leffler {
15914779705SSam Leffler 	uint16_t rd = getEepromRD(ah);
16014779705SSam Leffler 	int i;
16114779705SSam Leffler 
16214779705SSam Leffler 	if (rd & COUNTRY_ERD_FLAG) {
16314779705SSam Leffler 		uint16_t cc = rd &~ COUNTRY_ERD_FLAG;
16414779705SSam Leffler 		for (i = 0; i < N(allCountries); i++)
16514779705SSam Leffler 			if (allCountries[i].countryCode == cc)
16614779705SSam Leffler 				return AH_TRUE;
16714779705SSam Leffler 	} else {
16814779705SSam Leffler 		for (i = 0; i < N(regDomainPairs); i++)
16914779705SSam Leffler 			if (regDomainPairs[i].regDmnEnum == rd)
17014779705SSam Leffler 				return AH_TRUE;
17114779705SSam Leffler 	}
172f6f1dfb6SAdrian Chadd 	HALDEBUG_G(ah, HAL_DEBUG_REGDOMAIN,
17314779705SSam Leffler 	    "%s: invalid regulatory domain/country code 0x%x\n", __func__, rd);
17414779705SSam Leffler 	return AH_FALSE;
17514779705SSam Leffler }
17614779705SSam Leffler 
17714779705SSam Leffler /*
17814779705SSam Leffler  * Find the pointer to the country element in the country table
17914779705SSam Leffler  * corresponding to the country code
18014779705SSam Leffler  */
18114779705SSam Leffler static COUNTRY_CODE_TO_ENUM_RD*
18214779705SSam Leffler findCountry(HAL_CTRY_CODE countryCode)
18314779705SSam Leffler {
18414779705SSam Leffler 	int i;
18514779705SSam Leffler 
18614779705SSam Leffler 	for (i = 0; i < N(allCountries); i++) {
18714779705SSam Leffler 		if (allCountries[i].countryCode == countryCode)
18814779705SSam Leffler 			return &allCountries[i];
18914779705SSam Leffler 	}
19059efa8b5SSam Leffler 	return AH_NULL;
19159efa8b5SSam Leffler }
19259efa8b5SSam Leffler 
19359efa8b5SSam Leffler static REG_DOMAIN *
19459efa8b5SSam Leffler findRegDmn(int regDmn)
19559efa8b5SSam Leffler {
19659efa8b5SSam Leffler 	int i;
19759efa8b5SSam Leffler 
19859efa8b5SSam Leffler 	for (i = 0; i < N(regDomains); i++) {
19959efa8b5SSam Leffler 		if (regDomains[i].regDmnEnum == regDmn)
20059efa8b5SSam Leffler 			return &regDomains[i];
20159efa8b5SSam Leffler 	}
20259efa8b5SSam Leffler 	return AH_NULL;
20359efa8b5SSam Leffler }
20459efa8b5SSam Leffler 
20559efa8b5SSam Leffler static REG_DMN_PAIR_MAPPING *
20659efa8b5SSam Leffler findRegDmnPair(int regDmnPair)
20759efa8b5SSam Leffler {
20859efa8b5SSam Leffler 	int i;
20959efa8b5SSam Leffler 
21059efa8b5SSam Leffler 	if (regDmnPair != NO_ENUMRD) {
21159efa8b5SSam Leffler 		for (i = 0; i < N(regDomainPairs); i++) {
21259efa8b5SSam Leffler 			if (regDomainPairs[i].regDmnEnum == regDmnPair)
21359efa8b5SSam Leffler 				return &regDomainPairs[i];
21459efa8b5SSam Leffler 		}
21559efa8b5SSam Leffler 	}
21659efa8b5SSam Leffler 	return AH_NULL;
21714779705SSam Leffler }
21814779705SSam Leffler 
21914779705SSam Leffler /*
22014779705SSam Leffler  * Calculate a default country based on the EEPROM setting.
22114779705SSam Leffler  */
22214779705SSam Leffler static HAL_CTRY_CODE
22314779705SSam Leffler getDefaultCountry(struct ath_hal *ah)
22414779705SSam Leffler {
22559efa8b5SSam Leffler 	REG_DMN_PAIR_MAPPING *regpair;
22614779705SSam Leffler 	uint16_t rd;
22714779705SSam Leffler 
22814779705SSam Leffler 	rd = getEepromRD(ah);
22914779705SSam Leffler 	if (rd & COUNTRY_ERD_FLAG) {
23059efa8b5SSam Leffler 		COUNTRY_CODE_TO_ENUM_RD *country;
23114779705SSam Leffler 		uint16_t cc = rd & ~COUNTRY_ERD_FLAG;
23214779705SSam Leffler 		country = findCountry(cc);
23314779705SSam Leffler 		if (country != AH_NULL)
23414779705SSam Leffler 			return cc;
23514779705SSam Leffler 	}
23614779705SSam Leffler 	/*
23714779705SSam Leffler 	 * Check reg domains that have only one country
23814779705SSam Leffler 	 */
23959efa8b5SSam Leffler 	regpair = findRegDmnPair(rd);
24059efa8b5SSam Leffler 	return (regpair != AH_NULL) ? regpair->singleCC : CTRY_DEFAULT;
24114779705SSam Leffler }
24214779705SSam Leffler 
24314779705SSam Leffler static HAL_BOOL
24414779705SSam Leffler IS_BIT_SET(int bit, const uint64_t bitmask[])
24514779705SSam Leffler {
24614779705SSam Leffler 	int byteOffset, bitnum;
24714779705SSam Leffler 	uint64_t val;
24814779705SSam Leffler 
24914779705SSam Leffler 	byteOffset = bit/64;
25014779705SSam Leffler 	bitnum = bit - byteOffset*64;
25114779705SSam Leffler 	val = ((uint64_t) 1) << bitnum;
25214779705SSam Leffler 	return (bitmask[byteOffset] & val) != 0;
25314779705SSam Leffler }
25414779705SSam Leffler 
25559efa8b5SSam Leffler static HAL_STATUS
25659efa8b5SSam Leffler getregstate(struct ath_hal *ah, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
25759efa8b5SSam Leffler     COUNTRY_CODE_TO_ENUM_RD **pcountry,
25859efa8b5SSam Leffler     REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz)
25914779705SSam Leffler {
26059efa8b5SSam Leffler 	COUNTRY_CODE_TO_ENUM_RD *country;
26159efa8b5SSam Leffler 	REG_DOMAIN *rd5GHz, *rd2GHz;
26214779705SSam Leffler 
26359efa8b5SSam Leffler 	if (cc == CTRY_DEFAULT && regDmn == SKU_NONE) {
26414779705SSam Leffler 		/*
26514779705SSam Leffler 		 * Validate the EEPROM setting and setup defaults
26614779705SSam Leffler 		 */
26714779705SSam Leffler 		if (!isEepromValid(ah)) {
26814779705SSam Leffler 			/*
26914779705SSam Leffler 			 * Don't return any channels if the EEPROM has an
27014779705SSam Leffler 			 * invalid regulatory domain/country code setting.
27114779705SSam Leffler 			 */
27214779705SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
27314779705SSam Leffler 			    "%s: invalid EEPROM contents\n",__func__);
27459efa8b5SSam Leffler 			return HAL_EEBADREG;
27514779705SSam Leffler 		}
27614779705SSam Leffler 
27759efa8b5SSam Leffler 		cc = getDefaultCountry(ah);
27859efa8b5SSam Leffler 		country = findCountry(cc);
27914779705SSam Leffler 		if (country == AH_NULL) {
28014779705SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
28159efa8b5SSam Leffler 			    "NULL Country!, cc %d\n", cc);
28259efa8b5SSam Leffler 			return HAL_EEBADCC;
28314779705SSam Leffler 		}
28459efa8b5SSam Leffler 		regDmn = country->regDmnEnum;
28559efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: EEPROM cc %u rd 0x%x\n",
28659efa8b5SSam Leffler 		    __func__, cc, regDmn);
28759efa8b5SSam Leffler 
28859efa8b5SSam Leffler 		if (country->countryCode == CTRY_DEFAULT) {
28959efa8b5SSam Leffler 			/*
29059efa8b5SSam Leffler 			 * Check EEPROM; SKU may be for a country, single
29159efa8b5SSam Leffler 			 * domain, or multiple domains (WWR).
29259efa8b5SSam Leffler 			 */
29359efa8b5SSam Leffler 			uint16_t rdnum = getEepromRD(ah);
29459efa8b5SSam Leffler 			if ((rdnum & COUNTRY_ERD_FLAG) == 0 &&
29559efa8b5SSam Leffler 			    (findRegDmn(rdnum) != AH_NULL ||
29659efa8b5SSam Leffler 			     findRegDmnPair(rdnum) != AH_NULL)) {
29759efa8b5SSam Leffler 				regDmn = rdnum;
29814779705SSam Leffler 				HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
29959efa8b5SSam Leffler 				    "%s: EEPROM rd 0x%x\n", __func__, rdnum);
30059efa8b5SSam Leffler 			}
30159efa8b5SSam Leffler 		}
30259efa8b5SSam Leffler 	} else {
30359efa8b5SSam Leffler 		country = findCountry(cc);
30459efa8b5SSam Leffler 		if (country == AH_NULL) {
30559efa8b5SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
30659efa8b5SSam Leffler 			    "unknown country, cc %d\n", cc);
30759efa8b5SSam Leffler 			return HAL_EINVAL;
30859efa8b5SSam Leffler 		}
30959efa8b5SSam Leffler 		if (regDmn == SKU_NONE)
31059efa8b5SSam Leffler 			regDmn = country->regDmnEnum;
31159efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u rd 0x%x\n",
31259efa8b5SSam Leffler 		    __func__, cc, regDmn);
31314779705SSam Leffler 	}
31414779705SSam Leffler 
31559efa8b5SSam Leffler 	/*
31659efa8b5SSam Leffler 	 * Setup per-band state.
31759efa8b5SSam Leffler 	 */
31859efa8b5SSam Leffler 	if ((regDmn & MULTI_DOMAIN_MASK) == 0) {
31959efa8b5SSam Leffler 		REG_DMN_PAIR_MAPPING *regpair = findRegDmnPair(regDmn);
32059efa8b5SSam Leffler 		if (regpair == AH_NULL) {
32159efa8b5SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
32259efa8b5SSam Leffler 			    "%s: no reg domain pair %u for country %u\n",
32359efa8b5SSam Leffler 			    __func__, regDmn, country->countryCode);
32459efa8b5SSam Leffler 			return HAL_EINVAL;
32559efa8b5SSam Leffler 		}
32659efa8b5SSam Leffler 		rd5GHz = findRegDmn(regpair->regDmn5GHz);
32759efa8b5SSam Leffler 		if (rd5GHz == AH_NULL) {
32859efa8b5SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
32959efa8b5SSam Leffler 			    "%s: no 5GHz reg domain %u for country %u\n",
33059efa8b5SSam Leffler 			    __func__, regpair->regDmn5GHz, country->countryCode);
33159efa8b5SSam Leffler 			return HAL_EINVAL;
33259efa8b5SSam Leffler 		}
33359efa8b5SSam Leffler 		rd2GHz = findRegDmn(regpair->regDmn2GHz);
33459efa8b5SSam Leffler 		if (rd2GHz == AH_NULL) {
33559efa8b5SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
33659efa8b5SSam Leffler 			    "%s: no 2GHz reg domain %u for country %u\n",
33759efa8b5SSam Leffler 			    __func__, regpair->regDmn2GHz, country->countryCode);
33859efa8b5SSam Leffler 			return HAL_EINVAL;
33959efa8b5SSam Leffler 		}
34059efa8b5SSam Leffler 	} else {
34159efa8b5SSam Leffler 		rd5GHz = rd2GHz = findRegDmn(regDmn);
34259efa8b5SSam Leffler 		if (rd2GHz == AH_NULL) {
34359efa8b5SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
34459efa8b5SSam Leffler 			    "%s: no unitary reg domain %u for country %u\n",
34559efa8b5SSam Leffler 			    __func__, regDmn, country->countryCode);
34659efa8b5SSam Leffler 			return HAL_EINVAL;
34759efa8b5SSam Leffler 		}
34859efa8b5SSam Leffler 	}
34959efa8b5SSam Leffler 	if (pcountry != AH_NULL)
35059efa8b5SSam Leffler 		*pcountry = country;
35159efa8b5SSam Leffler 	*prd2GHz = rd2GHz;
35259efa8b5SSam Leffler 	*prd5GHz = rd5GHz;
35359efa8b5SSam Leffler 	return HAL_OK;
35459efa8b5SSam Leffler }
35514779705SSam Leffler 
35659efa8b5SSam Leffler /*
35759efa8b5SSam Leffler  * Construct the channel list for the specified regulatory config.
35859efa8b5SSam Leffler  */
35959efa8b5SSam Leffler static HAL_STATUS
36059efa8b5SSam Leffler getchannels(struct ath_hal *ah,
36159efa8b5SSam Leffler     struct ieee80211_channel chans[], u_int maxchans, int *nchans,
36259efa8b5SSam Leffler     u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
36359efa8b5SSam Leffler     HAL_BOOL enableExtendedChannels,
36459efa8b5SSam Leffler     COUNTRY_CODE_TO_ENUM_RD **pcountry,
36559efa8b5SSam Leffler     REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz)
36659efa8b5SSam Leffler {
36759efa8b5SSam Leffler #define CHANNEL_HALF_BW		10
36859efa8b5SSam Leffler #define CHANNEL_QUARTER_BW	5
36959efa8b5SSam Leffler #define	HAL_MODE_11A_ALL \
37059efa8b5SSam Leffler 	(HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \
37159efa8b5SSam Leffler 	 HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE)
37259efa8b5SSam Leffler 	REG_DOMAIN *rd5GHz, *rd2GHz;
37359efa8b5SSam Leffler 	u_int modesAvail;
37459efa8b5SSam Leffler 	const struct cmode *cm;
37559efa8b5SSam Leffler 	struct ieee80211_channel *ic;
37659efa8b5SSam Leffler 	int next, b;
37759efa8b5SSam Leffler 	HAL_STATUS status;
37859efa8b5SSam Leffler 
37959efa8b5SSam Leffler 	HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u regDmn 0x%x mode 0x%x%s\n",
38059efa8b5SSam Leffler 	    __func__, cc, regDmn, modeSelect,
38159efa8b5SSam Leffler 	    enableExtendedChannels ? " ecm" : "");
38259efa8b5SSam Leffler 
38359efa8b5SSam Leffler 	status = getregstate(ah, cc, regDmn, pcountry, &rd2GHz, &rd5GHz);
38459efa8b5SSam Leffler 	if (status != HAL_OK)
38559efa8b5SSam Leffler 		return status;
38659efa8b5SSam Leffler 
38759efa8b5SSam Leffler 	/* get modes that HW is capable of */
38859efa8b5SSam Leffler 	modesAvail = ath_hal_getWirelessModes(ah);
38959efa8b5SSam Leffler 	/* optimize work below if no 11a channels */
39059efa8b5SSam Leffler 	if (isChanBitMaskZero(rd5GHz->chan11a) &&
39159efa8b5SSam Leffler 	    (modesAvail & HAL_MODE_11A_ALL)) {
39259efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
39359efa8b5SSam Leffler 		    "%s: disallow all 11a\n", __func__);
39459efa8b5SSam Leffler 		modesAvail &= ~HAL_MODE_11A_ALL;
39559efa8b5SSam Leffler 	}
39659efa8b5SSam Leffler 
39714779705SSam Leffler 	next = 0;
39859efa8b5SSam Leffler 	ic = &chans[0];
39914779705SSam Leffler 	for (cm = modes; cm < &modes[N(modes)]; cm++) {
40014779705SSam Leffler 		uint16_t c, c_hi, c_lo;
40114779705SSam Leffler 		uint64_t *channelBM = AH_NULL;
40214779705SSam Leffler 		REG_DMN_FREQ_BAND *fband = AH_NULL,*freqs;
40314779705SSam Leffler 		int low_adj, hi_adj, channelSep, lastc;
40459efa8b5SSam Leffler 		uint32_t rdflags;
40559efa8b5SSam Leffler 		uint64_t dfsMask;
40659efa8b5SSam Leffler 		uint64_t pscan;
40714779705SSam Leffler 
40814779705SSam Leffler 		if ((cm->mode & modeSelect) == 0) {
40914779705SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
41014779705SSam Leffler 			    "%s: skip mode 0x%x flags 0x%x\n",
41114779705SSam Leffler 			    __func__, cm->mode, cm->flags);
41214779705SSam Leffler 			continue;
41314779705SSam Leffler 		}
41414779705SSam Leffler 		if ((cm->mode & modesAvail) == 0) {
41514779705SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
41614779705SSam Leffler 			    "%s: !avail mode 0x%x (0x%x) flags 0x%x\n",
41714779705SSam Leffler 			    __func__, modesAvail, cm->mode, cm->flags);
41814779705SSam Leffler 			continue;
41914779705SSam Leffler 		}
42014779705SSam Leffler 		if (!ath_hal_getChannelEdges(ah, cm->flags, &c_lo, &c_hi)) {
42114779705SSam Leffler 			/* channel not supported by hardware, skip it */
42214779705SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
42314779705SSam Leffler 			    "%s: channels 0x%x not supported by hardware\n",
42414779705SSam Leffler 			    __func__,cm->flags);
42514779705SSam Leffler 			continue;
42614779705SSam Leffler 		}
42714779705SSam Leffler 		switch (cm->mode) {
42814779705SSam Leffler 		case HAL_MODE_TURBO:
42959efa8b5SSam Leffler 		case HAL_MODE_11A_TURBO:
43059efa8b5SSam Leffler 			rdflags = rd5GHz->flags;
43159efa8b5SSam Leffler 			dfsMask = rd5GHz->dfsMask;
43259efa8b5SSam Leffler 			pscan = rd5GHz->pscan;
43359efa8b5SSam Leffler 			if (cm->mode == HAL_MODE_TURBO)
43459efa8b5SSam Leffler 				channelBM = rd5GHz->chan11a_turbo;
43559efa8b5SSam Leffler 			else
43659efa8b5SSam Leffler 				channelBM = rd5GHz->chan11a_dyn_turbo;
43714779705SSam Leffler 			freqs = &regDmn5GhzTurboFreq[0];
43859efa8b5SSam Leffler 			break;
43959efa8b5SSam Leffler 		case HAL_MODE_11G_TURBO:
44059efa8b5SSam Leffler 			rdflags = rd2GHz->flags;
44159efa8b5SSam Leffler 			dfsMask = rd2GHz->dfsMask;
44259efa8b5SSam Leffler 			pscan = rd2GHz->pscan;
44359efa8b5SSam Leffler 			channelBM = rd2GHz->chan11g_turbo;
44459efa8b5SSam Leffler 			freqs = &regDmn2Ghz11gTurboFreq[0];
44514779705SSam Leffler 			break;
44614779705SSam Leffler 		case HAL_MODE_11A:
44714779705SSam Leffler 		case HAL_MODE_11A_HALF_RATE:
44814779705SSam Leffler 		case HAL_MODE_11A_QUARTER_RATE:
44914779705SSam Leffler 		case HAL_MODE_11NA_HT20:
45014779705SSam Leffler 		case HAL_MODE_11NA_HT40PLUS:
45114779705SSam Leffler 		case HAL_MODE_11NA_HT40MINUS:
45259efa8b5SSam Leffler 			rdflags = rd5GHz->flags;
45359efa8b5SSam Leffler 			dfsMask = rd5GHz->dfsMask;
45459efa8b5SSam Leffler 			pscan = rd5GHz->pscan;
45514779705SSam Leffler 			if (cm->mode == HAL_MODE_11A_HALF_RATE)
45659efa8b5SSam Leffler 				channelBM = rd5GHz->chan11a_half;
45714779705SSam Leffler 			else if (cm->mode == HAL_MODE_11A_QUARTER_RATE)
45859efa8b5SSam Leffler 				channelBM = rd5GHz->chan11a_quarter;
45914779705SSam Leffler 			else
46059efa8b5SSam Leffler 				channelBM = rd5GHz->chan11a;
46114779705SSam Leffler 			freqs = &regDmn5GhzFreq[0];
46214779705SSam Leffler 			break;
46314779705SSam Leffler 		case HAL_MODE_11B:
46414779705SSam Leffler 		case HAL_MODE_11G:
46514779705SSam Leffler 		case HAL_MODE_11G_HALF_RATE:
46614779705SSam Leffler 		case HAL_MODE_11G_QUARTER_RATE:
46714779705SSam Leffler 		case HAL_MODE_11NG_HT20:
46814779705SSam Leffler 		case HAL_MODE_11NG_HT40PLUS:
46914779705SSam Leffler 		case HAL_MODE_11NG_HT40MINUS:
47059efa8b5SSam Leffler 			rdflags = rd2GHz->flags;
47159efa8b5SSam Leffler 			dfsMask = rd2GHz->dfsMask;
47259efa8b5SSam Leffler 			pscan = rd2GHz->pscan;
47314779705SSam Leffler 			if (cm->mode == HAL_MODE_11G_HALF_RATE)
47459efa8b5SSam Leffler 				channelBM = rd2GHz->chan11g_half;
47514779705SSam Leffler 			else if (cm->mode == HAL_MODE_11G_QUARTER_RATE)
47659efa8b5SSam Leffler 				channelBM = rd2GHz->chan11g_quarter;
47759efa8b5SSam Leffler 			else if (cm->mode == HAL_MODE_11B)
47859efa8b5SSam Leffler 				channelBM = rd2GHz->chan11b;
47914779705SSam Leffler 			else
48059efa8b5SSam Leffler 				channelBM = rd2GHz->chan11g;
48159efa8b5SSam Leffler 			if (cm->mode == HAL_MODE_11B)
48259efa8b5SSam Leffler 				freqs = &regDmn2GhzFreq[0];
48359efa8b5SSam Leffler 			else
48414779705SSam Leffler 				freqs = &regDmn2Ghz11gFreq[0];
48514779705SSam Leffler 			break;
48614779705SSam Leffler 		default:
48714779705SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
48814779705SSam Leffler 			    "%s: Unkonwn HAL mode 0x%x\n", __func__, cm->mode);
48914779705SSam Leffler 			continue;
49014779705SSam Leffler 		}
49114779705SSam Leffler 		if (isChanBitMaskZero(channelBM))
49214779705SSam Leffler 			continue;
49314779705SSam Leffler 		/*
49414779705SSam Leffler 		 * Setup special handling for HT40 channels; e.g.
49514779705SSam Leffler 		 * 5G HT40 channels require 40Mhz channel separation.
49614779705SSam Leffler 		 */
49714779705SSam Leffler 		hi_adj = (cm->mode == HAL_MODE_11NA_HT40PLUS ||
49814779705SSam Leffler 		    cm->mode == HAL_MODE_11NG_HT40PLUS) ? -20 : 0;
49914779705SSam Leffler 		low_adj = (cm->mode == HAL_MODE_11NA_HT40MINUS ||
50014779705SSam Leffler 		    cm->mode == HAL_MODE_11NG_HT40MINUS) ? 20 : 0;
50114779705SSam Leffler 		channelSep = (cm->mode == HAL_MODE_11NA_HT40PLUS ||
50214779705SSam Leffler 		    cm->mode == HAL_MODE_11NA_HT40MINUS) ? 40 : 0;
50314779705SSam Leffler 
50414779705SSam Leffler 		for (b = 0; b < 64*BMLEN; b++) {
50514779705SSam Leffler 			if (!IS_BIT_SET(b, channelBM))
50614779705SSam Leffler 				continue;
50714779705SSam Leffler 			fband = &freqs[b];
50814779705SSam Leffler 			lastc = 0;
50914779705SSam Leffler 
51014779705SSam Leffler 			for (c = fband->lowChannel + low_adj;
51114779705SSam Leffler 			     c <= fband->highChannel + hi_adj;
51214779705SSam Leffler 			     c += fband->channelSep) {
51314779705SSam Leffler 				if (!(c_lo <= c && c <= c_hi)) {
51414779705SSam Leffler 					HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
51514779705SSam Leffler 					    "%s: c %u out of range [%u..%u]\n",
51614779705SSam Leffler 					    __func__, c, c_lo, c_hi);
51714779705SSam Leffler 					continue;
51814779705SSam Leffler 				}
51914779705SSam Leffler 				if (next >= maxchans){
52014779705SSam Leffler 					HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
52114779705SSam Leffler 					    "%s: too many channels for channel table\n",
52214779705SSam Leffler 					    __func__);
52314779705SSam Leffler 					goto done;
52414779705SSam Leffler 				}
52514779705SSam Leffler 				if ((fband->usePassScan & IS_ECM_CHAN) &&
52614779705SSam Leffler 				    !enableExtendedChannels) {
52714779705SSam Leffler 					HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
52859efa8b5SSam Leffler 					    "skip ecm channel\n");
52914779705SSam Leffler 					continue;
53014779705SSam Leffler 				}
531e8e60359SAdrian Chadd #if 0
53259efa8b5SSam Leffler 				if ((fband->useDfs & dfsMask) &&
53359efa8b5SSam Leffler 				    (cm->flags & IEEE80211_CHAN_HT40)) {
53459efa8b5SSam Leffler 					/* NB: DFS and HT40 don't mix */
53514779705SSam Leffler 					HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
53659efa8b5SSam Leffler 					    "skip HT40 chan, DFS required\n");
53714779705SSam Leffler 					continue;
53814779705SSam Leffler 				}
539e8e60359SAdrian Chadd #endif
54014779705SSam Leffler 				/*
54114779705SSam Leffler 				 * Make sure that channel separation
54214779705SSam Leffler 				 * meets the requirement.
54314779705SSam Leffler 				 */
54414779705SSam Leffler 				if (lastc && channelSep &&
54514779705SSam Leffler 				    (c-lastc) < channelSep)
54614779705SSam Leffler 					continue;
54714779705SSam Leffler 				lastc = c;
54814779705SSam Leffler 
54959efa8b5SSam Leffler 				OS_MEMZERO(ic, sizeof(*ic));
55059efa8b5SSam Leffler 				ic->ic_freq = c;
55159efa8b5SSam Leffler 				ic->ic_flags = cm->flags;
55259efa8b5SSam Leffler 				ic->ic_maxregpower = fband->powerDfs;
55359efa8b5SSam Leffler 				ath_hal_getpowerlimits(ah, ic);
55459efa8b5SSam Leffler 				ic->ic_maxantgain = fband->antennaMax;
55559efa8b5SSam Leffler 				if (fband->usePassScan & pscan)
55659efa8b5SSam Leffler 					ic->ic_flags |= IEEE80211_CHAN_PASSIVE;
55759efa8b5SSam Leffler 				if (fband->useDfs & dfsMask)
55859efa8b5SSam Leffler 					ic->ic_flags |= IEEE80211_CHAN_DFS;
55959efa8b5SSam Leffler 				if (IEEE80211_IS_CHAN_5GHZ(ic) &&
56059efa8b5SSam Leffler 				    (rdflags & DISALLOW_ADHOC_11A))
56159efa8b5SSam Leffler 					ic->ic_flags |= IEEE80211_CHAN_NOADHOC;
56259efa8b5SSam Leffler 				if (IEEE80211_IS_CHAN_TURBO(ic) &&
56359efa8b5SSam Leffler 				    (rdflags & DISALLOW_ADHOC_11A_TURB))
56459efa8b5SSam Leffler 					ic->ic_flags |= IEEE80211_CHAN_NOADHOC;
56559efa8b5SSam Leffler 				if (rdflags & NO_HOSTAP)
56659efa8b5SSam Leffler 					ic->ic_flags |= IEEE80211_CHAN_NOHOSTAP;
56759efa8b5SSam Leffler 				if (rdflags & LIMIT_FRAME_4MS)
56859efa8b5SSam Leffler 					ic->ic_flags |= IEEE80211_CHAN_4MSXMIT;
56959efa8b5SSam Leffler 				if (rdflags & NEED_NFC)
57059efa8b5SSam Leffler 					ic->ic_flags |= CHANNEL_NFCREQUIRED;
57159efa8b5SSam Leffler 
57259efa8b5SSam Leffler 				ic++, next++;
57314779705SSam Leffler 			}
57414779705SSam Leffler 		}
57514779705SSam Leffler 	}
57614779705SSam Leffler done:
57714779705SSam Leffler 	*nchans = next;
57859efa8b5SSam Leffler 	/* NB: pcountry set above by getregstate */
57959efa8b5SSam Leffler 	if (prd2GHz != AH_NULL)
58059efa8b5SSam Leffler 		*prd2GHz = rd2GHz;
58159efa8b5SSam Leffler 	if (prd5GHz != AH_NULL)
58259efa8b5SSam Leffler 		*prd5GHz = rd5GHz;
58359efa8b5SSam Leffler 	return HAL_OK;
58459efa8b5SSam Leffler #undef HAL_MODE_11A_ALL
58514779705SSam Leffler #undef CHANNEL_HALF_BW
58614779705SSam Leffler #undef CHANNEL_QUARTER_BW
58714779705SSam Leffler }
58814779705SSam Leffler 
58914779705SSam Leffler /*
59059efa8b5SSam Leffler  * Retrieve a channel list without affecting runtime state.
59114779705SSam Leffler  */
59259efa8b5SSam Leffler HAL_STATUS
59359efa8b5SSam Leffler ath_hal_getchannels(struct ath_hal *ah,
59459efa8b5SSam Leffler     struct ieee80211_channel chans[], u_int maxchans, int *nchans,
59559efa8b5SSam Leffler     u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
59659efa8b5SSam Leffler     HAL_BOOL enableExtendedChannels)
59714779705SSam Leffler {
59859efa8b5SSam Leffler 	return getchannels(ah, chans, maxchans, nchans, modeSelect,
59959efa8b5SSam Leffler 	    cc, regDmn, enableExtendedChannels, AH_NULL, AH_NULL, AH_NULL);
60059efa8b5SSam Leffler }
60114779705SSam Leffler 
60214779705SSam Leffler /*
60359efa8b5SSam Leffler  * Handle frequency mapping from 900Mhz range to 2.4GHz range
60459efa8b5SSam Leffler  * for GSM radios.  This is done when we need the h/w frequency
60559efa8b5SSam Leffler  * and the channel is marked IEEE80211_CHAN_GSM.
60614779705SSam Leffler  */
60759efa8b5SSam Leffler static int
60859efa8b5SSam Leffler ath_hal_mapgsm(int sku, int freq)
60959efa8b5SSam Leffler {
61059efa8b5SSam Leffler 	if (sku == SKU_XR9)
61159efa8b5SSam Leffler 		return 1520 + freq;
61259efa8b5SSam Leffler 	if (sku == SKU_GZ901)
61359efa8b5SSam Leffler 		return 1544 + freq;
61459efa8b5SSam Leffler 	if (sku == SKU_SR9)
61559efa8b5SSam Leffler 		return 3344 - freq;
616f6f1dfb6SAdrian Chadd 	HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY,
61759efa8b5SSam Leffler 	    "%s: cannot map freq %u unknown gsm sku %u\n",
61859efa8b5SSam Leffler 	    __func__, freq, sku);
61959efa8b5SSam Leffler 	return freq;
62014779705SSam Leffler }
62114779705SSam Leffler 
62259efa8b5SSam Leffler /*
62359efa8b5SSam Leffler  * Setup the internal/private channel state given a table of
62459efa8b5SSam Leffler  * net80211 channels.  We collapse entries for the same frequency
62559efa8b5SSam Leffler  * and record the frequency for doing noise floor processing
62659efa8b5SSam Leffler  * where we don't have net80211 channel context.
62759efa8b5SSam Leffler  */
62859efa8b5SSam Leffler static HAL_BOOL
62959efa8b5SSam Leffler assignPrivateChannels(struct ath_hal *ah,
63059efa8b5SSam Leffler 	struct ieee80211_channel chans[], int nchans, int sku)
63159efa8b5SSam Leffler {
63259efa8b5SSam Leffler 	HAL_CHANNEL_INTERNAL *ic;
63359efa8b5SSam Leffler 	int i, j, next, freq;
63459efa8b5SSam Leffler 
63559efa8b5SSam Leffler 	next = 0;
63659efa8b5SSam Leffler 	for (i = 0; i < nchans; i++) {
63759efa8b5SSam Leffler 		struct ieee80211_channel *c = &chans[i];
63859efa8b5SSam Leffler 		for (j = i-1; j >= 0; j--)
63959efa8b5SSam Leffler 			if (chans[j].ic_freq == c->ic_freq) {
64059efa8b5SSam Leffler 				c->ic_devdata = chans[j].ic_devdata;
64159efa8b5SSam Leffler 				break;
64259efa8b5SSam Leffler 			}
64359efa8b5SSam Leffler 		if (j < 0) {
64459efa8b5SSam Leffler 			/* new entry, assign a private channel entry */
64559efa8b5SSam Leffler 			if (next >= N(AH_PRIVATE(ah)->ah_channels)) {
64659efa8b5SSam Leffler 				HALDEBUG(ah, HAL_DEBUG_ANY,
64750d5ad0eSSam Leffler 				    "%s: too many channels, max %zu\n",
64859efa8b5SSam Leffler 				    __func__, N(AH_PRIVATE(ah)->ah_channels));
64959efa8b5SSam Leffler 				return AH_FALSE;
65059efa8b5SSam Leffler 			}
65159efa8b5SSam Leffler 			/*
65259efa8b5SSam Leffler 			 * Handle frequency mapping for 900MHz devices.
65359efa8b5SSam Leffler 			 * The hardware uses 2.4GHz frequencies that are
65459efa8b5SSam Leffler 			 * down-converted.  The 802.11 layer uses the
65559efa8b5SSam Leffler 			 * true frequencies.
65659efa8b5SSam Leffler 			 */
65759efa8b5SSam Leffler 			freq = IEEE80211_IS_CHAN_GSM(c) ?
65859efa8b5SSam Leffler 			    ath_hal_mapgsm(sku, c->ic_freq) : c->ic_freq;
65959efa8b5SSam Leffler 
66059efa8b5SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
66159efa8b5SSam Leffler 			    "%s: private[%3u] %u/0x%x -> channel %u\n",
66259efa8b5SSam Leffler 			    __func__, next, c->ic_freq, c->ic_flags, freq);
66359efa8b5SSam Leffler 
66459efa8b5SSam Leffler 			ic = &AH_PRIVATE(ah)->ah_channels[next];
66559efa8b5SSam Leffler 			/*
66659efa8b5SSam Leffler 			 * NB: This clears privFlags which means ancillary
66759efa8b5SSam Leffler 			 *     code like ANI and IQ calibration will be
66859efa8b5SSam Leffler 			 *     restarted and re-setup any per-channel state.
66959efa8b5SSam Leffler 			 */
67059efa8b5SSam Leffler 			OS_MEMZERO(ic, sizeof(*ic));
67159efa8b5SSam Leffler 			ic->channel = freq;
67259efa8b5SSam Leffler 			c->ic_devdata = next;
67359efa8b5SSam Leffler 			next++;
67459efa8b5SSam Leffler 		}
67559efa8b5SSam Leffler 	}
67659efa8b5SSam Leffler 	AH_PRIVATE(ah)->ah_nchan = next;
67759efa8b5SSam Leffler 	HALDEBUG(ah, HAL_DEBUG_ANY, "%s: %u public, %u private channels\n",
67859efa8b5SSam Leffler 	    __func__, nchans, next);
67959efa8b5SSam Leffler 	return AH_TRUE;
68059efa8b5SSam Leffler }
68159efa8b5SSam Leffler 
68259efa8b5SSam Leffler /*
68359efa8b5SSam Leffler  * Setup the channel list based on the information in the EEPROM.
68459efa8b5SSam Leffler  */
68559efa8b5SSam Leffler HAL_STATUS
68659efa8b5SSam Leffler ath_hal_init_channels(struct ath_hal *ah,
68759efa8b5SSam Leffler     struct ieee80211_channel chans[], u_int maxchans, int *nchans,
68859efa8b5SSam Leffler     u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
68959efa8b5SSam Leffler     HAL_BOOL enableExtendedChannels)
69059efa8b5SSam Leffler {
69159efa8b5SSam Leffler 	COUNTRY_CODE_TO_ENUM_RD *country;
692*7dd4de13SAdrian Chadd 	REG_DOMAIN *rd5GHz, *rd2GHz;
69359efa8b5SSam Leffler 	HAL_STATUS status;
69459efa8b5SSam Leffler 
69559efa8b5SSam Leffler 	status = getchannels(ah, chans, maxchans, nchans, modeSelect,
69659efa8b5SSam Leffler 	    cc, regDmn, enableExtendedChannels, &country, &rd2GHz, &rd5GHz);
69759efa8b5SSam Leffler 	if (status == HAL_OK &&
69859efa8b5SSam Leffler 	    assignPrivateChannels(ah, chans, *nchans, AH_PRIVATE(ah)->ah_currentRD)) {
69959efa8b5SSam Leffler 		AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz;
70059efa8b5SSam Leffler 		AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz;
70159efa8b5SSam Leffler 
70259efa8b5SSam Leffler 		ah->ah_countryCode = country->countryCode;
70359efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n",
70459efa8b5SSam Leffler 		    __func__, ah->ah_countryCode);
705*7dd4de13SAdrian Chadd 
706*7dd4de13SAdrian Chadd 		/* Update current DFS domain */
707*7dd4de13SAdrian Chadd 		ath_hal_update_dfsdomain(ah);
70859efa8b5SSam Leffler 	} else
70959efa8b5SSam Leffler 		status = HAL_EINVAL;
7108db87e40SAdrian Chadd 
71159efa8b5SSam Leffler 	return status;
71259efa8b5SSam Leffler }
71359efa8b5SSam Leffler 
71459efa8b5SSam Leffler /*
71559efa8b5SSam Leffler  * Set the channel list.
71659efa8b5SSam Leffler  */
71759efa8b5SSam Leffler HAL_STATUS
71859efa8b5SSam Leffler ath_hal_set_channels(struct ath_hal *ah,
71959efa8b5SSam Leffler     struct ieee80211_channel chans[], int nchans,
72059efa8b5SSam Leffler     HAL_CTRY_CODE cc, HAL_REG_DOMAIN rd)
72159efa8b5SSam Leffler {
72259efa8b5SSam Leffler 	COUNTRY_CODE_TO_ENUM_RD *country;
72359efa8b5SSam Leffler 	REG_DOMAIN *rd5GHz, *rd2GHz;
72459efa8b5SSam Leffler 	HAL_STATUS status;
72559efa8b5SSam Leffler 
72659efa8b5SSam Leffler 	switch (rd) {
72759efa8b5SSam Leffler 	case SKU_SR9:
72859efa8b5SSam Leffler 	case SKU_XR9:
72959efa8b5SSam Leffler 	case SKU_GZ901:
73059efa8b5SSam Leffler 		/*
73159efa8b5SSam Leffler 		 * Map 900MHz sku's.  The frequencies will be mapped
73259efa8b5SSam Leffler 		 * according to the sku to compensate for the down-converter.
73359efa8b5SSam Leffler 		 * We use the FCC for these sku's as the mapped channel
73459efa8b5SSam Leffler 		 * list is known compatible (will need to change if/when
73559efa8b5SSam Leffler 		 * vendors do different mapping in different locales).
73659efa8b5SSam Leffler 		 */
73759efa8b5SSam Leffler 		status = getregstate(ah, CTRY_DEFAULT, SKU_FCC,
73859efa8b5SSam Leffler 		    &country, &rd2GHz, &rd5GHz);
73959efa8b5SSam Leffler 		break;
74059efa8b5SSam Leffler 	default:
74159efa8b5SSam Leffler 		status = getregstate(ah, cc, rd,
74259efa8b5SSam Leffler 		    &country, &rd2GHz, &rd5GHz);
74359efa8b5SSam Leffler 		rd = AH_PRIVATE(ah)->ah_currentRD;
74459efa8b5SSam Leffler 		break;
74559efa8b5SSam Leffler 	}
74659efa8b5SSam Leffler 	if (status == HAL_OK && assignPrivateChannels(ah, chans, nchans, rd)) {
74759efa8b5SSam Leffler 		AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz;
74859efa8b5SSam Leffler 		AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz;
74959efa8b5SSam Leffler 
75059efa8b5SSam Leffler 		ah->ah_countryCode = country->countryCode;
75159efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n",
75259efa8b5SSam Leffler 		    __func__, ah->ah_countryCode);
75359efa8b5SSam Leffler 	} else
75459efa8b5SSam Leffler 		status = HAL_EINVAL;
755*7dd4de13SAdrian Chadd 
756*7dd4de13SAdrian Chadd 	if (status == HAL_OK) {
757*7dd4de13SAdrian Chadd 		/* Update current DFS domain */
758*7dd4de13SAdrian Chadd 		(void) ath_hal_update_dfsdomain(ah);
759*7dd4de13SAdrian Chadd 	}
76059efa8b5SSam Leffler 	return status;
76159efa8b5SSam Leffler }
76259efa8b5SSam Leffler 
76359efa8b5SSam Leffler #ifdef AH_DEBUG
76459efa8b5SSam Leffler /*
76559efa8b5SSam Leffler  * Return the internal channel corresponding to a public channel.
76659efa8b5SSam Leffler  * NB: normally this routine is inline'd (see ah_internal.h)
76759efa8b5SSam Leffler  */
76859efa8b5SSam Leffler HAL_CHANNEL_INTERNAL *
76959efa8b5SSam Leffler ath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c)
77059efa8b5SSam Leffler {
77159efa8b5SSam Leffler 	HAL_CHANNEL_INTERNAL *cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata];
77259efa8b5SSam Leffler 
77359efa8b5SSam Leffler 	if (c->ic_devdata < AH_PRIVATE(ah)->ah_nchan &&
77459efa8b5SSam Leffler 	    (c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)))
77514779705SSam Leffler 		return cc;
77659efa8b5SSam Leffler 	if (c->ic_devdata >= AH_PRIVATE(ah)->ah_nchan) {
77759efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_ANY,
77859efa8b5SSam Leffler 		    "%s: bad mapping, devdata %u nchans %u\n",
77959efa8b5SSam Leffler 		   __func__, c->ic_devdata, AH_PRIVATE(ah)->ah_nchan);
78059efa8b5SSam Leffler 		HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan);
78159efa8b5SSam Leffler 	} else {
78259efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_ANY,
78359efa8b5SSam Leffler 		    "%s: no match for %u/0x%x devdata %u channel %u\n",
78459efa8b5SSam Leffler 		   __func__, c->ic_freq, c->ic_flags, c->ic_devdata,
78559efa8b5SSam Leffler 		   cc->channel);
78659efa8b5SSam Leffler 		HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c));
78714779705SSam Leffler 	}
78814779705SSam Leffler 	return AH_NULL;
78959efa8b5SSam Leffler }
79059efa8b5SSam Leffler #endif /* AH_DEBUG */
79159efa8b5SSam Leffler 
79259efa8b5SSam Leffler #define isWwrSKU(_ah) \
79359efa8b5SSam Leffler 	((getEepromRD((_ah)) & WORLD_SKU_MASK) == WORLD_SKU_PREFIX || \
79459efa8b5SSam Leffler 	  getEepromRD(_ah) == WORLD)
79559efa8b5SSam Leffler 
79659efa8b5SSam Leffler /*
79759efa8b5SSam Leffler  * Return the test group for the specific channel based on
79859efa8b5SSam Leffler  * the current regulatory setup.
79959efa8b5SSam Leffler  */
80059efa8b5SSam Leffler u_int
80159efa8b5SSam Leffler ath_hal_getctl(struct ath_hal *ah, const struct ieee80211_channel *c)
80259efa8b5SSam Leffler {
80359efa8b5SSam Leffler 	u_int ctl;
80459efa8b5SSam Leffler 
80559efa8b5SSam Leffler 	if (AH_PRIVATE(ah)->ah_rd2GHz == AH_PRIVATE(ah)->ah_rd5GHz ||
80659efa8b5SSam Leffler 	    (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)))
80759efa8b5SSam Leffler 		ctl = SD_NO_CTL;
80859efa8b5SSam Leffler 	else if (IEEE80211_IS_CHAN_2GHZ(c))
80959efa8b5SSam Leffler 		ctl = AH_PRIVATE(ah)->ah_rd2GHz->conformanceTestLimit;
81059efa8b5SSam Leffler 	else
81159efa8b5SSam Leffler 		ctl = AH_PRIVATE(ah)->ah_rd5GHz->conformanceTestLimit;
81259efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_B(c))
81359efa8b5SSam Leffler 		return ctl | CTL_11B;
81459efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_G(c))
81559efa8b5SSam Leffler 		return ctl | CTL_11G;
81659efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_108G(c))
81759efa8b5SSam Leffler 		return ctl | CTL_108G;
81859efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_TURBO(c))
81959efa8b5SSam Leffler 		return ctl | CTL_TURBO;
82059efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_A(c))
82159efa8b5SSam Leffler 		return ctl | CTL_11A;
82259efa8b5SSam Leffler 	return ctl;
82314779705SSam Leffler }
82414779705SSam Leffler 
825*7dd4de13SAdrian Chadd 
826*7dd4de13SAdrian Chadd /*
827*7dd4de13SAdrian Chadd  * Update the current dfsDomain setting based on the given
828*7dd4de13SAdrian Chadd  * country code.
829*7dd4de13SAdrian Chadd  *
830*7dd4de13SAdrian Chadd  * Since FreeBSD/net80211 allows the channel set to change
831*7dd4de13SAdrian Chadd  * after the card has been setup (via ath_hal_init_channels())
832*7dd4de13SAdrian Chadd  * this function method is needed to update ah_dfsDomain.
833*7dd4de13SAdrian Chadd  */
834*7dd4de13SAdrian Chadd void
835*7dd4de13SAdrian Chadd ath_hal_update_dfsdomain(struct ath_hal *ah)
836*7dd4de13SAdrian Chadd {
837*7dd4de13SAdrian Chadd 	const REG_DOMAIN *rd5GHz = AH_PRIVATE(ah)->ah_rd5GHz;
838*7dd4de13SAdrian Chadd 	HAL_CTRY_CODE cc = ah->ah_countryCode;
839*7dd4de13SAdrian Chadd 	HAL_DFS_DOMAIN dfsDomain = HAL_DFS_UNINIT_DOMAIN;
840*7dd4de13SAdrian Chadd 	HAL_REG_DOMAIN regDmn = AH_PRIVATE(ah)->ah_currentRD;
841*7dd4de13SAdrian Chadd 
842*7dd4de13SAdrian Chadd 	HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s CC: %d, RegDmn: %d\n",__func__,
843*7dd4de13SAdrian Chadd 	    cc, regDmn);
844*7dd4de13SAdrian Chadd 	if (rd5GHz->dfsMask & DFS_FCC3)
845*7dd4de13SAdrian Chadd 		dfsDomain = HAL_DFS_FCC_DOMAIN;
846*7dd4de13SAdrian Chadd 	if (rd5GHz->dfsMask & DFS_ETSI)
847*7dd4de13SAdrian Chadd 		dfsDomain = HAL_DFS_ETSI_DOMAIN;
848*7dd4de13SAdrian Chadd 	if (rd5GHz->dfsMask & DFS_MKK4)
849*7dd4de13SAdrian Chadd 		dfsDomain = HAL_DFS_MKK4_DOMAIN;
850*7dd4de13SAdrian Chadd 	AH_PRIVATE(ah)->ah_dfsDomain = dfsDomain;
851*7dd4de13SAdrian Chadd 	HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s ah_dfsDomain: %d\n",
852*7dd4de13SAdrian Chadd 	    __func__, AH_PRIVATE(ah)->ah_dfsDomain);
853*7dd4de13SAdrian Chadd }
854*7dd4de13SAdrian Chadd 
855*7dd4de13SAdrian Chadd 
85614779705SSam Leffler /*
85714779705SSam Leffler  * Return the max allowed antenna gain and apply any regulatory
85814779705SSam Leffler  * domain specific changes.
85914779705SSam Leffler  *
86014779705SSam Leffler  * NOTE: a negative reduction is possible in RD's that only
86114779705SSam Leffler  * measure radiated power (e.g., ETSI) which would increase
86214779705SSam Leffler  * that actual conducted output power (though never beyond
86314779705SSam Leffler  * the calibrated target power).
86414779705SSam Leffler  */
86514779705SSam Leffler u_int
86659efa8b5SSam Leffler ath_hal_getantennareduction(struct ath_hal *ah,
86759efa8b5SSam Leffler     const struct ieee80211_channel *chan, u_int twiceGain)
86814779705SSam Leffler {
86959efa8b5SSam Leffler 	int8_t antennaMax = twiceGain - chan->ic_maxantgain*2;
87014779705SSam Leffler 	return (antennaMax < 0) ? 0 : antennaMax;
87114779705SSam Leffler }
872