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$ 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 128224718Sadrianstatic void ath_hal_update_dfsdomain(struct ath_hal *ah); 129224718Sadrian 130187831Ssamstatic OS_INLINE uint16_t 131185377SsamgetEepromRD(struct ath_hal *ah) 132185377Ssam{ 133185377Ssam return AH_PRIVATE(ah)->ah_currentRD &~ WORLDWIDE_ROAMING_FLAG; 134185377Ssam} 135185377Ssam 136185377Ssam/* 137185377Ssam * Test to see if the bitmask array is all zeros 138185377Ssam */ 139185377Ssamstatic HAL_BOOL 140185380SsamisChanBitMaskZero(const uint64_t *bitmask) 141185377Ssam{ 142185377Ssam#if BMLEN > 2 143185377Ssam#error "add more cases" 144185377Ssam#endif 145185377Ssam#if BMLEN > 1 146185377Ssam if (bitmask[1] != 0) 147185377Ssam return AH_FALSE; 148185377Ssam#endif 149185377Ssam return (bitmask[0] == 0); 150185377Ssam} 151185377Ssam 152185377Ssam/* 153185377Ssam * Return whether or not the regulatory domain/country in EEPROM 154185377Ssam * is acceptable. 155185377Ssam */ 156185377Ssamstatic HAL_BOOL 157185377SsamisEepromValid(struct ath_hal *ah) 158185377Ssam{ 159185377Ssam uint16_t rd = getEepromRD(ah); 160185377Ssam int i; 161185377Ssam 162185377Ssam if (rd & COUNTRY_ERD_FLAG) { 163185377Ssam uint16_t cc = rd &~ COUNTRY_ERD_FLAG; 164185377Ssam for (i = 0; i < N(allCountries); i++) 165185377Ssam if (allCountries[i].countryCode == cc) 166185377Ssam return AH_TRUE; 167185377Ssam } else { 168185377Ssam for (i = 0; i < N(regDomainPairs); i++) 169185377Ssam if (regDomainPairs[i].regDmnEnum == rd) 170185377Ssam return AH_TRUE; 171185377Ssam } 172225883Sadrian HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 173185377Ssam "%s: invalid regulatory domain/country code 0x%x\n", __func__, rd); 174185377Ssam return AH_FALSE; 175185377Ssam} 176185377Ssam 177185377Ssam/* 178187831Ssam * Find the pointer to the country element in the country table 179187831Ssam * corresponding to the country code 180185377Ssam */ 181187831Ssamstatic COUNTRY_CODE_TO_ENUM_RD* 182187831SsamfindCountry(HAL_CTRY_CODE countryCode) 183185377Ssam{ 184187831Ssam int i; 185185377Ssam 186187831Ssam for (i = 0; i < N(allCountries); i++) { 187187831Ssam if (allCountries[i].countryCode == countryCode) 188187831Ssam return &allCountries[i]; 189185377Ssam } 190187831Ssam return AH_NULL; 191185377Ssam} 192185377Ssam 193187831Ssamstatic REG_DOMAIN * 194187831SsamfindRegDmn(int regDmn) 195185377Ssam{ 196187831Ssam int i; 197185377Ssam 198187831Ssam for (i = 0; i < N(regDomains); i++) { 199187831Ssam if (regDomains[i].regDmnEnum == regDmn) 200187831Ssam return ®Domains[i]; 201185380Ssam } 202187831Ssam return AH_NULL; 203185377Ssam} 204185377Ssam 205187831Ssamstatic REG_DMN_PAIR_MAPPING * 206187831SsamfindRegDmnPair(int regDmnPair) 207185377Ssam{ 208185377Ssam int i; 209185377Ssam 210187831Ssam if (regDmnPair != NO_ENUMRD) { 211187831Ssam for (i = 0; i < N(regDomainPairs); i++) { 212187831Ssam if (regDomainPairs[i].regDmnEnum == regDmnPair) 213187831Ssam return ®DomainPairs[i]; 214187831Ssam } 215185377Ssam } 216187831Ssam return AH_NULL; 217185377Ssam} 218185377Ssam 219185377Ssam/* 220185377Ssam * Calculate a default country based on the EEPROM setting. 221185377Ssam */ 222185377Ssamstatic HAL_CTRY_CODE 223185377SsamgetDefaultCountry(struct ath_hal *ah) 224185377Ssam{ 225187831Ssam REG_DMN_PAIR_MAPPING *regpair; 226185377Ssam uint16_t rd; 227185377Ssam 228185377Ssam rd = getEepromRD(ah); 229185377Ssam if (rd & COUNTRY_ERD_FLAG) { 230187831Ssam COUNTRY_CODE_TO_ENUM_RD *country; 231185377Ssam uint16_t cc = rd & ~COUNTRY_ERD_FLAG; 232185377Ssam country = findCountry(cc); 233185377Ssam if (country != AH_NULL) 234185377Ssam return cc; 235185377Ssam } 236185377Ssam /* 237185377Ssam * Check reg domains that have only one country 238185377Ssam */ 239187831Ssam regpair = findRegDmnPair(rd); 240187831Ssam return (regpair != AH_NULL) ? regpair->singleCC : CTRY_DEFAULT; 241185377Ssam} 242185377Ssam 243185377Ssamstatic HAL_BOOL 244187831SsamIS_BIT_SET(int bit, const uint64_t bitmask[]) 245185377Ssam{ 246187831Ssam int byteOffset, bitnum; 247187831Ssam uint64_t val; 248185377Ssam 249187831Ssam byteOffset = bit/64; 250187831Ssam bitnum = bit - byteOffset*64; 251187831Ssam val = ((uint64_t) 1) << bitnum; 252187831Ssam return (bitmask[byteOffset] & val) != 0; 253185377Ssam} 254185377Ssam 255187831Ssamstatic HAL_STATUS 256187831Ssamgetregstate(struct ath_hal *ah, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 257187831Ssam COUNTRY_CODE_TO_ENUM_RD **pcountry, 258187831Ssam REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz) 259185377Ssam{ 260187831Ssam COUNTRY_CODE_TO_ENUM_RD *country; 261187831Ssam REG_DOMAIN *rd5GHz, *rd2GHz; 262185377Ssam 263187831Ssam if (cc == CTRY_DEFAULT && regDmn == SKU_NONE) { 264187831Ssam /* 265187831Ssam * Validate the EEPROM setting and setup defaults 266187831Ssam */ 267187831Ssam if (!isEepromValid(ah)) { 268187831Ssam /* 269187831Ssam * Don't return any channels if the EEPROM has an 270187831Ssam * invalid regulatory domain/country code setting. 271187831Ssam */ 272187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 273187831Ssam "%s: invalid EEPROM contents\n",__func__); 274187831Ssam return HAL_EEBADREG; 275187831Ssam } 276185377Ssam 277187831Ssam cc = getDefaultCountry(ah); 278187831Ssam country = findCountry(cc); 279187831Ssam if (country == AH_NULL) { 280187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 281187831Ssam "NULL Country!, cc %d\n", cc); 282187831Ssam return HAL_EEBADCC; 283187831Ssam } 284187831Ssam regDmn = country->regDmnEnum; 285187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: EEPROM cc %u rd 0x%x\n", 286187831Ssam __func__, cc, regDmn); 287185377Ssam 288187831Ssam if (country->countryCode == CTRY_DEFAULT) { 289187831Ssam /* 290187831Ssam * Check EEPROM; SKU may be for a country, single 291187831Ssam * domain, or multiple domains (WWR). 292187831Ssam */ 293187831Ssam uint16_t rdnum = getEepromRD(ah); 294187831Ssam if ((rdnum & COUNTRY_ERD_FLAG) == 0 && 295187831Ssam (findRegDmn(rdnum) != AH_NULL || 296187831Ssam findRegDmnPair(rdnum) != AH_NULL)) { 297185377Ssam regDmn = rdnum; 298187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 299187831Ssam "%s: EEPROM rd 0x%x\n", __func__, rdnum); 300185377Ssam } 301185377Ssam } 302187831Ssam } else { 303187831Ssam country = findCountry(cc); 304187831Ssam if (country == AH_NULL) { 305185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 306187831Ssam "unknown country, cc %d\n", cc); 307187831Ssam return HAL_EINVAL; 308185377Ssam } 309187831Ssam if (regDmn == SKU_NONE) 310187831Ssam regDmn = country->regDmnEnum; 311187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u rd 0x%x\n", 312187831Ssam __func__, cc, regDmn); 313185377Ssam } 314185377Ssam 315185377Ssam /* 316187831Ssam * Setup per-band state. 317185377Ssam */ 318187831Ssam if ((regDmn & MULTI_DOMAIN_MASK) == 0) { 319187831Ssam REG_DMN_PAIR_MAPPING *regpair = findRegDmnPair(regDmn); 320187831Ssam if (regpair == AH_NULL) { 321187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 322187831Ssam "%s: no reg domain pair %u for country %u\n", 323187831Ssam __func__, regDmn, country->countryCode); 324187831Ssam return HAL_EINVAL; 325187831Ssam } 326187831Ssam rd5GHz = findRegDmn(regpair->regDmn5GHz); 327187831Ssam if (rd5GHz == AH_NULL) { 328187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 329187831Ssam "%s: no 5GHz reg domain %u for country %u\n", 330187831Ssam __func__, regpair->regDmn5GHz, country->countryCode); 331187831Ssam return HAL_EINVAL; 332187831Ssam } 333187831Ssam rd2GHz = findRegDmn(regpair->regDmn2GHz); 334187831Ssam if (rd2GHz == AH_NULL) { 335187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 336187831Ssam "%s: no 2GHz reg domain %u for country %u\n", 337187831Ssam __func__, regpair->regDmn2GHz, country->countryCode); 338187831Ssam return HAL_EINVAL; 339187831Ssam } 340185377Ssam } else { 341187831Ssam rd5GHz = rd2GHz = findRegDmn(regDmn); 342187831Ssam if (rd2GHz == AH_NULL) { 343187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 344187831Ssam "%s: no unitary reg domain %u for country %u\n", 345187831Ssam __func__, regDmn, country->countryCode); 346187831Ssam return HAL_EINVAL; 347185377Ssam } 348185377Ssam } 349187831Ssam if (pcountry != AH_NULL) 350187831Ssam *pcountry = country; 351187831Ssam *prd2GHz = rd2GHz; 352187831Ssam *prd5GHz = rd5GHz; 353187831Ssam return HAL_OK; 354185377Ssam} 355185377Ssam 356185377Ssam/* 357187831Ssam * Construct the channel list for the specified regulatory config. 358185377Ssam */ 359187831Ssamstatic HAL_STATUS 360187831Ssamgetchannels(struct ath_hal *ah, 361187831Ssam struct ieee80211_channel chans[], u_int maxchans, int *nchans, 362187831Ssam u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 363187831Ssam HAL_BOOL enableExtendedChannels, 364187831Ssam COUNTRY_CODE_TO_ENUM_RD **pcountry, 365187831Ssam REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz) 366185377Ssam{ 367185377Ssam#define CHANNEL_HALF_BW 10 368185377Ssam#define CHANNEL_QUARTER_BW 5 369187831Ssam#define HAL_MODE_11A_ALL \ 370187831Ssam (HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \ 371187831Ssam HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE) 372187831Ssam REG_DOMAIN *rd5GHz, *rd2GHz; 373185377Ssam u_int modesAvail; 374185377Ssam const struct cmode *cm; 375187831Ssam struct ieee80211_channel *ic; 376185377Ssam int next, b; 377187831Ssam HAL_STATUS status; 378185377Ssam 379187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u regDmn 0x%x mode 0x%x%s\n", 380187831Ssam __func__, cc, regDmn, modeSelect, 381187831Ssam enableExtendedChannels ? " ecm" : ""); 382185377Ssam 383187831Ssam status = getregstate(ah, cc, regDmn, pcountry, &rd2GHz, &rd5GHz); 384187831Ssam if (status != HAL_OK) 385187831Ssam return status; 386185377Ssam 387187831Ssam /* get modes that HW is capable of */ 388187831Ssam modesAvail = ath_hal_getWirelessModes(ah); 389187831Ssam /* optimize work below if no 11a channels */ 390187831Ssam if (isChanBitMaskZero(rd5GHz->chan11a) && 391187831Ssam (modesAvail & HAL_MODE_11A_ALL)) { 392185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 393187831Ssam "%s: disallow all 11a\n", __func__); 394187831Ssam modesAvail &= ~HAL_MODE_11A_ALL; 395185377Ssam } 396185377Ssam 397185377Ssam next = 0; 398187831Ssam ic = &chans[0]; 399185377Ssam for (cm = modes; cm < &modes[N(modes)]; cm++) { 400185377Ssam uint16_t c, c_hi, c_lo; 401185377Ssam uint64_t *channelBM = AH_NULL; 402185377Ssam REG_DMN_FREQ_BAND *fband = AH_NULL,*freqs; 403185377Ssam int low_adj, hi_adj, channelSep, lastc; 404187831Ssam uint32_t rdflags; 405187831Ssam uint64_t dfsMask; 406187831Ssam uint64_t pscan; 407185377Ssam 408185377Ssam if ((cm->mode & modeSelect) == 0) { 409185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 410185377Ssam "%s: skip mode 0x%x flags 0x%x\n", 411185377Ssam __func__, cm->mode, cm->flags); 412185377Ssam continue; 413185377Ssam } 414185377Ssam if ((cm->mode & modesAvail) == 0) { 415185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 416185377Ssam "%s: !avail mode 0x%x (0x%x) flags 0x%x\n", 417185377Ssam __func__, modesAvail, cm->mode, cm->flags); 418185377Ssam continue; 419185377Ssam } 420185377Ssam if (!ath_hal_getChannelEdges(ah, cm->flags, &c_lo, &c_hi)) { 421185377Ssam /* channel not supported by hardware, skip it */ 422185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 423185377Ssam "%s: channels 0x%x not supported by hardware\n", 424185377Ssam __func__,cm->flags); 425185377Ssam continue; 426185377Ssam } 427185377Ssam switch (cm->mode) { 428185377Ssam case HAL_MODE_TURBO: 429187831Ssam case HAL_MODE_11A_TURBO: 430187831Ssam rdflags = rd5GHz->flags; 431187831Ssam dfsMask = rd5GHz->dfsMask; 432187831Ssam pscan = rd5GHz->pscan; 433187831Ssam if (cm->mode == HAL_MODE_TURBO) 434187831Ssam channelBM = rd5GHz->chan11a_turbo; 435187831Ssam else 436187831Ssam channelBM = rd5GHz->chan11a_dyn_turbo; 437185377Ssam freqs = ®Dmn5GhzTurboFreq[0]; 438185377Ssam break; 439187831Ssam case HAL_MODE_11G_TURBO: 440187831Ssam rdflags = rd2GHz->flags; 441187831Ssam dfsMask = rd2GHz->dfsMask; 442187831Ssam pscan = rd2GHz->pscan; 443187831Ssam channelBM = rd2GHz->chan11g_turbo; 444187831Ssam freqs = ®Dmn2Ghz11gTurboFreq[0]; 445187831Ssam break; 446185377Ssam case HAL_MODE_11A: 447185380Ssam case HAL_MODE_11A_HALF_RATE: 448185380Ssam case HAL_MODE_11A_QUARTER_RATE: 449185377Ssam case HAL_MODE_11NA_HT20: 450185377Ssam case HAL_MODE_11NA_HT40PLUS: 451185377Ssam case HAL_MODE_11NA_HT40MINUS: 452187831Ssam rdflags = rd5GHz->flags; 453187831Ssam dfsMask = rd5GHz->dfsMask; 454187831Ssam pscan = rd5GHz->pscan; 455185380Ssam if (cm->mode == HAL_MODE_11A_HALF_RATE) 456187831Ssam channelBM = rd5GHz->chan11a_half; 457185380Ssam else if (cm->mode == HAL_MODE_11A_QUARTER_RATE) 458187831Ssam channelBM = rd5GHz->chan11a_quarter; 459185380Ssam else 460187831Ssam channelBM = rd5GHz->chan11a; 461185377Ssam freqs = ®Dmn5GhzFreq[0]; 462185377Ssam break; 463185377Ssam case HAL_MODE_11B: 464185377Ssam case HAL_MODE_11G: 465185380Ssam case HAL_MODE_11G_HALF_RATE: 466185380Ssam case HAL_MODE_11G_QUARTER_RATE: 467185377Ssam case HAL_MODE_11NG_HT20: 468185377Ssam case HAL_MODE_11NG_HT40PLUS: 469185377Ssam case HAL_MODE_11NG_HT40MINUS: 470187831Ssam rdflags = rd2GHz->flags; 471187831Ssam dfsMask = rd2GHz->dfsMask; 472187831Ssam pscan = rd2GHz->pscan; 473185380Ssam if (cm->mode == HAL_MODE_11G_HALF_RATE) 474187831Ssam channelBM = rd2GHz->chan11g_half; 475185380Ssam else if (cm->mode == HAL_MODE_11G_QUARTER_RATE) 476187831Ssam channelBM = rd2GHz->chan11g_quarter; 477187831Ssam else if (cm->mode == HAL_MODE_11B) 478187831Ssam channelBM = rd2GHz->chan11b; 479185380Ssam else 480187831Ssam channelBM = rd2GHz->chan11g; 481187831Ssam if (cm->mode == HAL_MODE_11B) 482187831Ssam freqs = ®Dmn2GhzFreq[0]; 483187831Ssam else 484187831Ssam freqs = ®Dmn2Ghz11gFreq[0]; 485185377Ssam break; 486185377Ssam default: 487185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 488185377Ssam "%s: Unkonwn HAL mode 0x%x\n", __func__, cm->mode); 489185377Ssam continue; 490185377Ssam } 491185377Ssam if (isChanBitMaskZero(channelBM)) 492185377Ssam continue; 493185377Ssam /* 494185377Ssam * Setup special handling for HT40 channels; e.g. 495185377Ssam * 5G HT40 channels require 40Mhz channel separation. 496185377Ssam */ 497185377Ssam hi_adj = (cm->mode == HAL_MODE_11NA_HT40PLUS || 498185377Ssam cm->mode == HAL_MODE_11NG_HT40PLUS) ? -20 : 0; 499185377Ssam low_adj = (cm->mode == HAL_MODE_11NA_HT40MINUS || 500185377Ssam cm->mode == HAL_MODE_11NG_HT40MINUS) ? 20 : 0; 501185377Ssam channelSep = (cm->mode == HAL_MODE_11NA_HT40PLUS || 502185377Ssam cm->mode == HAL_MODE_11NA_HT40MINUS) ? 40 : 0; 503185377Ssam 504185377Ssam for (b = 0; b < 64*BMLEN; b++) { 505185377Ssam if (!IS_BIT_SET(b, channelBM)) 506185377Ssam continue; 507185377Ssam fband = &freqs[b]; 508185377Ssam lastc = 0; 509185377Ssam 510185377Ssam for (c = fband->lowChannel + low_adj; 511185377Ssam c <= fband->highChannel + hi_adj; 512185377Ssam c += fband->channelSep) { 513185377Ssam if (!(c_lo <= c && c <= c_hi)) { 514185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 515185377Ssam "%s: c %u out of range [%u..%u]\n", 516185377Ssam __func__, c, c_lo, c_hi); 517185377Ssam continue; 518185377Ssam } 519185377Ssam if (next >= maxchans){ 520185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 521185377Ssam "%s: too many channels for channel table\n", 522185377Ssam __func__); 523185377Ssam goto done; 524185377Ssam } 525185377Ssam if ((fband->usePassScan & IS_ECM_CHAN) && 526185377Ssam !enableExtendedChannels) { 527185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 528187831Ssam "skip ecm channel\n"); 529185377Ssam continue; 530185377Ssam } 531224242Sadrian#if 0 532187831Ssam if ((fband->useDfs & dfsMask) && 533187831Ssam (cm->flags & IEEE80211_CHAN_HT40)) { 534187831Ssam /* NB: DFS and HT40 don't mix */ 535185377Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 536187831Ssam "skip HT40 chan, DFS required\n"); 537185377Ssam continue; 538185377Ssam } 539224242Sadrian#endif 540185377Ssam /* 541185377Ssam * Make sure that channel separation 542185377Ssam * meets the requirement. 543185377Ssam */ 544185377Ssam if (lastc && channelSep && 545185377Ssam (c-lastc) < channelSep) 546185377Ssam continue; 547185377Ssam lastc = c; 548185377Ssam 549187831Ssam OS_MEMZERO(ic, sizeof(*ic)); 550187831Ssam ic->ic_freq = c; 551187831Ssam ic->ic_flags = cm->flags; 552187831Ssam ic->ic_maxregpower = fband->powerDfs; 553187831Ssam ath_hal_getpowerlimits(ah, ic); 554187831Ssam ic->ic_maxantgain = fband->antennaMax; 555187831Ssam if (fband->usePassScan & pscan) 556187831Ssam ic->ic_flags |= IEEE80211_CHAN_PASSIVE; 557187831Ssam if (fband->useDfs & dfsMask) 558187831Ssam ic->ic_flags |= IEEE80211_CHAN_DFS; 559187831Ssam if (IEEE80211_IS_CHAN_5GHZ(ic) && 560187831Ssam (rdflags & DISALLOW_ADHOC_11A)) 561187831Ssam ic->ic_flags |= IEEE80211_CHAN_NOADHOC; 562187831Ssam if (IEEE80211_IS_CHAN_TURBO(ic) && 563187831Ssam (rdflags & DISALLOW_ADHOC_11A_TURB)) 564187831Ssam ic->ic_flags |= IEEE80211_CHAN_NOADHOC; 565187831Ssam if (rdflags & NO_HOSTAP) 566187831Ssam ic->ic_flags |= IEEE80211_CHAN_NOHOSTAP; 567187831Ssam if (rdflags & LIMIT_FRAME_4MS) 568187831Ssam ic->ic_flags |= IEEE80211_CHAN_4MSXMIT; 569187831Ssam if (rdflags & NEED_NFC) 570187831Ssam ic->ic_flags |= CHANNEL_NFCREQUIRED; 571187831Ssam 572187831Ssam ic++, next++; 573185377Ssam } 574185377Ssam } 575185377Ssam } 576185377Ssamdone: 577185377Ssam *nchans = next; 578187831Ssam /* NB: pcountry set above by getregstate */ 579187831Ssam if (prd2GHz != AH_NULL) 580187831Ssam *prd2GHz = rd2GHz; 581187831Ssam if (prd5GHz != AH_NULL) 582187831Ssam *prd5GHz = rd5GHz; 583187831Ssam return HAL_OK; 584187831Ssam#undef HAL_MODE_11A_ALL 585185377Ssam#undef CHANNEL_HALF_BW 586185377Ssam#undef CHANNEL_QUARTER_BW 587185377Ssam} 588185377Ssam 589185377Ssam/* 590187831Ssam * Retrieve a channel list without affecting runtime state. 591185377Ssam */ 592187831SsamHAL_STATUS 593187831Ssamath_hal_getchannels(struct ath_hal *ah, 594187831Ssam struct ieee80211_channel chans[], u_int maxchans, int *nchans, 595187831Ssam u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 596187831Ssam HAL_BOOL enableExtendedChannels) 597185377Ssam{ 598187831Ssam return getchannels(ah, chans, maxchans, nchans, modeSelect, 599187831Ssam cc, regDmn, enableExtendedChannels, AH_NULL, AH_NULL, AH_NULL); 600187831Ssam} 601185377Ssam 602187831Ssam/* 603187831Ssam * Handle frequency mapping from 900Mhz range to 2.4GHz range 604187831Ssam * for GSM radios. This is done when we need the h/w frequency 605187831Ssam * and the channel is marked IEEE80211_CHAN_GSM. 606187831Ssam */ 607187831Ssamstatic int 608187831Ssamath_hal_mapgsm(int sku, int freq) 609187831Ssam{ 610187831Ssam if (sku == SKU_XR9) 611187831Ssam return 1520 + freq; 612187831Ssam if (sku == SKU_GZ901) 613187831Ssam return 1544 + freq; 614187831Ssam if (sku == SKU_SR9) 615187831Ssam return 3344 - freq; 616243975Sadrian if (sku == SKU_XC900M) 617243975Sadrian return 1517 + freq; 618225883Sadrian HALDEBUG(AH_NULL, HAL_DEBUG_ANY, 619187831Ssam "%s: cannot map freq %u unknown gsm sku %u\n", 620187831Ssam __func__, freq, sku); 621187831Ssam return freq; 622187831Ssam} 623185377Ssam 624187831Ssam/* 625187831Ssam * Setup the internal/private channel state given a table of 626187831Ssam * net80211 channels. We collapse entries for the same frequency 627187831Ssam * and record the frequency for doing noise floor processing 628187831Ssam * where we don't have net80211 channel context. 629187831Ssam */ 630187831Ssamstatic HAL_BOOL 631187831SsamassignPrivateChannels(struct ath_hal *ah, 632187831Ssam struct ieee80211_channel chans[], int nchans, int sku) 633187831Ssam{ 634187831Ssam HAL_CHANNEL_INTERNAL *ic; 635187831Ssam int i, j, next, freq; 636187831Ssam 637187831Ssam next = 0; 638187831Ssam for (i = 0; i < nchans; i++) { 639187831Ssam struct ieee80211_channel *c = &chans[i]; 640187831Ssam for (j = i-1; j >= 0; j--) 641187831Ssam if (chans[j].ic_freq == c->ic_freq) { 642187831Ssam c->ic_devdata = chans[j].ic_devdata; 643187831Ssam break; 644185377Ssam } 645187831Ssam if (j < 0) { 646187831Ssam /* new entry, assign a private channel entry */ 647187831Ssam if (next >= N(AH_PRIVATE(ah)->ah_channels)) { 648187831Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 649188084Ssam "%s: too many channels, max %zu\n", 650187831Ssam __func__, N(AH_PRIVATE(ah)->ah_channels)); 651187831Ssam return AH_FALSE; 652187831Ssam } 653187831Ssam /* 654187831Ssam * Handle frequency mapping for 900MHz devices. 655187831Ssam * The hardware uses 2.4GHz frequencies that are 656187831Ssam * down-converted. The 802.11 layer uses the 657187831Ssam * true frequencies. 658187831Ssam */ 659187831Ssam freq = IEEE80211_IS_CHAN_GSM(c) ? 660187831Ssam ath_hal_mapgsm(sku, c->ic_freq) : c->ic_freq; 661187831Ssam 662187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 663187831Ssam "%s: private[%3u] %u/0x%x -> channel %u\n", 664187831Ssam __func__, next, c->ic_freq, c->ic_flags, freq); 665187831Ssam 666187831Ssam ic = &AH_PRIVATE(ah)->ah_channels[next]; 667187831Ssam /* 668187831Ssam * NB: This clears privFlags which means ancillary 669187831Ssam * code like ANI and IQ calibration will be 670187831Ssam * restarted and re-setup any per-channel state. 671187831Ssam */ 672187831Ssam OS_MEMZERO(ic, sizeof(*ic)); 673187831Ssam ic->channel = freq; 674187831Ssam c->ic_devdata = next; 675187831Ssam next++; 676185377Ssam } 677185377Ssam } 678187831Ssam AH_PRIVATE(ah)->ah_nchan = next; 679187831Ssam HALDEBUG(ah, HAL_DEBUG_ANY, "%s: %u public, %u private channels\n", 680187831Ssam __func__, nchans, next); 681187831Ssam return AH_TRUE; 682185377Ssam} 683185377Ssam 684185377Ssam/* 685187831Ssam * Setup the channel list based on the information in the EEPROM. 686185377Ssam */ 687187831SsamHAL_STATUS 688187831Ssamath_hal_init_channels(struct ath_hal *ah, 689187831Ssam struct ieee80211_channel chans[], u_int maxchans, int *nchans, 690187831Ssam u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 691187831Ssam HAL_BOOL enableExtendedChannels) 692185377Ssam{ 693187831Ssam COUNTRY_CODE_TO_ENUM_RD *country; 694224718Sadrian REG_DOMAIN *rd5GHz, *rd2GHz; 695187831Ssam HAL_STATUS status; 696185377Ssam 697187831Ssam status = getchannels(ah, chans, maxchans, nchans, modeSelect, 698187831Ssam cc, regDmn, enableExtendedChannels, &country, &rd2GHz, &rd5GHz); 699187831Ssam if (status == HAL_OK && 700187831Ssam assignPrivateChannels(ah, chans, *nchans, AH_PRIVATE(ah)->ah_currentRD)) { 701187831Ssam AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz; 702187831Ssam AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz; 703187831Ssam 704187831Ssam ah->ah_countryCode = country->countryCode; 705187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n", 706187831Ssam __func__, ah->ah_countryCode); 707224718Sadrian 708224718Sadrian /* Update current DFS domain */ 709224718Sadrian ath_hal_update_dfsdomain(ah); 710187831Ssam } else 711187831Ssam status = HAL_EINVAL; 712224716Sadrian 713187831Ssam return status; 714185377Ssam} 715185377Ssam 716187831Ssam/* 717187831Ssam * Set the channel list. 718187831Ssam */ 719187831SsamHAL_STATUS 720187831Ssamath_hal_set_channels(struct ath_hal *ah, 721187831Ssam struct ieee80211_channel chans[], int nchans, 722187831Ssam HAL_CTRY_CODE cc, HAL_REG_DOMAIN rd) 723187831Ssam{ 724187831Ssam COUNTRY_CODE_TO_ENUM_RD *country; 725187831Ssam REG_DOMAIN *rd5GHz, *rd2GHz; 726187831Ssam HAL_STATUS status; 727185377Ssam 728187831Ssam switch (rd) { 729187831Ssam case SKU_SR9: 730187831Ssam case SKU_XR9: 731187831Ssam case SKU_GZ901: 732243975Sadrian case SKU_XC900M: 733187831Ssam /* 734187831Ssam * Map 900MHz sku's. The frequencies will be mapped 735187831Ssam * according to the sku to compensate for the down-converter. 736187831Ssam * We use the FCC for these sku's as the mapped channel 737187831Ssam * list is known compatible (will need to change if/when 738187831Ssam * vendors do different mapping in different locales). 739187831Ssam */ 740187831Ssam status = getregstate(ah, CTRY_DEFAULT, SKU_FCC, 741187831Ssam &country, &rd2GHz, &rd5GHz); 742187831Ssam break; 743187831Ssam default: 744187831Ssam status = getregstate(ah, cc, rd, 745187831Ssam &country, &rd2GHz, &rd5GHz); 746187831Ssam rd = AH_PRIVATE(ah)->ah_currentRD; 747187831Ssam break; 748187831Ssam } 749187831Ssam if (status == HAL_OK && assignPrivateChannels(ah, chans, nchans, rd)) { 750187831Ssam AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz; 751187831Ssam AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz; 752185377Ssam 753187831Ssam ah->ah_countryCode = country->countryCode; 754187831Ssam HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n", 755187831Ssam __func__, ah->ah_countryCode); 756187831Ssam } else 757187831Ssam status = HAL_EINVAL; 758224718Sadrian 759224718Sadrian if (status == HAL_OK) { 760224718Sadrian /* Update current DFS domain */ 761224718Sadrian (void) ath_hal_update_dfsdomain(ah); 762224718Sadrian } 763187831Ssam return status; 764187831Ssam} 765185377Ssam 766187831Ssam#ifdef AH_DEBUG 767185377Ssam/* 768187831Ssam * Return the internal channel corresponding to a public channel. 769187831Ssam * NB: normally this routine is inline'd (see ah_internal.h) 770185377Ssam */ 771187831SsamHAL_CHANNEL_INTERNAL * 772187831Ssamath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c) 773185377Ssam{ 774187831Ssam HAL_CHANNEL_INTERNAL *cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata]; 775185377Ssam 776187831Ssam if (c->ic_devdata < AH_PRIVATE(ah)->ah_nchan && 777187831Ssam (c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c))) 778187831Ssam return cc; 779187831Ssam if (c->ic_devdata >= AH_PRIVATE(ah)->ah_nchan) { 780187831Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 781187831Ssam "%s: bad mapping, devdata %u nchans %u\n", 782187831Ssam __func__, c->ic_devdata, AH_PRIVATE(ah)->ah_nchan); 783187831Ssam HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan); 784185377Ssam } else { 785187831Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 786187831Ssam "%s: no match for %u/0x%x devdata %u channel %u\n", 787187831Ssam __func__, c->ic_freq, c->ic_flags, c->ic_devdata, 788187831Ssam cc->channel); 789187831Ssam HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)); 790185377Ssam } 791187831Ssam return AH_NULL; 792185377Ssam} 793187831Ssam#endif /* AH_DEBUG */ 794185377Ssam 795187831Ssam#define isWwrSKU(_ah) \ 796187831Ssam ((getEepromRD((_ah)) & WORLD_SKU_MASK) == WORLD_SKU_PREFIX || \ 797187831Ssam getEepromRD(_ah) == WORLD) 798187831Ssam 799185377Ssam/* 800187831Ssam * Return the test group for the specific channel based on 801187831Ssam * the current regulatory setup. 802185377Ssam */ 803187831Ssamu_int 804187831Ssamath_hal_getctl(struct ath_hal *ah, const struct ieee80211_channel *c) 805185377Ssam{ 806187831Ssam u_int ctl; 807185377Ssam 808187831Ssam if (AH_PRIVATE(ah)->ah_rd2GHz == AH_PRIVATE(ah)->ah_rd5GHz || 809187831Ssam (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah))) 810187831Ssam ctl = SD_NO_CTL; 811187831Ssam else if (IEEE80211_IS_CHAN_2GHZ(c)) 812187831Ssam ctl = AH_PRIVATE(ah)->ah_rd2GHz->conformanceTestLimit; 813187831Ssam else 814187831Ssam ctl = AH_PRIVATE(ah)->ah_rd5GHz->conformanceTestLimit; 815187831Ssam if (IEEE80211_IS_CHAN_B(c)) 816187831Ssam return ctl | CTL_11B; 817187831Ssam if (IEEE80211_IS_CHAN_G(c)) 818187831Ssam return ctl | CTL_11G; 819187831Ssam if (IEEE80211_IS_CHAN_108G(c)) 820187831Ssam return ctl | CTL_108G; 821187831Ssam if (IEEE80211_IS_CHAN_TURBO(c)) 822187831Ssam return ctl | CTL_TURBO; 823187831Ssam if (IEEE80211_IS_CHAN_A(c)) 824187831Ssam return ctl | CTL_11A; 825187831Ssam return ctl; 826185377Ssam} 827185377Ssam 828224718Sadrian 829185377Ssam/* 830224718Sadrian * Update the current dfsDomain setting based on the given 831224718Sadrian * country code. 832224718Sadrian * 833224718Sadrian * Since FreeBSD/net80211 allows the channel set to change 834224718Sadrian * after the card has been setup (via ath_hal_init_channels()) 835224718Sadrian * this function method is needed to update ah_dfsDomain. 836224718Sadrian */ 837224718Sadrianvoid 838224718Sadrianath_hal_update_dfsdomain(struct ath_hal *ah) 839224718Sadrian{ 840224718Sadrian const REG_DOMAIN *rd5GHz = AH_PRIVATE(ah)->ah_rd5GHz; 841224718Sadrian HAL_DFS_DOMAIN dfsDomain = HAL_DFS_UNINIT_DOMAIN; 842224718Sadrian 843224718Sadrian if (rd5GHz->dfsMask & DFS_FCC3) 844224718Sadrian dfsDomain = HAL_DFS_FCC_DOMAIN; 845224718Sadrian if (rd5GHz->dfsMask & DFS_ETSI) 846224718Sadrian dfsDomain = HAL_DFS_ETSI_DOMAIN; 847224718Sadrian if (rd5GHz->dfsMask & DFS_MKK4) 848224718Sadrian dfsDomain = HAL_DFS_MKK4_DOMAIN; 849224718Sadrian AH_PRIVATE(ah)->ah_dfsDomain = dfsDomain; 850224718Sadrian HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s ah_dfsDomain: %d\n", 851224718Sadrian __func__, AH_PRIVATE(ah)->ah_dfsDomain); 852224718Sadrian} 853224718Sadrian 854224718Sadrian 855224718Sadrian/* 856187831Ssam * Return the max allowed antenna gain and apply any regulatory 857187831Ssam * domain specific changes. 858187831Ssam * 859187831Ssam * NOTE: a negative reduction is possible in RD's that only 860187831Ssam * measure radiated power (e.g., ETSI) which would increase 861187831Ssam * that actual conducted output power (though never beyond 862187831Ssam * the calibrated target power). 863185377Ssam */ 864187831Ssamu_int 865187831Ssamath_hal_getantennareduction(struct ath_hal *ah, 866187831Ssam const struct ieee80211_channel *chan, u_int twiceGain) 867185377Ssam{ 868187831Ssam int8_t antennaMax = twiceGain - chan->ic_maxantgain*2; 869187831Ssam return (antennaMax < 0) ? 0 : antennaMax; 870185377Ssam} 871