ah_regdomain.c revision 219442
1185377Ssam/* 2187831Ssam * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 3185377Ssam * Copyright (c) 2005-2006 Atheros Communications, Inc. 4185377Ssam * All rights reserved. 5185377Ssam * 6185377Ssam * Permission to use, copy, modify, and/or distribute this software for any 7185377Ssam * purpose with or without fee is hereby granted, provided that the above 8185377Ssam * copyright notice and this permission notice appear in all copies. 9185377Ssam * 10185377Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11185377Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12185377Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13185377Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14185377Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15185377Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16185377Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17185377Ssam * 18187345Ssam * $FreeBSD: head/sys/dev/ath/ath_hal/ah_regdomain.c 219442 2011-03-10 03:13:56Z adrian $ 19185377Ssam */ 20185377Ssam#include "opt_ah.h" 21185377Ssam 22185377Ssam#include "ah.h" 23187831Ssam 24187831Ssam#include <net80211/_ieee80211.h> 25187831Ssam#include <net80211/ieee80211_regdomain.h> 26187831Ssam 27185377Ssam#include "ah_internal.h" 28185377Ssam#include "ah_eeprom.h" 29185377Ssam#include "ah_devid.h" 30185377Ssam 31219394Sadrian#include "ah_regdomain.h" 32219394Sadrian 33185377Ssam/* 34185377Ssam * XXX this code needs a audit+review 35185377Ssam */ 36185377Ssam 37185377Ssam/* used throughout this file... */ 38185377Ssam#define N(a) (sizeof (a) / sizeof (a[0])) 39185377Ssam 40185377Ssam#define HAL_MODE_11A_TURBO HAL_MODE_108A 41185377Ssam#define HAL_MODE_11G_TURBO HAL_MODE_108G 42185377Ssam 43185377Ssam/* 44185380Ssam * Mask to check whether a domain is a multidomain or a single domain 45185380Ssam */ 46185377Ssam#define MULTI_DOMAIN_MASK 0xFF00 47185377Ssam 48185380Ssam/* 49185380Ssam * Enumerated Regulatory Domain Information 8 bit values indicate that 50185377Ssam * the regdomain is really a pair of unitary regdomains. 12 bit values 51185377Ssam * are the real unitary regdomains and are the only ones which have the 52185377Ssam * frequency bitmasks and flags set. 53185377Ssam */ 54219442Sadrian#include "ah_regdomain/ah_rd_regenum.h" 55185377Ssam 56185377Ssam#define WORLD_SKU_MASK 0x00F0 57185377Ssam#define WORLD_SKU_PREFIX 0x0060 58185377Ssam 59185377Ssam/* 60185377Ssam * THE following table is the mapping of regdomain pairs specified by 61185377Ssam * an 8 bit regdomain value to the individual unitary reg domains 62185377Ssam */ 63219442Sadrian#include "ah_regdomain/ah_rd_regmap.h" 64185377Ssam 65185377Ssam/* 66185380Ssam * The following tables are the master list for all different freqeuncy 67185377Ssam * bands with the complete matrix of all possible flags and settings 68185377Ssam * for each band if it is used in ANY reg domain. 69185377Ssam */ 70185377Ssam 71185377Ssam#define COUNTRY_ERD_FLAG 0x8000 72185377Ssam#define WORLDWIDE_ROAMING_FLAG 0x4000 73185377Ssam 74185380Ssam/* 75219442Sadrian * This table maps country ISO codes from net80211 into regulatory 76219442Sadrian * domains which the ath regulatory domain code understands. 77185380Ssam */ 78219442Sadrian#include "ah_regdomain/ah_rd_ctry.h" 79185380Ssam 80185377Ssam/* 81219442Sadrian * The frequency band collections are a set of frequency ranges 82219442Sadrian * with shared properties - max tx power, max antenna gain, channel width, 83219442Sadrian * channel spacing, DFS requirements and passive scanning requirements. 84219442Sadrian * 85219442Sadrian * These are represented as entries in a frequency band bitmask. 86219442Sadrian * Each regulatory domain entry in ah_regdomain_domains.h uses one 87219442Sadrian * or more frequency band entries for each of the channel modes 88219442Sadrian * supported (11bg, 11a, half, quarter, turbo, etc.) 89219442Sadrian * 90185377Ssam */ 91219442Sadrian#include "ah_regdomain/ah_rd_freqbands.h" 92185377Ssam 93185377Ssam/* 94219442Sadrian * This is the main regulatory database. It defines the supported 95219442Sadrian * set of features and requirements for each of the defined regulatory 96219442Sadrian * zones. It uses combinations of frequency ranges - represented in 97219442Sadrian * a bitmask - to determine the requirements and limitations needed. 98185377Ssam */ 99219442Sadrian#include "ah_regdomain/ah_rd_domains.h" 100185377Ssam 101185377Ssamstatic const struct cmode modes[] = { 102187831Ssam { HAL_MODE_TURBO, IEEE80211_CHAN_ST }, 103187831Ssam { HAL_MODE_11A, IEEE80211_CHAN_A }, 104187831Ssam { HAL_MODE_11B, IEEE80211_CHAN_B }, 105187831Ssam { HAL_MODE_11G, IEEE80211_CHAN_G }, 106187831Ssam { HAL_MODE_11G_TURBO, IEEE80211_CHAN_108G }, 107187831Ssam { HAL_MODE_11A_TURBO, IEEE80211_CHAN_108A }, 108187831Ssam { HAL_MODE_11A_QUARTER_RATE, 109187831Ssam IEEE80211_CHAN_A | IEEE80211_CHAN_QUARTER }, 110187831Ssam { HAL_MODE_11A_HALF_RATE, 111187831Ssam IEEE80211_CHAN_A | IEEE80211_CHAN_HALF }, 112187831Ssam { HAL_MODE_11G_QUARTER_RATE, 113187831Ssam IEEE80211_CHAN_G | IEEE80211_CHAN_QUARTER }, 114187831Ssam { HAL_MODE_11G_HALF_RATE, 115187831Ssam IEEE80211_CHAN_G | IEEE80211_CHAN_HALF }, 116188264Ssam { HAL_MODE_11NG_HT20, IEEE80211_CHAN_G | IEEE80211_CHAN_HT20 }, 117187831Ssam { HAL_MODE_11NG_HT40PLUS, 118188264Ssam IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U }, 119187831Ssam { HAL_MODE_11NG_HT40MINUS, 120188264Ssam IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D }, 121188264Ssam { HAL_MODE_11NA_HT20, IEEE80211_CHAN_A | IEEE80211_CHAN_HT20 }, 122187831Ssam { HAL_MODE_11NA_HT40PLUS, 123188264Ssam IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U }, 124187831Ssam { HAL_MODE_11NA_HT40MINUS, 125188264Ssam IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D }, 126185377Ssam}; 127185377Ssam 128187831Ssamstatic OS_INLINE uint16_t 129185377SsamgetEepromRD(struct ath_hal *ah) 130185377Ssam{ 131185377Ssam return AH_PRIVATE(ah)->ah_currentRD &~ WORLDWIDE_ROAMING_FLAG; 132185377Ssam} 133185377Ssam 134185377Ssam/* 135185377Ssam * Test to see if the bitmask array is all zeros 136185377Ssam */ 137185377Ssamstatic HAL_BOOL 138185380SsamisChanBitMaskZero(const uint64_t *bitmask) 139185377Ssam{ 140185377Ssam#if BMLEN > 2 141185377Ssam#error "add more cases" 142185377Ssam#endif 143185377Ssam#if BMLEN > 1 144185377Ssam if (bitmask[1] != 0) 145185377Ssam return AH_FALSE; 146185377Ssam#endif 147185377Ssam return (bitmask[0] == 0); 148185377Ssam} 149185377Ssam 150185377Ssam/* 151185377Ssam * Return whether or not the regulatory domain/country in EEPROM 152185377Ssam * is acceptable. 153185377Ssam */ 154185377Ssamstatic HAL_BOOL 155185377SsamisEepromValid(struct ath_hal *ah) 156185377Ssam{ 157185377Ssam uint16_t rd = getEepromRD(ah); 158185377Ssam int i; 159185377Ssam 160185377Ssam if (rd & COUNTRY_ERD_FLAG) { 161185377Ssam uint16_t cc = rd &~ COUNTRY_ERD_FLAG; 162185377Ssam for (i = 0; i < N(allCountries); i++) 163185377Ssam if (allCountries[i].countryCode == cc) 164185377Ssam return AH_TRUE; 165185377Ssam } else { 166185377Ssam for (i = 0; i < N(regDomainPairs); i++) 167185377Ssam if (regDomainPairs[i].regDmnEnum == rd) 168185377Ssam return AH_TRUE; 169185377Ssam } 170185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 171185377Ssam "%s: invalid regulatory domain/country code 0x%x\n", __func__, rd); 172185377Ssam return AH_FALSE; 173185377Ssam} 174185377Ssam 175185377Ssam/* 176187831Ssam * Find the pointer to the country element in the country table 177187831Ssam * corresponding to the country code 178185377Ssam */ 179187831Ssamstatic COUNTRY_CODE_TO_ENUM_RD* 180187831SsamfindCountry(HAL_CTRY_CODE countryCode) 181185377Ssam{ 182187831Ssam int i; 183185377Ssam 184187831Ssam for (i = 0; i < N(allCountries); i++) { 185187831Ssam if (allCountries[i].countryCode == countryCode) 186187831Ssam return &allCountries[i]; 187185377Ssam } 188187831Ssam return AH_NULL; 189185377Ssam} 190185377Ssam 191187831Ssamstatic REG_DOMAIN * 192187831SsamfindRegDmn(int regDmn) 193185377Ssam{ 194187831Ssam int i; 195185377Ssam 196187831Ssam for (i = 0; i < N(regDomains); i++) { 197187831Ssam if (regDomains[i].regDmnEnum == regDmn) 198187831Ssam return ®Domains[i]; 199185380Ssam } 200187831Ssam return AH_NULL; 201185377Ssam} 202185377Ssam 203187831Ssamstatic REG_DMN_PAIR_MAPPING * 204187831SsamfindRegDmnPair(int regDmnPair) 205185377Ssam{ 206185377Ssam int i; 207185377Ssam 208187831Ssam if (regDmnPair != NO_ENUMRD) { 209187831Ssam for (i = 0; i < N(regDomainPairs); i++) { 210187831Ssam if (regDomainPairs[i].regDmnEnum == regDmnPair) 211187831Ssam return ®DomainPairs[i]; 212187831Ssam } 213185377Ssam } 214187831Ssam return AH_NULL; 215185377Ssam} 216185377Ssam 217185377Ssam/* 218185377Ssam * Calculate a default country based on the EEPROM setting. 219185377Ssam */ 220185377Ssamstatic HAL_CTRY_CODE 221185377SsamgetDefaultCountry(struct ath_hal *ah) 222185377Ssam{ 223187831Ssam REG_DMN_PAIR_MAPPING *regpair; 224185377Ssam uint16_t rd; 225185377Ssam 226185377Ssam rd = getEepromRD(ah); 227185377Ssam if (rd & COUNTRY_ERD_FLAG) { 228187831Ssam COUNTRY_CODE_TO_ENUM_RD *country; 229185377Ssam uint16_t cc = rd & ~COUNTRY_ERD_FLAG; 230185377Ssam country = findCountry(cc); 231185377Ssam if (country != AH_NULL) 232185377Ssam return cc; 233185377Ssam } 234185377Ssam /* 235185377Ssam * Check reg domains that have only one country 236185377Ssam */ 237187831Ssam regpair = findRegDmnPair(rd); 238187831Ssam return (regpair != AH_NULL) ? regpair->singleCC : CTRY_DEFAULT; 239185377Ssam} 240185377Ssam 241185377Ssamstatic HAL_BOOL 242187831SsamIS_BIT_SET(int bit, const uint64_t bitmask[]) 243185377Ssam{ 244187831Ssam int byteOffset, bitnum; 245187831Ssam uint64_t val; 246185377Ssam 247187831Ssam byteOffset = bit/64; 248187831Ssam bitnum = bit - byteOffset*64; 249187831Ssam val = ((uint64_t) 1) << bitnum; 250187831Ssam return (bitmask[byteOffset] & val) != 0; 251185377Ssam} 252185377Ssam 253187831Ssamstatic HAL_STATUS 254187831Ssamgetregstate(struct ath_hal *ah, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 255187831Ssam COUNTRY_CODE_TO_ENUM_RD **pcountry, 256187831Ssam REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz) 257185377Ssam{ 258187831Ssam COUNTRY_CODE_TO_ENUM_RD *country; 259187831Ssam REG_DOMAIN *rd5GHz, *rd2GHz; 260185377Ssam 261187831Ssam if (cc == CTRY_DEFAULT && regDmn == SKU_NONE) { 262187831Ssam /* 263187831Ssam * Validate the EEPROM setting and setup defaults 264187831Ssam */ 265187831Ssam if (!isEepromValid(ah)) { 266187831Ssam /* 267187831Ssam * Don't return any channels if the EEPROM has an 268187831Ssam * invalid regulatory domain/country code setting. 269187831Ssam */ 270187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 271187831Ssam "%s: invalid EEPROM contents\n",__func__); 272187831Ssam return HAL_EEBADREG; 273187831Ssam } 274185377Ssam 275187831Ssam cc = getDefaultCountry(ah); 276187831Ssam country = findCountry(cc); 277187831Ssam if (country == AH_NULL) { 278187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 279187831Ssam "NULL Country!, cc %d\n", cc); 280187831Ssam return HAL_EEBADCC; 281187831Ssam } 282187831Ssam regDmn = country->regDmnEnum; 283187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: EEPROM cc %u rd 0x%x\n", 284187831Ssam __func__, cc, regDmn); 285185377Ssam 286187831Ssam if (country->countryCode == CTRY_DEFAULT) { 287187831Ssam /* 288187831Ssam * Check EEPROM; SKU may be for a country, single 289187831Ssam * domain, or multiple domains (WWR). 290187831Ssam */ 291187831Ssam uint16_t rdnum = getEepromRD(ah); 292187831Ssam if ((rdnum & COUNTRY_ERD_FLAG) == 0 && 293187831Ssam (findRegDmn(rdnum) != AH_NULL || 294187831Ssam findRegDmnPair(rdnum) != AH_NULL)) { 295185377Ssam regDmn = rdnum; 296187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 297187831Ssam "%s: EEPROM rd 0x%x\n", __func__, rdnum); 298185377Ssam } 299185377Ssam } 300187831Ssam } else { 301187831Ssam country = findCountry(cc); 302187831Ssam if (country == AH_NULL) { 303185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 304187831Ssam "unknown country, cc %d\n", cc); 305187831Ssam return HAL_EINVAL; 306185377Ssam } 307187831Ssam if (regDmn == SKU_NONE) 308187831Ssam regDmn = country->regDmnEnum; 309187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u rd 0x%x\n", 310187831Ssam __func__, cc, regDmn); 311185377Ssam } 312185377Ssam 313185377Ssam /* 314187831Ssam * Setup per-band state. 315185377Ssam */ 316187831Ssam if ((regDmn & MULTI_DOMAIN_MASK) == 0) { 317187831Ssam REG_DMN_PAIR_MAPPING *regpair = findRegDmnPair(regDmn); 318187831Ssam if (regpair == AH_NULL) { 319187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 320187831Ssam "%s: no reg domain pair %u for country %u\n", 321187831Ssam __func__, regDmn, country->countryCode); 322187831Ssam return HAL_EINVAL; 323187831Ssam } 324187831Ssam rd5GHz = findRegDmn(regpair->regDmn5GHz); 325187831Ssam if (rd5GHz == AH_NULL) { 326187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 327187831Ssam "%s: no 5GHz reg domain %u for country %u\n", 328187831Ssam __func__, regpair->regDmn5GHz, country->countryCode); 329187831Ssam return HAL_EINVAL; 330187831Ssam } 331187831Ssam rd2GHz = findRegDmn(regpair->regDmn2GHz); 332187831Ssam if (rd2GHz == AH_NULL) { 333187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 334187831Ssam "%s: no 2GHz reg domain %u for country %u\n", 335187831Ssam __func__, regpair->regDmn2GHz, country->countryCode); 336187831Ssam return HAL_EINVAL; 337187831Ssam } 338185377Ssam } else { 339187831Ssam rd5GHz = rd2GHz = findRegDmn(regDmn); 340187831Ssam if (rd2GHz == AH_NULL) { 341187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 342187831Ssam "%s: no unitary reg domain %u for country %u\n", 343187831Ssam __func__, regDmn, country->countryCode); 344187831Ssam return HAL_EINVAL; 345185377Ssam } 346185377Ssam } 347187831Ssam if (pcountry != AH_NULL) 348187831Ssam *pcountry = country; 349187831Ssam *prd2GHz = rd2GHz; 350187831Ssam *prd5GHz = rd5GHz; 351187831Ssam return HAL_OK; 352185377Ssam} 353185377Ssam 354185377Ssam/* 355187831Ssam * Construct the channel list for the specified regulatory config. 356185377Ssam */ 357187831Ssamstatic HAL_STATUS 358187831Ssamgetchannels(struct ath_hal *ah, 359187831Ssam struct ieee80211_channel chans[], u_int maxchans, int *nchans, 360187831Ssam u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 361187831Ssam HAL_BOOL enableExtendedChannels, 362187831Ssam COUNTRY_CODE_TO_ENUM_RD **pcountry, 363187831Ssam REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz) 364185377Ssam{ 365185377Ssam#define CHANNEL_HALF_BW 10 366185377Ssam#define CHANNEL_QUARTER_BW 5 367187831Ssam#define HAL_MODE_11A_ALL \ 368187831Ssam (HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \ 369187831Ssam HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE) 370187831Ssam REG_DOMAIN *rd5GHz, *rd2GHz; 371185377Ssam u_int modesAvail; 372185377Ssam const struct cmode *cm; 373187831Ssam struct ieee80211_channel *ic; 374185377Ssam int next, b; 375187831Ssam HAL_STATUS status; 376185377Ssam 377187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u regDmn 0x%x mode 0x%x%s\n", 378187831Ssam __func__, cc, regDmn, modeSelect, 379187831Ssam enableExtendedChannels ? " ecm" : ""); 380185377Ssam 381187831Ssam status = getregstate(ah, cc, regDmn, pcountry, &rd2GHz, &rd5GHz); 382187831Ssam if (status != HAL_OK) 383187831Ssam return status; 384185377Ssam 385187831Ssam /* get modes that HW is capable of */ 386187831Ssam modesAvail = ath_hal_getWirelessModes(ah); 387187831Ssam /* optimize work below if no 11a channels */ 388187831Ssam if (isChanBitMaskZero(rd5GHz->chan11a) && 389187831Ssam (modesAvail & HAL_MODE_11A_ALL)) { 390185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 391187831Ssam "%s: disallow all 11a\n", __func__); 392187831Ssam modesAvail &= ~HAL_MODE_11A_ALL; 393185377Ssam } 394185377Ssam 395185377Ssam next = 0; 396187831Ssam ic = &chans[0]; 397185377Ssam for (cm = modes; cm < &modes[N(modes)]; cm++) { 398185377Ssam uint16_t c, c_hi, c_lo; 399185377Ssam uint64_t *channelBM = AH_NULL; 400185377Ssam REG_DMN_FREQ_BAND *fband = AH_NULL,*freqs; 401185377Ssam int low_adj, hi_adj, channelSep, lastc; 402187831Ssam uint32_t rdflags; 403187831Ssam uint64_t dfsMask; 404187831Ssam uint64_t pscan; 405185377Ssam 406185377Ssam if ((cm->mode & modeSelect) == 0) { 407185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 408185377Ssam "%s: skip mode 0x%x flags 0x%x\n", 409185377Ssam __func__, cm->mode, cm->flags); 410185377Ssam continue; 411185377Ssam } 412185377Ssam if ((cm->mode & modesAvail) == 0) { 413185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 414185377Ssam "%s: !avail mode 0x%x (0x%x) flags 0x%x\n", 415185377Ssam __func__, modesAvail, cm->mode, cm->flags); 416185377Ssam continue; 417185377Ssam } 418185377Ssam if (!ath_hal_getChannelEdges(ah, cm->flags, &c_lo, &c_hi)) { 419185377Ssam /* channel not supported by hardware, skip it */ 420185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 421185377Ssam "%s: channels 0x%x not supported by hardware\n", 422185377Ssam __func__,cm->flags); 423185377Ssam continue; 424185377Ssam } 425185377Ssam switch (cm->mode) { 426185377Ssam case HAL_MODE_TURBO: 427187831Ssam case HAL_MODE_11A_TURBO: 428187831Ssam rdflags = rd5GHz->flags; 429187831Ssam dfsMask = rd5GHz->dfsMask; 430187831Ssam pscan = rd5GHz->pscan; 431187831Ssam if (cm->mode == HAL_MODE_TURBO) 432187831Ssam channelBM = rd5GHz->chan11a_turbo; 433187831Ssam else 434187831Ssam channelBM = rd5GHz->chan11a_dyn_turbo; 435185377Ssam freqs = ®Dmn5GhzTurboFreq[0]; 436185377Ssam break; 437187831Ssam case HAL_MODE_11G_TURBO: 438187831Ssam rdflags = rd2GHz->flags; 439187831Ssam dfsMask = rd2GHz->dfsMask; 440187831Ssam pscan = rd2GHz->pscan; 441187831Ssam channelBM = rd2GHz->chan11g_turbo; 442187831Ssam freqs = ®Dmn2Ghz11gTurboFreq[0]; 443187831Ssam break; 444185377Ssam case HAL_MODE_11A: 445185380Ssam case HAL_MODE_11A_HALF_RATE: 446185380Ssam case HAL_MODE_11A_QUARTER_RATE: 447185377Ssam case HAL_MODE_11NA_HT20: 448185377Ssam case HAL_MODE_11NA_HT40PLUS: 449185377Ssam case HAL_MODE_11NA_HT40MINUS: 450187831Ssam rdflags = rd5GHz->flags; 451187831Ssam dfsMask = rd5GHz->dfsMask; 452187831Ssam pscan = rd5GHz->pscan; 453185380Ssam if (cm->mode == HAL_MODE_11A_HALF_RATE) 454187831Ssam channelBM = rd5GHz->chan11a_half; 455185380Ssam else if (cm->mode == HAL_MODE_11A_QUARTER_RATE) 456187831Ssam channelBM = rd5GHz->chan11a_quarter; 457185380Ssam else 458187831Ssam channelBM = rd5GHz->chan11a; 459185377Ssam freqs = ®Dmn5GhzFreq[0]; 460185377Ssam break; 461185377Ssam case HAL_MODE_11B: 462185377Ssam case HAL_MODE_11G: 463185380Ssam case HAL_MODE_11G_HALF_RATE: 464185380Ssam case HAL_MODE_11G_QUARTER_RATE: 465185377Ssam case HAL_MODE_11NG_HT20: 466185377Ssam case HAL_MODE_11NG_HT40PLUS: 467185377Ssam case HAL_MODE_11NG_HT40MINUS: 468187831Ssam rdflags = rd2GHz->flags; 469187831Ssam dfsMask = rd2GHz->dfsMask; 470187831Ssam pscan = rd2GHz->pscan; 471185380Ssam if (cm->mode == HAL_MODE_11G_HALF_RATE) 472187831Ssam channelBM = rd2GHz->chan11g_half; 473185380Ssam else if (cm->mode == HAL_MODE_11G_QUARTER_RATE) 474187831Ssam channelBM = rd2GHz->chan11g_quarter; 475187831Ssam else if (cm->mode == HAL_MODE_11B) 476187831Ssam channelBM = rd2GHz->chan11b; 477185380Ssam else 478187831Ssam channelBM = rd2GHz->chan11g; 479187831Ssam if (cm->mode == HAL_MODE_11B) 480187831Ssam freqs = ®Dmn2GhzFreq[0]; 481187831Ssam else 482187831Ssam freqs = ®Dmn2Ghz11gFreq[0]; 483185377Ssam break; 484185377Ssam default: 485185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 486185377Ssam "%s: Unkonwn HAL mode 0x%x\n", __func__, cm->mode); 487185377Ssam continue; 488185377Ssam } 489185377Ssam if (isChanBitMaskZero(channelBM)) 490185377Ssam continue; 491185377Ssam /* 492185377Ssam * Setup special handling for HT40 channels; e.g. 493185377Ssam * 5G HT40 channels require 40Mhz channel separation. 494185377Ssam */ 495185377Ssam hi_adj = (cm->mode == HAL_MODE_11NA_HT40PLUS || 496185377Ssam cm->mode == HAL_MODE_11NG_HT40PLUS) ? -20 : 0; 497185377Ssam low_adj = (cm->mode == HAL_MODE_11NA_HT40MINUS || 498185377Ssam cm->mode == HAL_MODE_11NG_HT40MINUS) ? 20 : 0; 499185377Ssam channelSep = (cm->mode == HAL_MODE_11NA_HT40PLUS || 500185377Ssam cm->mode == HAL_MODE_11NA_HT40MINUS) ? 40 : 0; 501185377Ssam 502185377Ssam for (b = 0; b < 64*BMLEN; b++) { 503185377Ssam if (!IS_BIT_SET(b, channelBM)) 504185377Ssam continue; 505185377Ssam fband = &freqs[b]; 506185377Ssam lastc = 0; 507185377Ssam 508185377Ssam for (c = fband->lowChannel + low_adj; 509185377Ssam c <= fband->highChannel + hi_adj; 510185377Ssam c += fband->channelSep) { 511185377Ssam if (!(c_lo <= c && c <= c_hi)) { 512185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 513185377Ssam "%s: c %u out of range [%u..%u]\n", 514185377Ssam __func__, c, c_lo, c_hi); 515185377Ssam continue; 516185377Ssam } 517185377Ssam if (next >= maxchans){ 518185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 519185377Ssam "%s: too many channels for channel table\n", 520185377Ssam __func__); 521185377Ssam goto done; 522185377Ssam } 523185377Ssam if ((fband->usePassScan & IS_ECM_CHAN) && 524185377Ssam !enableExtendedChannels) { 525185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 526187831Ssam "skip ecm channel\n"); 527185377Ssam continue; 528185377Ssam } 529187831Ssam if ((fband->useDfs & dfsMask) && 530187831Ssam (cm->flags & IEEE80211_CHAN_HT40)) { 531187831Ssam /* NB: DFS and HT40 don't mix */ 532185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 533187831Ssam "skip HT40 chan, DFS required\n"); 534185377Ssam continue; 535185377Ssam } 536185377Ssam /* 537185377Ssam * Make sure that channel separation 538185377Ssam * meets the requirement. 539185377Ssam */ 540185377Ssam if (lastc && channelSep && 541185377Ssam (c-lastc) < channelSep) 542185377Ssam continue; 543185377Ssam lastc = c; 544185377Ssam 545187831Ssam OS_MEMZERO(ic, sizeof(*ic)); 546187831Ssam ic->ic_freq = c; 547187831Ssam ic->ic_flags = cm->flags; 548187831Ssam ic->ic_maxregpower = fband->powerDfs; 549187831Ssam ath_hal_getpowerlimits(ah, ic); 550187831Ssam ic->ic_maxantgain = fband->antennaMax; 551187831Ssam if (fband->usePassScan & pscan) 552187831Ssam ic->ic_flags |= IEEE80211_CHAN_PASSIVE; 553187831Ssam if (fband->useDfs & dfsMask) 554187831Ssam ic->ic_flags |= IEEE80211_CHAN_DFS; 555187831Ssam if (IEEE80211_IS_CHAN_5GHZ(ic) && 556187831Ssam (rdflags & DISALLOW_ADHOC_11A)) 557187831Ssam ic->ic_flags |= IEEE80211_CHAN_NOADHOC; 558187831Ssam if (IEEE80211_IS_CHAN_TURBO(ic) && 559187831Ssam (rdflags & DISALLOW_ADHOC_11A_TURB)) 560187831Ssam ic->ic_flags |= IEEE80211_CHAN_NOADHOC; 561187831Ssam if (rdflags & NO_HOSTAP) 562187831Ssam ic->ic_flags |= IEEE80211_CHAN_NOHOSTAP; 563187831Ssam if (rdflags & LIMIT_FRAME_4MS) 564187831Ssam ic->ic_flags |= IEEE80211_CHAN_4MSXMIT; 565187831Ssam if (rdflags & NEED_NFC) 566187831Ssam ic->ic_flags |= CHANNEL_NFCREQUIRED; 567187831Ssam 568187831Ssam ic++, next++; 569185377Ssam } 570185377Ssam } 571185377Ssam } 572185377Ssamdone: 573185377Ssam *nchans = next; 574187831Ssam /* NB: pcountry set above by getregstate */ 575187831Ssam if (prd2GHz != AH_NULL) 576187831Ssam *prd2GHz = rd2GHz; 577187831Ssam if (prd5GHz != AH_NULL) 578187831Ssam *prd5GHz = rd5GHz; 579187831Ssam return HAL_OK; 580187831Ssam#undef HAL_MODE_11A_ALL 581185377Ssam#undef CHANNEL_HALF_BW 582185377Ssam#undef CHANNEL_QUARTER_BW 583185377Ssam} 584185377Ssam 585185377Ssam/* 586187831Ssam * Retrieve a channel list without affecting runtime state. 587185377Ssam */ 588187831SsamHAL_STATUS 589187831Ssamath_hal_getchannels(struct ath_hal *ah, 590187831Ssam struct ieee80211_channel chans[], u_int maxchans, int *nchans, 591187831Ssam u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 592187831Ssam HAL_BOOL enableExtendedChannels) 593185377Ssam{ 594187831Ssam return getchannels(ah, chans, maxchans, nchans, modeSelect, 595187831Ssam cc, regDmn, enableExtendedChannels, AH_NULL, AH_NULL, AH_NULL); 596187831Ssam} 597185377Ssam 598187831Ssam/* 599187831Ssam * Handle frequency mapping from 900Mhz range to 2.4GHz range 600187831Ssam * for GSM radios. This is done when we need the h/w frequency 601187831Ssam * and the channel is marked IEEE80211_CHAN_GSM. 602187831Ssam */ 603187831Ssamstatic int 604187831Ssamath_hal_mapgsm(int sku, int freq) 605187831Ssam{ 606187831Ssam if (sku == SKU_XR9) 607187831Ssam return 1520 + freq; 608187831Ssam if (sku == SKU_GZ901) 609187831Ssam return 1544 + freq; 610187831Ssam if (sku == SKU_SR9) 611187831Ssam return 3344 - freq; 612187831Ssam HALDEBUG(AH_NULL, HAL_DEBUG_ANY, 613187831Ssam "%s: cannot map freq %u unknown gsm sku %u\n", 614187831Ssam __func__, freq, sku); 615187831Ssam return freq; 616187831Ssam} 617185377Ssam 618187831Ssam/* 619187831Ssam * Setup the internal/private channel state given a table of 620187831Ssam * net80211 channels. We collapse entries for the same frequency 621187831Ssam * and record the frequency for doing noise floor processing 622187831Ssam * where we don't have net80211 channel context. 623187831Ssam */ 624187831Ssamstatic HAL_BOOL 625187831SsamassignPrivateChannels(struct ath_hal *ah, 626187831Ssam struct ieee80211_channel chans[], int nchans, int sku) 627187831Ssam{ 628187831Ssam HAL_CHANNEL_INTERNAL *ic; 629187831Ssam int i, j, next, freq; 630187831Ssam 631187831Ssam next = 0; 632187831Ssam for (i = 0; i < nchans; i++) { 633187831Ssam struct ieee80211_channel *c = &chans[i]; 634187831Ssam for (j = i-1; j >= 0; j--) 635187831Ssam if (chans[j].ic_freq == c->ic_freq) { 636187831Ssam c->ic_devdata = chans[j].ic_devdata; 637187831Ssam break; 638185377Ssam } 639187831Ssam if (j < 0) { 640187831Ssam /* new entry, assign a private channel entry */ 641187831Ssam if (next >= N(AH_PRIVATE(ah)->ah_channels)) { 642187831Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 643188084Ssam "%s: too many channels, max %zu\n", 644187831Ssam __func__, N(AH_PRIVATE(ah)->ah_channels)); 645187831Ssam return AH_FALSE; 646187831Ssam } 647187831Ssam /* 648187831Ssam * Handle frequency mapping for 900MHz devices. 649187831Ssam * The hardware uses 2.4GHz frequencies that are 650187831Ssam * down-converted. The 802.11 layer uses the 651187831Ssam * true frequencies. 652187831Ssam */ 653187831Ssam freq = IEEE80211_IS_CHAN_GSM(c) ? 654187831Ssam ath_hal_mapgsm(sku, c->ic_freq) : c->ic_freq; 655187831Ssam 656187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 657187831Ssam "%s: private[%3u] %u/0x%x -> channel %u\n", 658187831Ssam __func__, next, c->ic_freq, c->ic_flags, freq); 659187831Ssam 660187831Ssam ic = &AH_PRIVATE(ah)->ah_channels[next]; 661187831Ssam /* 662187831Ssam * NB: This clears privFlags which means ancillary 663187831Ssam * code like ANI and IQ calibration will be 664187831Ssam * restarted and re-setup any per-channel state. 665187831Ssam */ 666187831Ssam OS_MEMZERO(ic, sizeof(*ic)); 667187831Ssam ic->channel = freq; 668187831Ssam c->ic_devdata = next; 669187831Ssam next++; 670185377Ssam } 671185377Ssam } 672187831Ssam AH_PRIVATE(ah)->ah_nchan = next; 673187831Ssam HALDEBUG(ah, HAL_DEBUG_ANY, "%s: %u public, %u private channels\n", 674187831Ssam __func__, nchans, next); 675187831Ssam return AH_TRUE; 676185377Ssam} 677185377Ssam 678185377Ssam/* 679187831Ssam * Setup the channel list based on the information in the EEPROM. 680185377Ssam */ 681187831SsamHAL_STATUS 682187831Ssamath_hal_init_channels(struct ath_hal *ah, 683187831Ssam struct ieee80211_channel chans[], u_int maxchans, int *nchans, 684187831Ssam u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 685187831Ssam HAL_BOOL enableExtendedChannels) 686185377Ssam{ 687187831Ssam COUNTRY_CODE_TO_ENUM_RD *country; 688187831Ssam REG_DOMAIN *rd5GHz, *rd2GHz; 689187831Ssam HAL_STATUS status; 690185377Ssam 691187831Ssam status = getchannels(ah, chans, maxchans, nchans, modeSelect, 692187831Ssam cc, regDmn, enableExtendedChannels, &country, &rd2GHz, &rd5GHz); 693187831Ssam if (status == HAL_OK && 694187831Ssam assignPrivateChannels(ah, chans, *nchans, AH_PRIVATE(ah)->ah_currentRD)) { 695187831Ssam AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz; 696187831Ssam AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz; 697187831Ssam 698187831Ssam ah->ah_countryCode = country->countryCode; 699187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n", 700187831Ssam __func__, ah->ah_countryCode); 701187831Ssam } else 702187831Ssam status = HAL_EINVAL; 703187831Ssam return status; 704185377Ssam} 705185377Ssam 706187831Ssam/* 707187831Ssam * Set the channel list. 708187831Ssam */ 709187831SsamHAL_STATUS 710187831Ssamath_hal_set_channels(struct ath_hal *ah, 711187831Ssam struct ieee80211_channel chans[], int nchans, 712187831Ssam HAL_CTRY_CODE cc, HAL_REG_DOMAIN rd) 713187831Ssam{ 714187831Ssam COUNTRY_CODE_TO_ENUM_RD *country; 715187831Ssam REG_DOMAIN *rd5GHz, *rd2GHz; 716187831Ssam HAL_STATUS status; 717185377Ssam 718187831Ssam switch (rd) { 719187831Ssam case SKU_SR9: 720187831Ssam case SKU_XR9: 721187831Ssam case SKU_GZ901: 722187831Ssam /* 723187831Ssam * Map 900MHz sku's. The frequencies will be mapped 724187831Ssam * according to the sku to compensate for the down-converter. 725187831Ssam * We use the FCC for these sku's as the mapped channel 726187831Ssam * list is known compatible (will need to change if/when 727187831Ssam * vendors do different mapping in different locales). 728187831Ssam */ 729187831Ssam status = getregstate(ah, CTRY_DEFAULT, SKU_FCC, 730187831Ssam &country, &rd2GHz, &rd5GHz); 731187831Ssam break; 732187831Ssam default: 733187831Ssam status = getregstate(ah, cc, rd, 734187831Ssam &country, &rd2GHz, &rd5GHz); 735187831Ssam rd = AH_PRIVATE(ah)->ah_currentRD; 736187831Ssam break; 737187831Ssam } 738187831Ssam if (status == HAL_OK && assignPrivateChannels(ah, chans, nchans, rd)) { 739187831Ssam AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz; 740187831Ssam AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz; 741185377Ssam 742187831Ssam ah->ah_countryCode = country->countryCode; 743187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n", 744187831Ssam __func__, ah->ah_countryCode); 745187831Ssam } else 746187831Ssam status = HAL_EINVAL; 747187831Ssam return status; 748187831Ssam} 749185377Ssam 750187831Ssam#ifdef AH_DEBUG 751185377Ssam/* 752187831Ssam * Return the internal channel corresponding to a public channel. 753187831Ssam * NB: normally this routine is inline'd (see ah_internal.h) 754185377Ssam */ 755187831SsamHAL_CHANNEL_INTERNAL * 756187831Ssamath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c) 757185377Ssam{ 758187831Ssam HAL_CHANNEL_INTERNAL *cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata]; 759185377Ssam 760187831Ssam if (c->ic_devdata < AH_PRIVATE(ah)->ah_nchan && 761187831Ssam (c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c))) 762187831Ssam return cc; 763187831Ssam if (c->ic_devdata >= AH_PRIVATE(ah)->ah_nchan) { 764187831Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 765187831Ssam "%s: bad mapping, devdata %u nchans %u\n", 766187831Ssam __func__, c->ic_devdata, AH_PRIVATE(ah)->ah_nchan); 767187831Ssam HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan); 768185377Ssam } else { 769187831Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 770187831Ssam "%s: no match for %u/0x%x devdata %u channel %u\n", 771187831Ssam __func__, c->ic_freq, c->ic_flags, c->ic_devdata, 772187831Ssam cc->channel); 773187831Ssam HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)); 774185377Ssam } 775187831Ssam return AH_NULL; 776185377Ssam} 777187831Ssam#endif /* AH_DEBUG */ 778185377Ssam 779187831Ssam#define isWwrSKU(_ah) \ 780187831Ssam ((getEepromRD((_ah)) & WORLD_SKU_MASK) == WORLD_SKU_PREFIX || \ 781187831Ssam getEepromRD(_ah) == WORLD) 782187831Ssam 783185377Ssam/* 784187831Ssam * Return the test group for the specific channel based on 785187831Ssam * the current regulatory setup. 786185377Ssam */ 787187831Ssamu_int 788187831Ssamath_hal_getctl(struct ath_hal *ah, const struct ieee80211_channel *c) 789185377Ssam{ 790187831Ssam u_int ctl; 791185377Ssam 792187831Ssam if (AH_PRIVATE(ah)->ah_rd2GHz == AH_PRIVATE(ah)->ah_rd5GHz || 793187831Ssam (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah))) 794187831Ssam ctl = SD_NO_CTL; 795187831Ssam else if (IEEE80211_IS_CHAN_2GHZ(c)) 796187831Ssam ctl = AH_PRIVATE(ah)->ah_rd2GHz->conformanceTestLimit; 797187831Ssam else 798187831Ssam ctl = AH_PRIVATE(ah)->ah_rd5GHz->conformanceTestLimit; 799187831Ssam if (IEEE80211_IS_CHAN_B(c)) 800187831Ssam return ctl | CTL_11B; 801187831Ssam if (IEEE80211_IS_CHAN_G(c)) 802187831Ssam return ctl | CTL_11G; 803187831Ssam if (IEEE80211_IS_CHAN_108G(c)) 804187831Ssam return ctl | CTL_108G; 805187831Ssam if (IEEE80211_IS_CHAN_TURBO(c)) 806187831Ssam return ctl | CTL_TURBO; 807187831Ssam if (IEEE80211_IS_CHAN_A(c)) 808187831Ssam return ctl | CTL_11A; 809187831Ssam return ctl; 810185377Ssam} 811185377Ssam 812185377Ssam/* 813187831Ssam * Return the max allowed antenna gain and apply any regulatory 814187831Ssam * domain specific changes. 815187831Ssam * 816187831Ssam * NOTE: a negative reduction is possible in RD's that only 817187831Ssam * measure radiated power (e.g., ETSI) which would increase 818187831Ssam * that actual conducted output power (though never beyond 819187831Ssam * the calibrated target power). 820185377Ssam */ 821187831Ssamu_int 822187831Ssamath_hal_getantennareduction(struct ath_hal *ah, 823187831Ssam const struct ieee80211_channel *chan, u_int twiceGain) 824185377Ssam{ 825187831Ssam int8_t antennaMax = twiceGain - chan->ic_maxantgain*2; 826187831Ssam return (antennaMax < 0) ? 0 : antennaMax; 827185377Ssam} 828