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