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