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... */ 3812d53238SAndriy 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[] = { 10212d53238SAndriy Voskoboinyk { HAL_MODE_TURBO, IEEE80211_CHAN_ST, ®Dmn5GhzTurboFreq[0] }, 10312d53238SAndriy Voskoboinyk { HAL_MODE_11A, IEEE80211_CHAN_A, ®Dmn5GhzFreq[0] }, 10412d53238SAndriy Voskoboinyk { HAL_MODE_11B, IEEE80211_CHAN_B, ®Dmn2GhzFreq[0] }, 10512d53238SAndriy Voskoboinyk { HAL_MODE_11G, IEEE80211_CHAN_G, ®Dmn2Ghz11gFreq[0] }, 10612d53238SAndriy Voskoboinyk { HAL_MODE_11G_TURBO, IEEE80211_CHAN_108G, ®Dmn2Ghz11gTurboFreq[0] }, 10712d53238SAndriy Voskoboinyk { HAL_MODE_11A_TURBO, IEEE80211_CHAN_108A, ®Dmn5GhzTurboFreq[0] }, 10859efa8b5SSam Leffler { HAL_MODE_11A_QUARTER_RATE, 10912d53238SAndriy Voskoboinyk IEEE80211_CHAN_A | IEEE80211_CHAN_QUARTER, ®Dmn5GhzFreq[0] }, 11059efa8b5SSam Leffler { HAL_MODE_11A_HALF_RATE, 11112d53238SAndriy Voskoboinyk IEEE80211_CHAN_A | IEEE80211_CHAN_HALF, ®Dmn5GhzFreq[0] }, 11259efa8b5SSam Leffler { HAL_MODE_11G_QUARTER_RATE, 11312d53238SAndriy Voskoboinyk IEEE80211_CHAN_G | IEEE80211_CHAN_QUARTER, ®Dmn2Ghz11gFreq[0] }, 11459efa8b5SSam Leffler { HAL_MODE_11G_HALF_RATE, 11512d53238SAndriy Voskoboinyk IEEE80211_CHAN_G | IEEE80211_CHAN_HALF, ®Dmn2Ghz11gFreq[0] }, 11612d53238SAndriy Voskoboinyk { HAL_MODE_11NG_HT20, 11712d53238SAndriy Voskoboinyk IEEE80211_CHAN_G | IEEE80211_CHAN_HT20, ®Dmn2Ghz11gFreq[0] }, 11859efa8b5SSam Leffler { HAL_MODE_11NG_HT40PLUS, 11912d53238SAndriy Voskoboinyk IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U, ®Dmn2Ghz11gFreq[0] }, 12059efa8b5SSam Leffler { HAL_MODE_11NG_HT40MINUS, 12112d53238SAndriy Voskoboinyk IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D, ®Dmn2Ghz11gFreq[0] }, 12212d53238SAndriy Voskoboinyk { HAL_MODE_11NA_HT20, 12312d53238SAndriy Voskoboinyk IEEE80211_CHAN_A | IEEE80211_CHAN_HT20, ®Dmn5GhzFreq[0] }, 12459efa8b5SSam Leffler { HAL_MODE_11NA_HT40PLUS, 12512d53238SAndriy Voskoboinyk IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U, ®Dmn5GhzFreq[0] }, 12659efa8b5SSam Leffler { HAL_MODE_11NA_HT40MINUS, 12712d53238SAndriy Voskoboinyk IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D, ®Dmn5GhzFreq[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 ®Domains[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 ®DomainPairs[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 36312d53238SAndriy Voskoboinyk static uint64_t * 36412d53238SAndriy Voskoboinyk getchannelBM(u_int mode, REG_DOMAIN *rd) 36512d53238SAndriy Voskoboinyk { 36612d53238SAndriy Voskoboinyk switch (mode) { 36712d53238SAndriy Voskoboinyk case HAL_MODE_11B: 36812d53238SAndriy Voskoboinyk return (rd->chan11b); 36912d53238SAndriy Voskoboinyk case HAL_MODE_11G_QUARTER_RATE: 37012d53238SAndriy Voskoboinyk return (rd->chan11g_quarter); 37112d53238SAndriy Voskoboinyk case HAL_MODE_11G_HALF_RATE: 37212d53238SAndriy Voskoboinyk return (rd->chan11g_half); 37312d53238SAndriy Voskoboinyk case HAL_MODE_11G: 37412d53238SAndriy Voskoboinyk case HAL_MODE_11NG_HT20: 37512d53238SAndriy Voskoboinyk case HAL_MODE_11NG_HT40PLUS: 37612d53238SAndriy Voskoboinyk case HAL_MODE_11NG_HT40MINUS: 37712d53238SAndriy Voskoboinyk return (rd->chan11g); 37812d53238SAndriy Voskoboinyk case HAL_MODE_11G_TURBO: 37912d53238SAndriy Voskoboinyk return (rd->chan11g_turbo); 38012d53238SAndriy Voskoboinyk case HAL_MODE_11A_QUARTER_RATE: 38112d53238SAndriy Voskoboinyk return (rd->chan11a_quarter); 38212d53238SAndriy Voskoboinyk case HAL_MODE_11A_HALF_RATE: 38312d53238SAndriy Voskoboinyk return (rd->chan11a_half); 38412d53238SAndriy Voskoboinyk case HAL_MODE_11A: 38512d53238SAndriy Voskoboinyk case HAL_MODE_11NA_HT20: 38612d53238SAndriy Voskoboinyk case HAL_MODE_11NA_HT40PLUS: 38712d53238SAndriy Voskoboinyk case HAL_MODE_11NA_HT40MINUS: 38812d53238SAndriy Voskoboinyk return (rd->chan11a); 38912d53238SAndriy Voskoboinyk case HAL_MODE_TURBO: 39012d53238SAndriy Voskoboinyk return (rd->chan11a_turbo); 39112d53238SAndriy Voskoboinyk case HAL_MODE_11A_TURBO: 39212d53238SAndriy Voskoboinyk return (rd->chan11a_dyn_turbo); 39312d53238SAndriy Voskoboinyk default: 39412d53238SAndriy Voskoboinyk return (AH_NULL); 39512d53238SAndriy Voskoboinyk } 39612d53238SAndriy Voskoboinyk } 39712d53238SAndriy Voskoboinyk 39812d53238SAndriy Voskoboinyk static void 39912d53238SAndriy Voskoboinyk setchannelflags(struct ieee80211_channel *c, REG_DMN_FREQ_BAND *fband, 40012d53238SAndriy Voskoboinyk REG_DOMAIN *rd) 40112d53238SAndriy Voskoboinyk { 40212d53238SAndriy Voskoboinyk if (fband->usePassScan & rd->pscan) 40312d53238SAndriy Voskoboinyk c->ic_flags |= IEEE80211_CHAN_PASSIVE; 40412d53238SAndriy Voskoboinyk if (fband->useDfs & rd->dfsMask) 40512d53238SAndriy Voskoboinyk c->ic_flags |= IEEE80211_CHAN_DFS; 40612d53238SAndriy Voskoboinyk if (IEEE80211_IS_CHAN_5GHZ(c) && (rd->flags & DISALLOW_ADHOC_11A)) 40712d53238SAndriy Voskoboinyk c->ic_flags |= IEEE80211_CHAN_NOADHOC; 40812d53238SAndriy Voskoboinyk if (IEEE80211_IS_CHAN_TURBO(c) && 40912d53238SAndriy Voskoboinyk (rd->flags & DISALLOW_ADHOC_11A_TURB)) 41012d53238SAndriy Voskoboinyk c->ic_flags |= IEEE80211_CHAN_NOADHOC; 41112d53238SAndriy Voskoboinyk if (rd->flags & NO_HOSTAP) 41212d53238SAndriy Voskoboinyk c->ic_flags |= IEEE80211_CHAN_NOHOSTAP; 41312d53238SAndriy Voskoboinyk if (rd->flags & LIMIT_FRAME_4MS) 41412d53238SAndriy Voskoboinyk c->ic_flags |= IEEE80211_CHAN_4MSXMIT; 41512d53238SAndriy Voskoboinyk if (rd->flags & NEED_NFC) 41612d53238SAndriy Voskoboinyk c->ic_flags |= CHANNEL_NFCREQUIRED; 41712d53238SAndriy Voskoboinyk } 41812d53238SAndriy Voskoboinyk 41912d53238SAndriy Voskoboinyk static int 42012d53238SAndriy Voskoboinyk addchan(struct ath_hal *ah, struct ieee80211_channel chans[], 42112d53238SAndriy Voskoboinyk u_int maxchans, int *nchans, uint16_t freq, uint32_t flags, 42212d53238SAndriy Voskoboinyk REG_DMN_FREQ_BAND *fband, REG_DOMAIN *rd) 42312d53238SAndriy Voskoboinyk { 42412d53238SAndriy Voskoboinyk struct ieee80211_channel *c; 42512d53238SAndriy Voskoboinyk 42612d53238SAndriy Voskoboinyk if (*nchans >= maxchans) 427b1940debSAdrian Chadd return (HAL_ENOMEM); 42812d53238SAndriy Voskoboinyk 429*eee8e362SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 430*eee8e362SAdrian Chadd "%s: %d: freq=%d, flags=0x%08x\n", 431*eee8e362SAdrian Chadd __func__, *nchans, (int) freq, flags); 432*eee8e362SAdrian Chadd 43312d53238SAndriy Voskoboinyk c = &chans[(*nchans)++]; 43412d53238SAndriy Voskoboinyk c->ic_freq = freq; 43512d53238SAndriy Voskoboinyk c->ic_flags = flags; 43612d53238SAndriy Voskoboinyk setchannelflags(c, fband, rd); 43712d53238SAndriy Voskoboinyk c->ic_maxregpower = fband->powerDfs; 43812d53238SAndriy Voskoboinyk ath_hal_getpowerlimits(ah, c); 43912d53238SAndriy Voskoboinyk c->ic_maxantgain = fband->antennaMax; 44012d53238SAndriy Voskoboinyk 44112d53238SAndriy Voskoboinyk return (0); 44212d53238SAndriy Voskoboinyk } 44312d53238SAndriy Voskoboinyk 44412d53238SAndriy Voskoboinyk static int 44512d53238SAndriy Voskoboinyk copychan_prev(struct ath_hal *ah, struct ieee80211_channel chans[], 446*eee8e362SAdrian Chadd u_int maxchans, int *nchans, uint16_t freq, uint32_t flags) 44712d53238SAndriy Voskoboinyk { 44812d53238SAndriy Voskoboinyk struct ieee80211_channel *c; 44912d53238SAndriy Voskoboinyk 450b1940debSAdrian Chadd if (*nchans == 0) 451b1940debSAdrian Chadd return (HAL_EINVAL); 45212d53238SAndriy Voskoboinyk 45312d53238SAndriy Voskoboinyk if (*nchans >= maxchans) 454b1940debSAdrian Chadd return (HAL_ENOMEM); 45512d53238SAndriy Voskoboinyk 456*eee8e362SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 457*eee8e362SAdrian Chadd "%s: %d: freq=%d, flags=0x%08x\n", 458*eee8e362SAdrian Chadd __func__, *nchans, (int) freq, flags); 459*eee8e362SAdrian Chadd 46012d53238SAndriy Voskoboinyk c = &chans[(*nchans)++]; 46112d53238SAndriy Voskoboinyk c[0] = c[-1]; 46212d53238SAndriy Voskoboinyk c->ic_freq = freq; 46312d53238SAndriy Voskoboinyk /* XXX is it needed here? */ 46412d53238SAndriy Voskoboinyk ath_hal_getpowerlimits(ah, c); 46512d53238SAndriy Voskoboinyk 46612d53238SAndriy Voskoboinyk return (0); 46712d53238SAndriy Voskoboinyk } 46812d53238SAndriy Voskoboinyk 46912d53238SAndriy Voskoboinyk static int 47012d53238SAndriy Voskoboinyk add_chanlist_band(struct ath_hal *ah, struct ieee80211_channel chans[], 47112d53238SAndriy Voskoboinyk int maxchans, int *nchans, uint16_t freq_lo, uint16_t freq_hi, int step, 47212d53238SAndriy Voskoboinyk uint32_t flags, REG_DMN_FREQ_BAND *fband, REG_DOMAIN *rd) 47312d53238SAndriy Voskoboinyk { 47412d53238SAndriy Voskoboinyk uint16_t freq = freq_lo; 47512d53238SAndriy Voskoboinyk int error; 47612d53238SAndriy Voskoboinyk 47712d53238SAndriy Voskoboinyk if (freq_hi < freq_lo) 47812d53238SAndriy Voskoboinyk return (0); 47912d53238SAndriy Voskoboinyk 480*eee8e362SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 481*eee8e362SAdrian Chadd "%s: freq=%d..%d, flags=0x%08x, step=%d\n", __func__, 482*eee8e362SAdrian Chadd (int) freq_lo, (int) freq_hi, flags, step); 483*eee8e362SAdrian Chadd 48412d53238SAndriy Voskoboinyk error = addchan(ah, chans, maxchans, nchans, freq, flags, fband, rd); 48512d53238SAndriy Voskoboinyk for (freq += step; freq <= freq_hi && error == 0; freq += step) 486*eee8e362SAdrian Chadd error = copychan_prev(ah, chans, maxchans, nchans, freq, flags); 48712d53238SAndriy Voskoboinyk 48812d53238SAndriy Voskoboinyk return (error); 48912d53238SAndriy Voskoboinyk } 49012d53238SAndriy Voskoboinyk 49112d53238SAndriy Voskoboinyk static void 49212d53238SAndriy Voskoboinyk adj_freq_ht40(u_int mode, int *low_adj, int *hi_adj, int *channelSep) 49312d53238SAndriy Voskoboinyk { 49412d53238SAndriy Voskoboinyk 49512d53238SAndriy Voskoboinyk *low_adj = *hi_adj = *channelSep = 0; 49612d53238SAndriy Voskoboinyk switch (mode) { 49712d53238SAndriy Voskoboinyk case HAL_MODE_11NA_HT40PLUS: 49812d53238SAndriy Voskoboinyk *channelSep = 40; 49912d53238SAndriy Voskoboinyk /* FALLTHROUGH */ 50012d53238SAndriy Voskoboinyk case HAL_MODE_11NG_HT40PLUS: 50112d53238SAndriy Voskoboinyk *hi_adj = -20; 50212d53238SAndriy Voskoboinyk break; 50312d53238SAndriy Voskoboinyk case HAL_MODE_11NA_HT40MINUS: 50412d53238SAndriy Voskoboinyk *channelSep = 40; 50512d53238SAndriy Voskoboinyk /* FALLTHROUGH */ 50612d53238SAndriy Voskoboinyk case HAL_MODE_11NG_HT40MINUS: 50712d53238SAndriy Voskoboinyk *low_adj = 20; 50812d53238SAndriy Voskoboinyk break; 50912d53238SAndriy Voskoboinyk } 51012d53238SAndriy Voskoboinyk } 51112d53238SAndriy Voskoboinyk 51212d53238SAndriy Voskoboinyk static void 51312d53238SAndriy Voskoboinyk add_chanlist_mode(struct ath_hal *ah, struct ieee80211_channel chans[], 51412d53238SAndriy Voskoboinyk u_int maxchans, int *nchans, const struct cmode *cm, REG_DOMAIN *rd, 51512d53238SAndriy Voskoboinyk HAL_BOOL enableExtendedChannels) 51612d53238SAndriy Voskoboinyk { 51712d53238SAndriy Voskoboinyk uint64_t *channelBM; 51812d53238SAndriy Voskoboinyk uint16_t freq_lo, freq_hi; 51912d53238SAndriy Voskoboinyk int b, error, low_adj, hi_adj, channelSep; 52012d53238SAndriy Voskoboinyk 52112d53238SAndriy Voskoboinyk if (!ath_hal_getChannelEdges(ah, cm->flags, &freq_lo, &freq_hi)) { 52212d53238SAndriy Voskoboinyk /* channel not supported by hardware, skip it */ 52312d53238SAndriy Voskoboinyk HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 52412d53238SAndriy Voskoboinyk "%s: channels 0x%x not supported by hardware\n", 52512d53238SAndriy Voskoboinyk __func__, cm->flags); 52612d53238SAndriy Voskoboinyk return; 52712d53238SAndriy Voskoboinyk } 52812d53238SAndriy Voskoboinyk 52912d53238SAndriy Voskoboinyk channelBM = getchannelBM(cm->mode, rd); 53012d53238SAndriy Voskoboinyk if (isChanBitMaskZero(channelBM)) 53112d53238SAndriy Voskoboinyk return; 53212d53238SAndriy Voskoboinyk 53312d53238SAndriy Voskoboinyk /* 53412d53238SAndriy Voskoboinyk * Setup special handling for HT40 channels; e.g. 53512d53238SAndriy Voskoboinyk * 5G HT40 channels require 40Mhz channel separation. 53612d53238SAndriy Voskoboinyk */ 53712d53238SAndriy Voskoboinyk adj_freq_ht40(cm->mode, &low_adj, &hi_adj, &channelSep); 53812d53238SAndriy Voskoboinyk 53912d53238SAndriy Voskoboinyk for (b = 0; b < 64*BMLEN; b++) { 54012d53238SAndriy Voskoboinyk REG_DMN_FREQ_BAND *fband; 54112d53238SAndriy Voskoboinyk uint16_t bfreq_lo, bfreq_hi; 54212d53238SAndriy Voskoboinyk int step; 54312d53238SAndriy Voskoboinyk 54412d53238SAndriy Voskoboinyk if (!IS_BIT_SET(b, channelBM)) 54512d53238SAndriy Voskoboinyk continue; 54612d53238SAndriy Voskoboinyk fband = &cm->freqs[b]; 54712d53238SAndriy Voskoboinyk 54812d53238SAndriy Voskoboinyk if ((fband->usePassScan & IS_ECM_CHAN) && 54912d53238SAndriy Voskoboinyk !enableExtendedChannels) { 55012d53238SAndriy Voskoboinyk HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 55112d53238SAndriy Voskoboinyk "skip ecm channels\n"); 55212d53238SAndriy Voskoboinyk continue; 55312d53238SAndriy Voskoboinyk } 55412d53238SAndriy Voskoboinyk #if 0 55512d53238SAndriy Voskoboinyk if ((fband->useDfs & rd->dfsMask) && 55612d53238SAndriy Voskoboinyk (cm->flags & IEEE80211_CHAN_HT40)) { 55712d53238SAndriy Voskoboinyk /* NB: DFS and HT40 don't mix */ 55812d53238SAndriy Voskoboinyk HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 55912d53238SAndriy Voskoboinyk "skip HT40 chan, DFS required\n"); 56012d53238SAndriy Voskoboinyk continue; 56112d53238SAndriy Voskoboinyk } 56212d53238SAndriy Voskoboinyk #endif 5635b06614cSAdrian Chadd /* 5645b06614cSAdrian Chadd * XXX TODO: handle REG_EXT_FCC_CH_144. 5655b06614cSAdrian Chadd * 5665b06614cSAdrian Chadd * Figure out which instances/uses cause us to not 5675b06614cSAdrian Chadd * be allowed to use channel 144 (pri or sec overlap.) 5685b06614cSAdrian Chadd */ 5695b06614cSAdrian Chadd 57012d53238SAndriy Voskoboinyk bfreq_lo = MAX(fband->lowChannel + low_adj, freq_lo); 57112d53238SAndriy Voskoboinyk bfreq_hi = MIN(fband->highChannel + hi_adj, freq_hi); 572*eee8e362SAdrian Chadd 573*eee8e362SAdrian Chadd /* 574*eee8e362SAdrian Chadd * Don't start the 5GHz channel list at 5120MHz. 575*eee8e362SAdrian Chadd * 576*eee8e362SAdrian Chadd * Unfortunately (sigh) the HT40 channel creation 577*eee8e362SAdrian Chadd * logic will create HT40U channels at 5120, 5160, 5200. 578*eee8e362SAdrian Chadd * This means that 36 (5180) isn't considered as a 579*eee8e362SAdrian Chadd * HT40 channel, and everything goes messed up from there. 580*eee8e362SAdrian Chadd */ 581*eee8e362SAdrian Chadd if ((cm->flags & IEEE80211_CHAN_5GHZ) && 582*eee8e362SAdrian Chadd (cm->flags & IEEE80211_CHAN_HT40U)) { 583*eee8e362SAdrian Chadd if (bfreq_lo < 5180) 584*eee8e362SAdrian Chadd bfreq_lo = 5180; 585*eee8e362SAdrian Chadd } 586*eee8e362SAdrian Chadd 587*eee8e362SAdrian Chadd /* 588*eee8e362SAdrian Chadd * Same with HT40D - need to start at 5200 or the low 589*eee8e362SAdrian Chadd * channels are all wrong again. 590*eee8e362SAdrian Chadd */ 591*eee8e362SAdrian Chadd if ((cm->flags & IEEE80211_CHAN_5GHZ) && 592*eee8e362SAdrian Chadd (cm->flags & IEEE80211_CHAN_HT40D)) { 593*eee8e362SAdrian Chadd if (bfreq_lo < 5200) 594*eee8e362SAdrian Chadd bfreq_lo = 5200; 595*eee8e362SAdrian Chadd } 596*eee8e362SAdrian Chadd 59712d53238SAndriy Voskoboinyk if (fband->channelSep >= channelSep) 59812d53238SAndriy Voskoboinyk step = fband->channelSep; 59912d53238SAndriy Voskoboinyk else 60012d53238SAndriy Voskoboinyk step = roundup(channelSep, fband->channelSep); 60112d53238SAndriy Voskoboinyk 602*eee8e362SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 603*eee8e362SAdrian Chadd "%s: freq_lo=%d, freq_hi=%d, low_adj=%d, hi_adj=%d, " 604*eee8e362SAdrian Chadd "bandlo=%d, bandhi=%d, bfreqlo=%d, bfreqhi=%d, step=%d, " 605*eee8e362SAdrian Chadd "flags=0x%08x\n", 606*eee8e362SAdrian Chadd __func__, 607*eee8e362SAdrian Chadd (int) freq_lo, 608*eee8e362SAdrian Chadd (int) freq_hi, 609*eee8e362SAdrian Chadd (int) low_adj, 610*eee8e362SAdrian Chadd (int) hi_adj, 611*eee8e362SAdrian Chadd (int) fband->lowChannel, 612*eee8e362SAdrian Chadd (int) fband->highChannel, 613*eee8e362SAdrian Chadd (int) bfreq_lo, 614*eee8e362SAdrian Chadd (int) bfreq_hi, 615*eee8e362SAdrian Chadd step, 616*eee8e362SAdrian Chadd (int) cm->flags); 617*eee8e362SAdrian Chadd 61812d53238SAndriy Voskoboinyk error = add_chanlist_band(ah, chans, maxchans, nchans, 61912d53238SAndriy Voskoboinyk bfreq_lo, bfreq_hi, step, cm->flags, fband, rd); 62012d53238SAndriy Voskoboinyk if (error != 0) { 62112d53238SAndriy Voskoboinyk HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 62212d53238SAndriy Voskoboinyk "%s: too many channels for channel table\n", 62312d53238SAndriy Voskoboinyk __func__); 62412d53238SAndriy Voskoboinyk return; 62512d53238SAndriy Voskoboinyk } 62612d53238SAndriy Voskoboinyk } 62712d53238SAndriy Voskoboinyk } 62812d53238SAndriy Voskoboinyk 62912d53238SAndriy Voskoboinyk static u_int 63012d53238SAndriy Voskoboinyk getmodesmask(struct ath_hal *ah, REG_DOMAIN *rd5GHz, u_int modeSelect) 63112d53238SAndriy Voskoboinyk { 63212d53238SAndriy Voskoboinyk #define HAL_MODE_11A_ALL \ 63312d53238SAndriy Voskoboinyk (HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \ 63412d53238SAndriy Voskoboinyk HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE) 63512d53238SAndriy Voskoboinyk u_int modesMask; 63612d53238SAndriy Voskoboinyk 63712d53238SAndriy Voskoboinyk /* get modes that HW is capable of */ 63812d53238SAndriy Voskoboinyk modesMask = ath_hal_getWirelessModes(ah); 63912d53238SAndriy Voskoboinyk modesMask &= modeSelect; 64012d53238SAndriy Voskoboinyk /* optimize work below if no 11a channels */ 64112d53238SAndriy Voskoboinyk if (isChanBitMaskZero(rd5GHz->chan11a) && 64212d53238SAndriy Voskoboinyk (modesMask & HAL_MODE_11A_ALL)) { 64312d53238SAndriy Voskoboinyk HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 64412d53238SAndriy Voskoboinyk "%s: disallow all 11a\n", __func__); 64512d53238SAndriy Voskoboinyk modesMask &= ~HAL_MODE_11A_ALL; 64612d53238SAndriy Voskoboinyk } 64712d53238SAndriy Voskoboinyk 64812d53238SAndriy Voskoboinyk return (modesMask); 64912d53238SAndriy Voskoboinyk #undef HAL_MODE_11A_ALL 65012d53238SAndriy Voskoboinyk } 65112d53238SAndriy Voskoboinyk 65259efa8b5SSam Leffler /* 65359efa8b5SSam Leffler * Construct the channel list for the specified regulatory config. 65459efa8b5SSam Leffler */ 65559efa8b5SSam Leffler static HAL_STATUS 65659efa8b5SSam Leffler getchannels(struct ath_hal *ah, 65759efa8b5SSam Leffler struct ieee80211_channel chans[], u_int maxchans, int *nchans, 65859efa8b5SSam Leffler u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 65959efa8b5SSam Leffler HAL_BOOL enableExtendedChannels, 66059efa8b5SSam Leffler COUNTRY_CODE_TO_ENUM_RD **pcountry, 66159efa8b5SSam Leffler REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz) 66259efa8b5SSam Leffler { 66359efa8b5SSam Leffler REG_DOMAIN *rd5GHz, *rd2GHz; 66412d53238SAndriy Voskoboinyk u_int modesMask; 66559efa8b5SSam Leffler const struct cmode *cm; 66659efa8b5SSam Leffler HAL_STATUS status; 66759efa8b5SSam Leffler 66859efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u regDmn 0x%x mode 0x%x%s\n", 66959efa8b5SSam Leffler __func__, cc, regDmn, modeSelect, 67059efa8b5SSam Leffler enableExtendedChannels ? " ecm" : ""); 67159efa8b5SSam Leffler 67259efa8b5SSam Leffler status = getregstate(ah, cc, regDmn, pcountry, &rd2GHz, &rd5GHz); 67359efa8b5SSam Leffler if (status != HAL_OK) 67459efa8b5SSam Leffler return status; 67559efa8b5SSam Leffler 67612d53238SAndriy Voskoboinyk modesMask = getmodesmask(ah, rd5GHz, modeSelect); 67712d53238SAndriy Voskoboinyk /* XXX error? */ 67812d53238SAndriy Voskoboinyk if (modesMask == 0) 67912d53238SAndriy Voskoboinyk goto done; 68059efa8b5SSam Leffler 68114779705SSam Leffler for (cm = modes; cm < &modes[N(modes)]; cm++) { 68212d53238SAndriy Voskoboinyk REG_DOMAIN *rd; 68314779705SSam Leffler 68412d53238SAndriy Voskoboinyk if ((cm->mode & modesMask) == 0) { 68514779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 68614779705SSam Leffler "%s: skip mode 0x%x flags 0x%x\n", 68714779705SSam Leffler __func__, cm->mode, cm->flags); 68814779705SSam Leffler continue; 68914779705SSam Leffler } 69014779705SSam Leffler 69112d53238SAndriy Voskoboinyk if (cm->flags & IEEE80211_CHAN_5GHZ) 69212d53238SAndriy Voskoboinyk rd = rd5GHz; 69312d53238SAndriy Voskoboinyk else if (cm->flags & IEEE80211_CHAN_2GHZ) 69412d53238SAndriy Voskoboinyk rd = rd2GHz; 69512d53238SAndriy Voskoboinyk else { 696b1940debSAdrian Chadd ath_hal_printf(ah, "%s: Unkonwn HAL flags 0x%x\n", 697b1940debSAdrian Chadd __func__, cm->flags); 69812d53238SAndriy Voskoboinyk return HAL_EINVAL; 69914779705SSam Leffler } 70012d53238SAndriy Voskoboinyk 70112d53238SAndriy Voskoboinyk add_chanlist_mode(ah, chans, maxchans, nchans, cm, 70212d53238SAndriy Voskoboinyk rd, enableExtendedChannels); 70312d53238SAndriy Voskoboinyk if (*nchans >= maxchans) 70414779705SSam Leffler goto done; 70514779705SSam Leffler } 70614779705SSam Leffler done: 70759efa8b5SSam Leffler /* NB: pcountry set above by getregstate */ 70859efa8b5SSam Leffler if (prd2GHz != AH_NULL) 70959efa8b5SSam Leffler *prd2GHz = rd2GHz; 71059efa8b5SSam Leffler if (prd5GHz != AH_NULL) 71159efa8b5SSam Leffler *prd5GHz = rd5GHz; 71259efa8b5SSam Leffler return HAL_OK; 71314779705SSam Leffler } 71414779705SSam Leffler 71514779705SSam Leffler /* 71659efa8b5SSam Leffler * Retrieve a channel list without affecting runtime state. 71714779705SSam Leffler */ 71859efa8b5SSam Leffler HAL_STATUS 71959efa8b5SSam Leffler ath_hal_getchannels(struct ath_hal *ah, 72059efa8b5SSam Leffler struct ieee80211_channel chans[], u_int maxchans, int *nchans, 72159efa8b5SSam Leffler u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 72259efa8b5SSam Leffler HAL_BOOL enableExtendedChannels) 72314779705SSam Leffler { 72459efa8b5SSam Leffler return getchannels(ah, chans, maxchans, nchans, modeSelect, 72559efa8b5SSam Leffler cc, regDmn, enableExtendedChannels, AH_NULL, AH_NULL, AH_NULL); 72659efa8b5SSam Leffler } 72714779705SSam Leffler 72814779705SSam Leffler /* 72959efa8b5SSam Leffler * Handle frequency mapping from 900Mhz range to 2.4GHz range 73059efa8b5SSam Leffler * for GSM radios. This is done when we need the h/w frequency 73159efa8b5SSam Leffler * and the channel is marked IEEE80211_CHAN_GSM. 73214779705SSam Leffler */ 73359efa8b5SSam Leffler static int 73459efa8b5SSam Leffler ath_hal_mapgsm(int sku, int freq) 73559efa8b5SSam Leffler { 73659efa8b5SSam Leffler if (sku == SKU_XR9) 73759efa8b5SSam Leffler return 1520 + freq; 73859efa8b5SSam Leffler if (sku == SKU_GZ901) 73959efa8b5SSam Leffler return 1544 + freq; 74059efa8b5SSam Leffler if (sku == SKU_SR9) 74159efa8b5SSam Leffler return 3344 - freq; 7422992cd22SAdrian Chadd if (sku == SKU_XC900M) 7432992cd22SAdrian Chadd return 1517 + freq; 7440e56140aSAdrian Chadd HALDEBUG(AH_NULL, HAL_DEBUG_ANY, 74559efa8b5SSam Leffler "%s: cannot map freq %u unknown gsm sku %u\n", 74659efa8b5SSam Leffler __func__, freq, sku); 74759efa8b5SSam Leffler return freq; 74814779705SSam Leffler } 74914779705SSam Leffler 75059efa8b5SSam Leffler /* 75159efa8b5SSam Leffler * Setup the internal/private channel state given a table of 75259efa8b5SSam Leffler * net80211 channels. We collapse entries for the same frequency 75359efa8b5SSam Leffler * and record the frequency for doing noise floor processing 75459efa8b5SSam Leffler * where we don't have net80211 channel context. 75559efa8b5SSam Leffler */ 75659efa8b5SSam Leffler static HAL_BOOL 75759efa8b5SSam Leffler assignPrivateChannels(struct ath_hal *ah, 75859efa8b5SSam Leffler struct ieee80211_channel chans[], int nchans, int sku) 75959efa8b5SSam Leffler { 76059efa8b5SSam Leffler HAL_CHANNEL_INTERNAL *ic; 76159efa8b5SSam Leffler int i, j, next, freq; 76259efa8b5SSam Leffler 76359efa8b5SSam Leffler next = 0; 76459efa8b5SSam Leffler for (i = 0; i < nchans; i++) { 76559efa8b5SSam Leffler struct ieee80211_channel *c = &chans[i]; 76659efa8b5SSam Leffler for (j = i-1; j >= 0; j--) 76759efa8b5SSam Leffler if (chans[j].ic_freq == c->ic_freq) { 76859efa8b5SSam Leffler c->ic_devdata = chans[j].ic_devdata; 76959efa8b5SSam Leffler break; 77059efa8b5SSam Leffler } 77159efa8b5SSam Leffler if (j < 0) { 77259efa8b5SSam Leffler /* new entry, assign a private channel entry */ 77359efa8b5SSam Leffler if (next >= N(AH_PRIVATE(ah)->ah_channels)) { 77459efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, 77550d5ad0eSSam Leffler "%s: too many channels, max %zu\n", 77659efa8b5SSam Leffler __func__, N(AH_PRIVATE(ah)->ah_channels)); 77759efa8b5SSam Leffler return AH_FALSE; 77859efa8b5SSam Leffler } 77959efa8b5SSam Leffler /* 78059efa8b5SSam Leffler * Handle frequency mapping for 900MHz devices. 78159efa8b5SSam Leffler * The hardware uses 2.4GHz frequencies that are 78259efa8b5SSam Leffler * down-converted. The 802.11 layer uses the 78359efa8b5SSam Leffler * true frequencies. 78459efa8b5SSam Leffler */ 78559efa8b5SSam Leffler freq = IEEE80211_IS_CHAN_GSM(c) ? 78659efa8b5SSam Leffler ath_hal_mapgsm(sku, c->ic_freq) : c->ic_freq; 78759efa8b5SSam Leffler 78859efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 78959efa8b5SSam Leffler "%s: private[%3u] %u/0x%x -> channel %u\n", 79059efa8b5SSam Leffler __func__, next, c->ic_freq, c->ic_flags, freq); 79159efa8b5SSam Leffler 79259efa8b5SSam Leffler ic = &AH_PRIVATE(ah)->ah_channels[next]; 79359efa8b5SSam Leffler /* 79459efa8b5SSam Leffler * NB: This clears privFlags which means ancillary 79559efa8b5SSam Leffler * code like ANI and IQ calibration will be 79659efa8b5SSam Leffler * restarted and re-setup any per-channel state. 79759efa8b5SSam Leffler */ 79859efa8b5SSam Leffler OS_MEMZERO(ic, sizeof(*ic)); 79959efa8b5SSam Leffler ic->channel = freq; 80059efa8b5SSam Leffler c->ic_devdata = next; 80159efa8b5SSam Leffler next++; 80259efa8b5SSam Leffler } 80359efa8b5SSam Leffler } 80459efa8b5SSam Leffler AH_PRIVATE(ah)->ah_nchan = next; 80559efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: %u public, %u private channels\n", 80659efa8b5SSam Leffler __func__, nchans, next); 80759efa8b5SSam Leffler return AH_TRUE; 80859efa8b5SSam Leffler } 80959efa8b5SSam Leffler 81059efa8b5SSam Leffler /* 81159efa8b5SSam Leffler * Setup the channel list based on the information in the EEPROM. 81259efa8b5SSam Leffler */ 81359efa8b5SSam Leffler HAL_STATUS 81459efa8b5SSam Leffler ath_hal_init_channels(struct ath_hal *ah, 81559efa8b5SSam Leffler struct ieee80211_channel chans[], u_int maxchans, int *nchans, 81659efa8b5SSam Leffler u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 81759efa8b5SSam Leffler HAL_BOOL enableExtendedChannels) 81859efa8b5SSam Leffler { 81959efa8b5SSam Leffler COUNTRY_CODE_TO_ENUM_RD *country; 8207dd4de13SAdrian Chadd REG_DOMAIN *rd5GHz, *rd2GHz; 82159efa8b5SSam Leffler HAL_STATUS status; 82259efa8b5SSam Leffler 82359efa8b5SSam Leffler status = getchannels(ah, chans, maxchans, nchans, modeSelect, 82459efa8b5SSam Leffler cc, regDmn, enableExtendedChannels, &country, &rd2GHz, &rd5GHz); 82559efa8b5SSam Leffler if (status == HAL_OK && 82659efa8b5SSam Leffler assignPrivateChannels(ah, chans, *nchans, AH_PRIVATE(ah)->ah_currentRD)) { 82759efa8b5SSam Leffler AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz; 82859efa8b5SSam Leffler AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz; 82959efa8b5SSam Leffler 83059efa8b5SSam Leffler ah->ah_countryCode = country->countryCode; 83159efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n", 83259efa8b5SSam Leffler __func__, ah->ah_countryCode); 8337dd4de13SAdrian Chadd 8347dd4de13SAdrian Chadd /* Update current DFS domain */ 8357dd4de13SAdrian Chadd ath_hal_update_dfsdomain(ah); 83659efa8b5SSam Leffler } else 83759efa8b5SSam Leffler status = HAL_EINVAL; 8388db87e40SAdrian Chadd 83959efa8b5SSam Leffler return status; 84059efa8b5SSam Leffler } 84159efa8b5SSam Leffler 84259efa8b5SSam Leffler /* 84359efa8b5SSam Leffler * Set the channel list. 84459efa8b5SSam Leffler */ 84559efa8b5SSam Leffler HAL_STATUS 84659efa8b5SSam Leffler ath_hal_set_channels(struct ath_hal *ah, 84759efa8b5SSam Leffler struct ieee80211_channel chans[], int nchans, 84859efa8b5SSam Leffler HAL_CTRY_CODE cc, HAL_REG_DOMAIN rd) 84959efa8b5SSam Leffler { 85059efa8b5SSam Leffler COUNTRY_CODE_TO_ENUM_RD *country; 85159efa8b5SSam Leffler REG_DOMAIN *rd5GHz, *rd2GHz; 85259efa8b5SSam Leffler HAL_STATUS status; 85359efa8b5SSam Leffler 85459efa8b5SSam Leffler switch (rd) { 85559efa8b5SSam Leffler case SKU_SR9: 85659efa8b5SSam Leffler case SKU_XR9: 85759efa8b5SSam Leffler case SKU_GZ901: 8582992cd22SAdrian Chadd case SKU_XC900M: 85959efa8b5SSam Leffler /* 86059efa8b5SSam Leffler * Map 900MHz sku's. The frequencies will be mapped 86159efa8b5SSam Leffler * according to the sku to compensate for the down-converter. 86259efa8b5SSam Leffler * We use the FCC for these sku's as the mapped channel 86359efa8b5SSam Leffler * list is known compatible (will need to change if/when 86459efa8b5SSam Leffler * vendors do different mapping in different locales). 86559efa8b5SSam Leffler */ 86659efa8b5SSam Leffler status = getregstate(ah, CTRY_DEFAULT, SKU_FCC, 86759efa8b5SSam Leffler &country, &rd2GHz, &rd5GHz); 86859efa8b5SSam Leffler break; 86959efa8b5SSam Leffler default: 87059efa8b5SSam Leffler status = getregstate(ah, cc, rd, 87159efa8b5SSam Leffler &country, &rd2GHz, &rd5GHz); 87259efa8b5SSam Leffler rd = AH_PRIVATE(ah)->ah_currentRD; 87359efa8b5SSam Leffler break; 87459efa8b5SSam Leffler } 87559efa8b5SSam Leffler if (status == HAL_OK && assignPrivateChannels(ah, chans, nchans, rd)) { 87659efa8b5SSam Leffler AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz; 87759efa8b5SSam Leffler AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz; 87859efa8b5SSam Leffler 87959efa8b5SSam Leffler ah->ah_countryCode = country->countryCode; 88059efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n", 88159efa8b5SSam Leffler __func__, ah->ah_countryCode); 88259efa8b5SSam Leffler } else 88359efa8b5SSam Leffler status = HAL_EINVAL; 8847dd4de13SAdrian Chadd 8857dd4de13SAdrian Chadd if (status == HAL_OK) { 8867dd4de13SAdrian Chadd /* Update current DFS domain */ 8877dd4de13SAdrian Chadd (void) ath_hal_update_dfsdomain(ah); 8887dd4de13SAdrian Chadd } 88959efa8b5SSam Leffler return status; 89059efa8b5SSam Leffler } 89159efa8b5SSam Leffler 89259efa8b5SSam Leffler #ifdef AH_DEBUG 89359efa8b5SSam Leffler /* 89459efa8b5SSam Leffler * Return the internal channel corresponding to a public channel. 89559efa8b5SSam Leffler * NB: normally this routine is inline'd (see ah_internal.h) 89659efa8b5SSam Leffler */ 89759efa8b5SSam Leffler HAL_CHANNEL_INTERNAL * 89859efa8b5SSam Leffler ath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c) 89959efa8b5SSam Leffler { 90059efa8b5SSam Leffler HAL_CHANNEL_INTERNAL *cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata]; 90159efa8b5SSam Leffler 90259efa8b5SSam Leffler if (c->ic_devdata < AH_PRIVATE(ah)->ah_nchan && 90359efa8b5SSam Leffler (c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c))) 90414779705SSam Leffler return cc; 90559efa8b5SSam Leffler if (c->ic_devdata >= AH_PRIVATE(ah)->ah_nchan) { 90659efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, 90759efa8b5SSam Leffler "%s: bad mapping, devdata %u nchans %u\n", 90859efa8b5SSam Leffler __func__, c->ic_devdata, AH_PRIVATE(ah)->ah_nchan); 90959efa8b5SSam Leffler HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan); 91059efa8b5SSam Leffler } else { 91159efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, 91259efa8b5SSam Leffler "%s: no match for %u/0x%x devdata %u channel %u\n", 91359efa8b5SSam Leffler __func__, c->ic_freq, c->ic_flags, c->ic_devdata, 91459efa8b5SSam Leffler cc->channel); 91559efa8b5SSam Leffler HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)); 91614779705SSam Leffler } 91714779705SSam Leffler return AH_NULL; 91859efa8b5SSam Leffler } 91959efa8b5SSam Leffler #endif /* AH_DEBUG */ 92059efa8b5SSam Leffler 92159efa8b5SSam Leffler #define isWwrSKU(_ah) \ 92259efa8b5SSam Leffler ((getEepromRD((_ah)) & WORLD_SKU_MASK) == WORLD_SKU_PREFIX || \ 92359efa8b5SSam Leffler getEepromRD(_ah) == WORLD) 92459efa8b5SSam Leffler 92559efa8b5SSam Leffler /* 92659efa8b5SSam Leffler * Return the test group for the specific channel based on 92759efa8b5SSam Leffler * the current regulatory setup. 92859efa8b5SSam Leffler */ 92959efa8b5SSam Leffler u_int 93059efa8b5SSam Leffler ath_hal_getctl(struct ath_hal *ah, const struct ieee80211_channel *c) 93159efa8b5SSam Leffler { 93259efa8b5SSam Leffler u_int ctl; 93359efa8b5SSam Leffler 93459efa8b5SSam Leffler if (AH_PRIVATE(ah)->ah_rd2GHz == AH_PRIVATE(ah)->ah_rd5GHz || 93559efa8b5SSam Leffler (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah))) 93659efa8b5SSam Leffler ctl = SD_NO_CTL; 93759efa8b5SSam Leffler else if (IEEE80211_IS_CHAN_2GHZ(c)) 93859efa8b5SSam Leffler ctl = AH_PRIVATE(ah)->ah_rd2GHz->conformanceTestLimit; 93959efa8b5SSam Leffler else 94059efa8b5SSam Leffler ctl = AH_PRIVATE(ah)->ah_rd5GHz->conformanceTestLimit; 94159efa8b5SSam Leffler if (IEEE80211_IS_CHAN_B(c)) 94259efa8b5SSam Leffler return ctl | CTL_11B; 94359efa8b5SSam Leffler if (IEEE80211_IS_CHAN_G(c)) 94459efa8b5SSam Leffler return ctl | CTL_11G; 94559efa8b5SSam Leffler if (IEEE80211_IS_CHAN_108G(c)) 94659efa8b5SSam Leffler return ctl | CTL_108G; 94759efa8b5SSam Leffler if (IEEE80211_IS_CHAN_TURBO(c)) 94859efa8b5SSam Leffler return ctl | CTL_TURBO; 94959efa8b5SSam Leffler if (IEEE80211_IS_CHAN_A(c)) 95059efa8b5SSam Leffler return ctl | CTL_11A; 95159efa8b5SSam Leffler return ctl; 95214779705SSam Leffler } 95314779705SSam Leffler 9547dd4de13SAdrian Chadd 9557dd4de13SAdrian Chadd /* 9567dd4de13SAdrian Chadd * Update the current dfsDomain setting based on the given 9577dd4de13SAdrian Chadd * country code. 9587dd4de13SAdrian Chadd * 9597dd4de13SAdrian Chadd * Since FreeBSD/net80211 allows the channel set to change 9607dd4de13SAdrian Chadd * after the card has been setup (via ath_hal_init_channels()) 9617dd4de13SAdrian Chadd * this function method is needed to update ah_dfsDomain. 9627dd4de13SAdrian Chadd */ 9637dd4de13SAdrian Chadd void 9647dd4de13SAdrian Chadd ath_hal_update_dfsdomain(struct ath_hal *ah) 9657dd4de13SAdrian Chadd { 9667dd4de13SAdrian Chadd const REG_DOMAIN *rd5GHz = AH_PRIVATE(ah)->ah_rd5GHz; 9677dd4de13SAdrian Chadd HAL_DFS_DOMAIN dfsDomain = HAL_DFS_UNINIT_DOMAIN; 9687dd4de13SAdrian Chadd 9697dd4de13SAdrian Chadd if (rd5GHz->dfsMask & DFS_FCC3) 9707dd4de13SAdrian Chadd dfsDomain = HAL_DFS_FCC_DOMAIN; 9717dd4de13SAdrian Chadd if (rd5GHz->dfsMask & DFS_ETSI) 9727dd4de13SAdrian Chadd dfsDomain = HAL_DFS_ETSI_DOMAIN; 9737dd4de13SAdrian Chadd if (rd5GHz->dfsMask & DFS_MKK4) 9747dd4de13SAdrian Chadd dfsDomain = HAL_DFS_MKK4_DOMAIN; 9757dd4de13SAdrian Chadd AH_PRIVATE(ah)->ah_dfsDomain = dfsDomain; 9767dd4de13SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s ah_dfsDomain: %d\n", 9777dd4de13SAdrian Chadd __func__, AH_PRIVATE(ah)->ah_dfsDomain); 9787dd4de13SAdrian Chadd } 9797dd4de13SAdrian Chadd 9807dd4de13SAdrian Chadd 98114779705SSam Leffler /* 98214779705SSam Leffler * Return the max allowed antenna gain and apply any regulatory 98314779705SSam Leffler * domain specific changes. 98414779705SSam Leffler * 98514779705SSam Leffler * NOTE: a negative reduction is possible in RD's that only 98614779705SSam Leffler * measure radiated power (e.g., ETSI) which would increase 98714779705SSam Leffler * that actual conducted output power (though never beyond 98814779705SSam Leffler * the calibrated target power). 98914779705SSam Leffler */ 99014779705SSam Leffler u_int 99159efa8b5SSam Leffler ath_hal_getantennareduction(struct ath_hal *ah, 99259efa8b5SSam Leffler const struct ieee80211_channel *chan, u_int twiceGain) 99314779705SSam Leffler { 99459efa8b5SSam Leffler int8_t antennaMax = twiceGain - chan->ic_maxantgain*2; 99514779705SSam Leffler return (antennaMax < 0) ? 0 : antennaMax; 99614779705SSam Leffler } 997