ah_regdomain.c revision 224718
1193323Sed/* 2193323Sed * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 3193323Sed * Copyright (c) 2005-2006 Atheros Communications, Inc. 4193323Sed * All rights reserved. 5193323Sed * 6193323Sed * Permission to use, copy, modify, and/or distribute this software for any 7193323Sed * purpose with or without fee is hereby granted, provided that the above 8193323Sed * copyright notice and this permission notice appear in all copies. 9193323Sed * 10193323Sed * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11193323Sed * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12193323Sed * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13193323Sed * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14193323Sed * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15193323Sed * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16193323Sed * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17193323Sed * 18193323Sed * $FreeBSD: head/sys/dev/ath/ath_hal/ah_regdomain.c 224718 2011-08-08 17:33:35Z adrian $ 19193323Sed */ 20193323Sed#include "opt_ah.h" 21193323Sed 22198892Srdivacky#include "ah.h" 23193323Sed 24193323Sed#include <net80211/_ieee80211.h> 25193323Sed#include <net80211/ieee80211_regdomain.h> 26193323Sed 27193323Sed#include "ah_internal.h" 28193323Sed#include "ah_eeprom.h" 29193323Sed#include "ah_devid.h" 30193323Sed 31198090Srdivacky#include "ah_regdomain.h" 32198090Srdivacky 33198090Srdivacky/* 34193323Sed * XXX this code needs a audit+review 35198090Srdivacky */ 36198090Srdivacky 37193323Sed/* used throughout this file... */ 38193323Sed#define N(a) (sizeof (a) / sizeof (a[0])) 39193323Sed 40193323Sed#define HAL_MODE_11A_TURBO HAL_MODE_108A 41193323Sed#define HAL_MODE_11G_TURBO HAL_MODE_108G 42193323Sed 43198090Srdivacky/* 44198892Srdivacky * Mask to check whether a domain is a multidomain or a single domain 45193323Sed */ 46193323Sed#define MULTI_DOMAIN_MASK 0xFF00 47193323Sed 48193323Sed/* 49205218Srdivacky * Enumerated Regulatory Domain Information 8 bit values indicate that 50193323Sed * the regdomain is really a pair of unitary regdomains. 12 bit values 51193323Sed * are the real unitary regdomains and are the only ones which have the 52193323Sed * frequency bitmasks and flags set. 53193323Sed */ 54193323Sed#include "ah_regdomain/ah_rd_regenum.h" 55193323Sed 56212904Sdim#define WORLD_SKU_MASK 0x00F0 57193323Sed#define WORLD_SKU_PREFIX 0x0060 58193323Sed 59193323Sed/* 60193323Sed * THE following table is the mapping of regdomain pairs specified by 61212904Sdim * an 8 bit regdomain value to the individual unitary reg domains 62193323Sed */ 63193323Sed#include "ah_regdomain/ah_rd_regmap.h" 64193323Sed 65193323Sed/* 66193323Sed * The following tables are the master list for all different freqeuncy 67193323Sed * bands with the complete matrix of all possible flags and settings 68193323Sed * for each band if it is used in ANY reg domain. 69193323Sed */ 70193323Sed 71224145Sdim#define COUNTRY_ERD_FLAG 0x8000 72193323Sed#define WORLDWIDE_ROAMING_FLAG 0x4000 73193323Sed 74198090Srdivacky/* 75193323Sed * This table maps country ISO codes from net80211 into regulatory 76193323Sed * domains which the ath regulatory domain code understands. 77193323Sed */ 78193323Sed#include "ah_regdomain/ah_rd_ctry.h" 79193323Sed 80193323Sed/* 81207618Srdivacky * The frequency band collections are a set of frequency ranges 82193323Sed * with shared properties - max tx power, max antenna gain, channel width, 83199481Srdivacky * channel spacing, DFS requirements and passive scanning requirements. 84193323Sed * 85193323Sed * These are represented as entries in a frequency band bitmask. 86193323Sed * Each regulatory domain entry in ah_regdomain_domains.h uses one 87193323Sed * or more frequency band entries for each of the channel modes 88193323Sed * supported (11bg, 11a, half, quarter, turbo, etc.) 89193323Sed * 90193323Sed */ 91198090Srdivacky#include "ah_regdomain/ah_rd_freqbands.h" 92193323Sed 93193323Sed/* 94193323Sed * This is the main regulatory database. It defines the supported 95193323Sed * set of features and requirements for each of the defined regulatory 96193323Sed * zones. It uses combinations of frequency ranges - represented in 97193323Sed * a bitmask - to determine the requirements and limitations needed. 98193323Sed */ 99193323Sed#include "ah_regdomain/ah_rd_domains.h" 100193323Sed 101193323Sedstatic const struct cmode modes[] = { 102193323Sed { HAL_MODE_TURBO, IEEE80211_CHAN_ST }, 103193323Sed { HAL_MODE_11A, IEEE80211_CHAN_A }, 104193323Sed { HAL_MODE_11B, IEEE80211_CHAN_B }, 105198090Srdivacky { HAL_MODE_11G, IEEE80211_CHAN_G }, 106193323Sed { HAL_MODE_11G_TURBO, IEEE80211_CHAN_108G }, 107193323Sed { HAL_MODE_11A_TURBO, IEEE80211_CHAN_108A }, 108193323Sed { HAL_MODE_11A_QUARTER_RATE, 109198090Srdivacky IEEE80211_CHAN_A | IEEE80211_CHAN_QUARTER }, 110198090Srdivacky { HAL_MODE_11A_HALF_RATE, 111193323Sed IEEE80211_CHAN_A | IEEE80211_CHAN_HALF }, 112193323Sed { HAL_MODE_11G_QUARTER_RATE, 113193323Sed IEEE80211_CHAN_G | IEEE80211_CHAN_QUARTER }, 114193323Sed { HAL_MODE_11G_HALF_RATE, 115193323Sed IEEE80211_CHAN_G | IEEE80211_CHAN_HALF }, 116205218Srdivacky { HAL_MODE_11NG_HT20, IEEE80211_CHAN_G | IEEE80211_CHAN_HT20 }, 117205218Srdivacky { HAL_MODE_11NG_HT40PLUS, 118193323Sed IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U }, 119193323Sed { HAL_MODE_11NG_HT40MINUS, 120193323Sed IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D }, 121193323Sed { HAL_MODE_11NA_HT20, IEEE80211_CHAN_A | IEEE80211_CHAN_HT20 }, 122193323Sed { HAL_MODE_11NA_HT40PLUS, 123193323Sed IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U }, 124193323Sed { HAL_MODE_11NA_HT40MINUS, 125202375Srdivacky IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D }, 126198090Srdivacky}; 127193323Sed 128193323Sedstatic void ath_hal_update_dfsdomain(struct ath_hal *ah); 129193323Sed 130193323Sedstatic OS_INLINE uint16_t 131218893SdimgetEepromRD(struct ath_hal *ah) 132193323Sed{ 133224145Sdim return AH_PRIVATE(ah)->ah_currentRD &~ WORLDWIDE_ROAMING_FLAG; 134193323Sed} 135193323Sed 136193323Sed/* 137193323Sed * Test to see if the bitmask array is all zeros 138210299Sed */ 139193323Sedstatic HAL_BOOL 140193323SedisChanBitMaskZero(const uint64_t *bitmask) 141193323Sed{ 142193323Sed#if BMLEN > 2 143193323Sed#error "add more cases" 144193323Sed#endif 145193323Sed#if BMLEN > 1 146212904Sdim if (bitmask[1] != 0) 147212904Sdim return AH_FALSE; 148212904Sdim#endif 149212904Sdim return (bitmask[0] == 0); 150212904Sdim} 151224145Sdim 152212904Sdim/* 153212904Sdim * Return whether or not the regulatory domain/country in EEPROM 154212904Sdim * is acceptable. 155212904Sdim */ 156212904Sdimstatic HAL_BOOL 157212904SdimisEepromValid(struct ath_hal *ah) 158212904Sdim{ 159212904Sdim uint16_t rd = getEepromRD(ah); 160212904Sdim int i; 161212904Sdim 162224145Sdim if (rd & COUNTRY_ERD_FLAG) { 163212904Sdim uint16_t cc = rd &~ COUNTRY_ERD_FLAG; 164212904Sdim for (i = 0; i < N(allCountries); i++) 165212904Sdim if (allCountries[i].countryCode == cc) 166212904Sdim return AH_TRUE; 167212904Sdim } else { 168212904Sdim for (i = 0; i < N(regDomainPairs); i++) 169212904Sdim if (regDomainPairs[i].regDmnEnum == rd) 170226633Sdim return AH_TRUE; 171212904Sdim } 172212904Sdim HALDEBUG_G(ah, HAL_DEBUG_REGDOMAIN, 173212904Sdim "%s: invalid regulatory domain/country code 0x%x\n", __func__, rd); 174212904Sdim return AH_FALSE; 175212904Sdim} 176212904Sdim 177212904Sdim/* 178212904Sdim * Find the pointer to the country element in the country table 179212904Sdim * corresponding to the country code 180212904Sdim */ 181212904Sdimstatic COUNTRY_CODE_TO_ENUM_RD* 182212904SdimfindCountry(HAL_CTRY_CODE countryCode) 183212904Sdim{ 184212904Sdim int i; 185212904Sdim 186212904Sdim for (i = 0; i < N(allCountries); i++) { 187212904Sdim if (allCountries[i].countryCode == countryCode) 188212904Sdim return &allCountries[i]; 189212904Sdim } 190212904Sdim return AH_NULL; 191212904Sdim} 192212904Sdim 193212904Sdimstatic REG_DOMAIN * 194212904SdimfindRegDmn(int regDmn) 195212904Sdim{ 196212904Sdim int i; 197212904Sdim 198212904Sdim for (i = 0; i < N(regDomains); i++) { 199212904Sdim if (regDomains[i].regDmnEnum == regDmn) 200212904Sdim return ®Domains[i]; 201212904Sdim } 202212904Sdim return AH_NULL; 203212904Sdim} 204212904Sdim 205212904Sdimstatic REG_DMN_PAIR_MAPPING * 206212904SdimfindRegDmnPair(int regDmnPair) 207212904Sdim{ 208212904Sdim int i; 209212904Sdim 210212904Sdim if (regDmnPair != NO_ENUMRD) { 211212904Sdim for (i = 0; i < N(regDomainPairs); i++) { 212212904Sdim if (regDomainPairs[i].regDmnEnum == regDmnPair) 213212904Sdim return ®DomainPairs[i]; 214212904Sdim } 215212904Sdim } 216212904Sdim return AH_NULL; 217212904Sdim} 218212904Sdim 219212904Sdim/* 220212904Sdim * Calculate a default country based on the EEPROM setting. 221212904Sdim */ 222212904Sdimstatic HAL_CTRY_CODE 223212904SdimgetDefaultCountry(struct ath_hal *ah) 224212904Sdim{ 225212904Sdim REG_DMN_PAIR_MAPPING *regpair; 226212904Sdim uint16_t rd; 227212904Sdim 228212904Sdim rd = getEepromRD(ah); 229212904Sdim if (rd & COUNTRY_ERD_FLAG) { 230212904Sdim COUNTRY_CODE_TO_ENUM_RD *country; 231212904Sdim uint16_t cc = rd & ~COUNTRY_ERD_FLAG; 232212904Sdim country = findCountry(cc); 233212904Sdim if (country != AH_NULL) 234212904Sdim return cc; 235212904Sdim } 236212904Sdim /* 237212904Sdim * Check reg domains that have only one country 238212904Sdim */ 239212904Sdim regpair = findRegDmnPair(rd); 240212904Sdim return (regpair != AH_NULL) ? regpair->singleCC : CTRY_DEFAULT; 241212904Sdim} 242212904Sdim 243193323Sedstatic HAL_BOOL 244193323SedIS_BIT_SET(int bit, const uint64_t bitmask[]) 245193323Sed{ 246193323Sed int byteOffset, bitnum; 247193323Sed uint64_t val; 248193323Sed 249193323Sed byteOffset = bit/64; 250193323Sed bitnum = bit - byteOffset*64; 251193323Sed val = ((uint64_t) 1) << bitnum; 252193323Sed return (bitmask[byteOffset] & val) != 0; 253193323Sed} 254193323Sed 255193323Sedstatic HAL_STATUS 256193323Sedgetregstate(struct ath_hal *ah, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 257193323Sed COUNTRY_CODE_TO_ENUM_RD **pcountry, 258193323Sed REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz) 259193323Sed{ 260207618Srdivacky COUNTRY_CODE_TO_ENUM_RD *country; 261207618Srdivacky REG_DOMAIN *rd5GHz, *rd2GHz; 262193323Sed 263193323Sed if (cc == CTRY_DEFAULT && regDmn == SKU_NONE) { 264193323Sed /* 265198090Srdivacky * Validate the EEPROM setting and setup defaults 266193323Sed */ 267193323Sed if (!isEepromValid(ah)) { 268193323Sed /* 269193323Sed * Don't return any channels if the EEPROM has an 270193323Sed * invalid regulatory domain/country code setting. 271193323Sed */ 272207618Srdivacky HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 273207618Srdivacky "%s: invalid EEPROM contents\n",__func__); 274193323Sed return HAL_EEBADREG; 275207618Srdivacky } 276193323Sed 277193323Sed cc = getDefaultCountry(ah); 278193323Sed country = findCountry(cc); 279193323Sed if (country == AH_NULL) { 280193323Sed HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 281193323Sed "NULL Country!, cc %d\n", cc); 282193323Sed return HAL_EEBADCC; 283193323Sed } 284193323Sed regDmn = country->regDmnEnum; 285193323Sed HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: EEPROM cc %u rd 0x%x\n", 286193323Sed __func__, cc, regDmn); 287193323Sed 288193323Sed if (country->countryCode == CTRY_DEFAULT) { 289193323Sed /* 290193323Sed * Check EEPROM; SKU may be for a country, single 291203954Srdivacky * domain, or multiple domains (WWR). 292203954Srdivacky */ 293203954Srdivacky uint16_t rdnum = getEepromRD(ah); 294203954Srdivacky if ((rdnum & COUNTRY_ERD_FLAG) == 0 && 295203954Srdivacky (findRegDmn(rdnum) != AH_NULL || 296203954Srdivacky findRegDmnPair(rdnum) != AH_NULL)) { 297193323Sed regDmn = rdnum; 298203954Srdivacky HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 299203954Srdivacky "%s: EEPROM rd 0x%x\n", __func__, rdnum); 300193323Sed } 301193323Sed } 302193323Sed } else { 303193323Sed country = findCountry(cc); 304193323Sed if (country == AH_NULL) { 305193323Sed HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 306193323Sed "unknown country, cc %d\n", cc); 307193323Sed return HAL_EINVAL; 308193323Sed } 309193323Sed if (regDmn == SKU_NONE) 310193323Sed regDmn = country->regDmnEnum; 311193323Sed HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u rd 0x%x\n", 312193323Sed __func__, cc, regDmn); 313193323Sed } 314193323Sed 315193323Sed /* 316193323Sed * Setup per-band state. 317193323Sed */ 318193323Sed if ((regDmn & MULTI_DOMAIN_MASK) == 0) { 319193323Sed REG_DMN_PAIR_MAPPING *regpair = findRegDmnPair(regDmn); 320193323Sed if (regpair == AH_NULL) { 321193323Sed HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 322193323Sed "%s: no reg domain pair %u for country %u\n", 323193323Sed __func__, regDmn, country->countryCode); 324193323Sed return HAL_EINVAL; 325193323Sed } 326193323Sed rd5GHz = findRegDmn(regpair->regDmn5GHz); 327193323Sed if (rd5GHz == AH_NULL) { 328193323Sed HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 329193323Sed "%s: no 5GHz reg domain %u for country %u\n", 330193323Sed __func__, regpair->regDmn5GHz, country->countryCode); 331193323Sed return HAL_EINVAL; 332193323Sed } 333193323Sed rd2GHz = findRegDmn(regpair->regDmn2GHz); 334193323Sed if (rd2GHz == AH_NULL) { 335193323Sed HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 336193323Sed "%s: no 2GHz reg domain %u for country %u\n", 337193323Sed __func__, regpair->regDmn2GHz, country->countryCode); 338193323Sed return HAL_EINVAL; 339193323Sed } 340193323Sed } else { 341193323Sed rd5GHz = rd2GHz = findRegDmn(regDmn); 342193323Sed if (rd2GHz == AH_NULL) { 343193323Sed HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 344193323Sed "%s: no unitary reg domain %u for country %u\n", 345193323Sed __func__, regDmn, country->countryCode); 346193323Sed return HAL_EINVAL; 347193323Sed } 348193323Sed } 349193323Sed if (pcountry != AH_NULL) 350193323Sed *pcountry = country; 351193323Sed *prd2GHz = rd2GHz; 352193323Sed *prd5GHz = rd5GHz; 353193323Sed return HAL_OK; 354193323Sed} 355193323Sed 356226633Sdim/* 357193323Sed * Construct the channel list for the specified regulatory config. 358193323Sed */ 359193323Sedstatic HAL_STATUS 360193323Sedgetchannels(struct ath_hal *ah, 361193323Sed struct ieee80211_channel chans[], u_int maxchans, int *nchans, 362193323Sed u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 363193323Sed HAL_BOOL enableExtendedChannels, 364193323Sed COUNTRY_CODE_TO_ENUM_RD **pcountry, 365193323Sed REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz) 366193323Sed{ 367193323Sed#define CHANNEL_HALF_BW 10 368193323Sed#define CHANNEL_QUARTER_BW 5 369193323Sed#define HAL_MODE_11A_ALL \ 370193323Sed (HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \ 371193323Sed HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE) 372193323Sed REG_DOMAIN *rd5GHz, *rd2GHz; 373193323Sed u_int modesAvail; 374193323Sed const struct cmode *cm; 375193323Sed struct ieee80211_channel *ic; 376193323Sed int next, b; 377193323Sed HAL_STATUS status; 378193323Sed 379193323Sed HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u regDmn 0x%x mode 0x%x%s\n", 380193323Sed __func__, cc, regDmn, modeSelect, 381193323Sed enableExtendedChannels ? " ecm" : ""); 382193323Sed 383193323Sed status = getregstate(ah, cc, regDmn, pcountry, &rd2GHz, &rd5GHz); 384193323Sed if (status != HAL_OK) 385193323Sed return status; 386193323Sed 387198090Srdivacky /* get modes that HW is capable of */ 388198090Srdivacky modesAvail = ath_hal_getWirelessModes(ah); 389198090Srdivacky /* optimize work below if no 11a channels */ 390193323Sed if (isChanBitMaskZero(rd5GHz->chan11a) && 391198090Srdivacky (modesAvail & HAL_MODE_11A_ALL)) { 392198090Srdivacky HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 393198090Srdivacky "%s: disallow all 11a\n", __func__); 394198090Srdivacky modesAvail &= ~HAL_MODE_11A_ALL; 395198090Srdivacky } 396198090Srdivacky 397198090Srdivacky next = 0; 398193323Sed ic = &chans[0]; 399193323Sed for (cm = modes; cm < &modes[N(modes)]; cm++) { 400193323Sed uint16_t c, c_hi, c_lo; 401193323Sed uint64_t *channelBM = AH_NULL; 402198090Srdivacky REG_DMN_FREQ_BAND *fband = AH_NULL,*freqs; 403198090Srdivacky int low_adj, hi_adj, channelSep, lastc; 404198090Srdivacky uint32_t rdflags; 405193323Sed uint64_t dfsMask; 406193323Sed uint64_t pscan; 407193323Sed 408193323Sed if ((cm->mode & modeSelect) == 0) { 409193323Sed HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 410193323Sed "%s: skip mode 0x%x flags 0x%x\n", 411198090Srdivacky __func__, cm->mode, cm->flags); 412193323Sed continue; 413193323Sed } 414198090Srdivacky if ((cm->mode & modesAvail) == 0) { 415198090Srdivacky HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 416198090Srdivacky "%s: !avail mode 0x%x (0x%x) flags 0x%x\n", 417193323Sed __func__, modesAvail, cm->mode, cm->flags); 418193323Sed continue; 419198090Srdivacky } 420198090Srdivacky if (!ath_hal_getChannelEdges(ah, cm->flags, &c_lo, &c_hi)) { 421193323Sed /* channel not supported by hardware, skip it */ 422193323Sed HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 423198090Srdivacky "%s: channels 0x%x not supported by hardware\n", 424198090Srdivacky __func__,cm->flags); 425199481Srdivacky continue; 426198090Srdivacky } 427198090Srdivacky switch (cm->mode) { 428193323Sed case HAL_MODE_TURBO: 429198090Srdivacky case HAL_MODE_11A_TURBO: 430198090Srdivacky rdflags = rd5GHz->flags; 431193323Sed dfsMask = rd5GHz->dfsMask; 432198090Srdivacky pscan = rd5GHz->pscan; 433198090Srdivacky if (cm->mode == HAL_MODE_TURBO) 434193323Sed channelBM = rd5GHz->chan11a_turbo; 435193323Sed else 436193323Sed channelBM = rd5GHz->chan11a_dyn_turbo; 437193323Sed freqs = ®Dmn5GhzTurboFreq[0]; 438193323Sed break; 439198090Srdivacky case HAL_MODE_11G_TURBO: 440198090Srdivacky rdflags = rd2GHz->flags; 441193323Sed dfsMask = rd2GHz->dfsMask; 442193323Sed pscan = rd2GHz->pscan; 443193323Sed channelBM = rd2GHz->chan11g_turbo; 444193323Sed freqs = ®Dmn2Ghz11gTurboFreq[0]; 445193323Sed break; 446193323Sed case HAL_MODE_11A: 447193323Sed case HAL_MODE_11A_HALF_RATE: 448198090Srdivacky case HAL_MODE_11A_QUARTER_RATE: 449198090Srdivacky case HAL_MODE_11NA_HT20: 450193323Sed case HAL_MODE_11NA_HT40PLUS: 451198090Srdivacky case HAL_MODE_11NA_HT40MINUS: 452193323Sed rdflags = rd5GHz->flags; 453193323Sed dfsMask = rd5GHz->dfsMask; 454193323Sed pscan = rd5GHz->pscan; 455193323Sed if (cm->mode == HAL_MODE_11A_HALF_RATE) 456193323Sed channelBM = rd5GHz->chan11a_half; 457193323Sed else if (cm->mode == HAL_MODE_11A_QUARTER_RATE) 458198090Srdivacky channelBM = rd5GHz->chan11a_quarter; 459193323Sed else 460193323Sed channelBM = rd5GHz->chan11a; 461193323Sed freqs = ®Dmn5GhzFreq[0]; 462193323Sed break; 463193323Sed case HAL_MODE_11B: 464193323Sed case HAL_MODE_11G: 465193323Sed case HAL_MODE_11G_HALF_RATE: 466193323Sed case HAL_MODE_11G_QUARTER_RATE: 467193323Sed case HAL_MODE_11NG_HT20: 468193323Sed case HAL_MODE_11NG_HT40PLUS: 469193323Sed case HAL_MODE_11NG_HT40MINUS: 470193323Sed rdflags = rd2GHz->flags; 471193323Sed dfsMask = rd2GHz->dfsMask; 472207618Srdivacky pscan = rd2GHz->pscan; 473207618Srdivacky if (cm->mode == HAL_MODE_11G_HALF_RATE) 474207618Srdivacky channelBM = rd2GHz->chan11g_half; 475207618Srdivacky else if (cm->mode == HAL_MODE_11G_QUARTER_RATE) 476207618Srdivacky channelBM = rd2GHz->chan11g_quarter; 477207618Srdivacky else if (cm->mode == HAL_MODE_11B) 478207618Srdivacky channelBM = rd2GHz->chan11b; 479207618Srdivacky else 480207618Srdivacky channelBM = rd2GHz->chan11g; 481207618Srdivacky if (cm->mode == HAL_MODE_11B) 482193323Sed freqs = ®Dmn2GhzFreq[0]; 483198090Srdivacky else 484198090Srdivacky freqs = ®Dmn2Ghz11gFreq[0]; 485198090Srdivacky break; 486198090Srdivacky default: 487198090Srdivacky HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 488193323Sed "%s: Unkonwn HAL mode 0x%x\n", __func__, cm->mode); 489198090Srdivacky continue; 490198090Srdivacky } 491198090Srdivacky if (isChanBitMaskZero(channelBM)) 492203954Srdivacky continue; 493203954Srdivacky /* 494226633Sdim * Setup special handling for HT40 channels; e.g. 495203954Srdivacky * 5G HT40 channels require 40Mhz channel separation. 496203954Srdivacky */ 497198090Srdivacky hi_adj = (cm->mode == HAL_MODE_11NA_HT40PLUS || 498203954Srdivacky cm->mode == HAL_MODE_11NG_HT40PLUS) ? -20 : 0; 499203954Srdivacky low_adj = (cm->mode == HAL_MODE_11NA_HT40MINUS || 500203954Srdivacky cm->mode == HAL_MODE_11NG_HT40MINUS) ? 20 : 0; 501203954Srdivacky channelSep = (cm->mode == HAL_MODE_11NA_HT40PLUS || 502203954Srdivacky cm->mode == HAL_MODE_11NA_HT40MINUS) ? 40 : 0; 503203954Srdivacky 504203954Srdivacky for (b = 0; b < 64*BMLEN; b++) { 505203954Srdivacky if (!IS_BIT_SET(b, channelBM)) 506203954Srdivacky continue; 507193323Sed fband = &freqs[b]; 508198090Srdivacky lastc = 0; 509203954Srdivacky 510193323Sed for (c = fband->lowChannel + low_adj; 511203954Srdivacky c <= fband->highChannel + hi_adj; 512203954Srdivacky c += fband->channelSep) { 513203954Srdivacky if (!(c_lo <= c && c <= c_hi)) { 514203954Srdivacky HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 515203954Srdivacky "%s: c %u out of range [%u..%u]\n", 516203954Srdivacky __func__, c, c_lo, c_hi); 517203954Srdivacky continue; 518203954Srdivacky } 519193323Sed if (next >= maxchans){ 520203954Srdivacky HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 521203954Srdivacky "%s: too many channels for channel table\n", 522203954Srdivacky __func__); 523203954Srdivacky goto done; 524203954Srdivacky } 525203954Srdivacky if ((fband->usePassScan & IS_ECM_CHAN) && 526203954Srdivacky !enableExtendedChannels) { 527203954Srdivacky HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 528203954Srdivacky "skip ecm channel\n"); 529203954Srdivacky continue; 530203954Srdivacky } 531203954Srdivacky#if 0 532203954Srdivacky if ((fband->useDfs & dfsMask) && 533203954Srdivacky (cm->flags & IEEE80211_CHAN_HT40)) { 534203954Srdivacky /* NB: DFS and HT40 don't mix */ 535203954Srdivacky HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 536203954Srdivacky "skip HT40 chan, DFS required\n"); 537193323Sed continue; 538203954Srdivacky } 539203954Srdivacky#endif 540203954Srdivacky /* 541203954Srdivacky * Make sure that channel separation 542203954Srdivacky * meets the requirement. 543203954Srdivacky */ 544203954Srdivacky if (lastc && channelSep && 545203954Srdivacky (c-lastc) < channelSep) 546203954Srdivacky continue; 547203954Srdivacky lastc = c; 548203954Srdivacky 549207618Srdivacky OS_MEMZERO(ic, sizeof(*ic)); 550203954Srdivacky ic->ic_freq = c; 551203954Srdivacky ic->ic_flags = cm->flags; 552203954Srdivacky ic->ic_maxregpower = fband->powerDfs; 553203954Srdivacky ath_hal_getpowerlimits(ah, ic); 554203954Srdivacky ic->ic_maxantgain = fband->antennaMax; 555203954Srdivacky if (fband->usePassScan & pscan) 556203954Srdivacky ic->ic_flags |= IEEE80211_CHAN_PASSIVE; 557203954Srdivacky if (fband->useDfs & dfsMask) 558203954Srdivacky ic->ic_flags |= IEEE80211_CHAN_DFS; 559203954Srdivacky if (IEEE80211_IS_CHAN_5GHZ(ic) && 560193323Sed (rdflags & DISALLOW_ADHOC_11A)) 561203954Srdivacky ic->ic_flags |= IEEE80211_CHAN_NOADHOC; 562226633Sdim if (IEEE80211_IS_CHAN_TURBO(ic) && 563203954Srdivacky (rdflags & DISALLOW_ADHOC_11A_TURB)) 564193323Sed ic->ic_flags |= IEEE80211_CHAN_NOADHOC; 565203954Srdivacky if (rdflags & NO_HOSTAP) 566203954Srdivacky ic->ic_flags |= IEEE80211_CHAN_NOHOSTAP; 567203954Srdivacky if (rdflags & LIMIT_FRAME_4MS) 568203954Srdivacky ic->ic_flags |= IEEE80211_CHAN_4MSXMIT; 569203954Srdivacky if (rdflags & NEED_NFC) 570226633Sdim ic->ic_flags |= CHANNEL_NFCREQUIRED; 571203954Srdivacky 572203954Srdivacky ic++, next++; 573203954Srdivacky } 574203954Srdivacky } 575226633Sdim } 576203954Srdivackydone: 577203954Srdivacky *nchans = next; 578226633Sdim /* NB: pcountry set above by getregstate */ 579203954Srdivacky if (prd2GHz != AH_NULL) 580203954Srdivacky *prd2GHz = rd2GHz; 581203954Srdivacky if (prd5GHz != AH_NULL) 582193323Sed *prd5GHz = rd5GHz; 583203954Srdivacky return HAL_OK; 584203954Srdivacky#undef HAL_MODE_11A_ALL 585203954Srdivacky#undef CHANNEL_HALF_BW 586203954Srdivacky#undef CHANNEL_QUARTER_BW 587203954Srdivacky} 588203954Srdivacky 589203954Srdivacky/* 590193323Sed * Retrieve a channel list without affecting runtime state. 591193323Sed */ 592228379SdimHAL_STATUS 593228379Sdimath_hal_getchannels(struct ath_hal *ah, 594228379Sdim struct ieee80211_channel chans[], u_int maxchans, int *nchans, 595228379Sdim u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 596228379Sdim HAL_BOOL enableExtendedChannels) 597228379Sdim{ 598228379Sdim return getchannels(ah, chans, maxchans, nchans, modeSelect, 599193323Sed cc, regDmn, enableExtendedChannels, AH_NULL, AH_NULL, AH_NULL); 600218893Sdim} 601224145Sdim 602202375Srdivacky/* 603218893Sdim * Handle frequency mapping from 900Mhz range to 2.4GHz range 604218893Sdim * for GSM radios. This is done when we need the h/w frequency 605218893Sdim * and the channel is marked IEEE80211_CHAN_GSM. 606228379Sdim */ 607228379Sdimstatic int 608228379Sdimath_hal_mapgsm(int sku, int freq) 609228379Sdim{ 610228379Sdim if (sku == SKU_XR9) 611228379Sdim return 1520 + freq; 612228379Sdim if (sku == SKU_GZ901) 613228379Sdim return 1544 + freq; 614228379Sdim if (sku == SKU_SR9) 615228379Sdim return 3344 - freq; 616228379Sdim HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY, 617228379Sdim "%s: cannot map freq %u unknown gsm sku %u\n", 618228379Sdim __func__, freq, sku); 619228379Sdim return freq; 620228379Sdim} 621228379Sdim 622228379Sdim/* 623218893Sdim * Setup the internal/private channel state given a table of 624218893Sdim * net80211 channels. We collapse entries for the same frequency 625193323Sed * and record the frequency for doing noise floor processing 626198090Srdivacky * where we don't have net80211 channel context. 627198090Srdivacky */ 628193323Sedstatic HAL_BOOL 629193323SedassignPrivateChannels(struct ath_hal *ah, 630193323Sed struct ieee80211_channel chans[], int nchans, int sku) 631198090Srdivacky{ 632198090Srdivacky HAL_CHANNEL_INTERNAL *ic; 633193323Sed int i, j, next, freq; 634193323Sed 635193323Sed next = 0; 636193323Sed for (i = 0; i < nchans; i++) { 637193323Sed struct ieee80211_channel *c = &chans[i]; 638193323Sed for (j = i-1; j >= 0; j--) 639193323Sed if (chans[j].ic_freq == c->ic_freq) { 640193323Sed c->ic_devdata = chans[j].ic_devdata; 641193323Sed break; 642198090Srdivacky } 643193323Sed if (j < 0) { 644193323Sed /* new entry, assign a private channel entry */ 645193323Sed if (next >= N(AH_PRIVATE(ah)->ah_channels)) { 646193323Sed HALDEBUG(ah, HAL_DEBUG_ANY, 647198090Srdivacky "%s: too many channels, max %zu\n", 648198090Srdivacky __func__, N(AH_PRIVATE(ah)->ah_channels)); 649193323Sed return AH_FALSE; 650193323Sed } 651198090Srdivacky /* 652198090Srdivacky * Handle frequency mapping for 900MHz devices. 653193323Sed * The hardware uses 2.4GHz frequencies that are 654193323Sed * down-converted. The 802.11 layer uses the 655198090Srdivacky * true frequencies. 656198090Srdivacky */ 657193323Sed freq = IEEE80211_IS_CHAN_GSM(c) ? 658193323Sed ath_hal_mapgsm(sku, c->ic_freq) : c->ic_freq; 659193323Sed 660193323Sed HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, 661193323Sed "%s: private[%3u] %u/0x%x -> channel %u\n", 662193323Sed __func__, next, c->ic_freq, c->ic_flags, freq); 663221345Sdim 664221345Sdim ic = &AH_PRIVATE(ah)->ah_channels[next]; 665193323Sed /* 666193323Sed * NB: This clears privFlags which means ancillary 667198090Srdivacky * code like ANI and IQ calibration will be 668198090Srdivacky * restarted and re-setup any per-channel state. 669198090Srdivacky */ 670198090Srdivacky OS_MEMZERO(ic, sizeof(*ic)); 671193323Sed ic->channel = freq; 672193323Sed c->ic_devdata = next; 673193323Sed next++; 674193323Sed } 675193323Sed } 676193323Sed AH_PRIVATE(ah)->ah_nchan = next; 677193323Sed HALDEBUG(ah, HAL_DEBUG_ANY, "%s: %u public, %u private channels\n", 678193323Sed __func__, nchans, next); 679193323Sed return AH_TRUE; 680193323Sed} 681193323Sed 682193323Sed/* 683193323Sed * Setup the channel list based on the information in the EEPROM. 684193323Sed */ 685193323SedHAL_STATUS 686198090Srdivackyath_hal_init_channels(struct ath_hal *ah, 687193323Sed struct ieee80211_channel chans[], u_int maxchans, int *nchans, 688193323Sed u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, 689193323Sed HAL_BOOL enableExtendedChannels) 690198090Srdivacky{ 691193323Sed COUNTRY_CODE_TO_ENUM_RD *country; 692212904Sdim REG_DOMAIN *rd5GHz, *rd2GHz; 693193323Sed HAL_STATUS status; 694193323Sed 695193323Sed status = getchannels(ah, chans, maxchans, nchans, modeSelect, 696193323Sed cc, regDmn, enableExtendedChannels, &country, &rd2GHz, &rd5GHz); 697193323Sed if (status == HAL_OK && 698193323Sed assignPrivateChannels(ah, chans, *nchans, AH_PRIVATE(ah)->ah_currentRD)) { 699193323Sed AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz; 700193323Sed AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz; 701198090Srdivacky 702198090Srdivacky ah->ah_countryCode = country->countryCode; 703193323Sed HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n", 704193323Sed __func__, ah->ah_countryCode); 705193323Sed 706193323Sed /* Update current DFS domain */ 707193323Sed ath_hal_update_dfsdomain(ah); 708221345Sdim } else 709221345Sdim status = HAL_EINVAL; 710221345Sdim 711221345Sdim return status; 712221345Sdim} 713221345Sdim 714193323Sed/* 715193323Sed * Set the channel list. 716193323Sed */ 717193323SedHAL_STATUS 718193323Sedath_hal_set_channels(struct ath_hal *ah, 719224145Sdim struct ieee80211_channel chans[], int nchans, 720193323Sed HAL_CTRY_CODE cc, HAL_REG_DOMAIN rd) 721224145Sdim{ 722193323Sed COUNTRY_CODE_TO_ENUM_RD *country; 723193323Sed REG_DOMAIN *rd5GHz, *rd2GHz; 724193323Sed HAL_STATUS status; 725203954Srdivacky 726193323Sed switch (rd) { 727198090Srdivacky case SKU_SR9: 728198090Srdivacky case SKU_XR9: 729193323Sed case SKU_GZ901: 730193323Sed /* 731193323Sed * Map 900MHz sku's. The frequencies will be mapped 732193323Sed * according to the sku to compensate for the down-converter. 733193323Sed * We use the FCC for these sku's as the mapped channel 734212904Sdim * list is known compatible (will need to change if/when 735198090Srdivacky * vendors do different mapping in different locales). 736193323Sed */ 737212904Sdim status = getregstate(ah, CTRY_DEFAULT, SKU_FCC, 738212904Sdim &country, &rd2GHz, &rd5GHz); 739212904Sdim break; 740212904Sdim default: 741212904Sdim status = getregstate(ah, cc, rd, 742212904Sdim &country, &rd2GHz, &rd5GHz); 743203954Srdivacky rd = AH_PRIVATE(ah)->ah_currentRD; 744193323Sed break; 745193323Sed } 746198090Srdivacky if (status == HAL_OK && assignPrivateChannels(ah, chans, nchans, rd)) { 747207618Srdivacky AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz; 748193323Sed AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz; 749212904Sdim 750205218Srdivacky ah->ah_countryCode = country->countryCode; 751203954Srdivacky HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n", 752205218Srdivacky __func__, ah->ah_countryCode); 753193323Sed } else 754212904Sdim status = HAL_EINVAL; 755203954Srdivacky 756203954Srdivacky if (status == HAL_OK) { 757193323Sed /* Update current DFS domain */ 758193323Sed (void) ath_hal_update_dfsdomain(ah); 759193323Sed } 760193323Sed return status; 761203954Srdivacky} 762193323Sed 763193323Sed#ifdef AH_DEBUG 764193323Sed/* 765193323Sed * Return the internal channel corresponding to a public channel. 766193323Sed * NB: normally this routine is inline'd (see ah_internal.h) 767193323Sed */ 768193323SedHAL_CHANNEL_INTERNAL * 769193323Sedath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c) 770193323Sed{ 771198090Srdivacky HAL_CHANNEL_INTERNAL *cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata]; 772193323Sed 773193323Sed if (c->ic_devdata < AH_PRIVATE(ah)->ah_nchan && 774198090Srdivacky (c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c))) 775198090Srdivacky return cc; 776198090Srdivacky if (c->ic_devdata >= AH_PRIVATE(ah)->ah_nchan) { 777198090Srdivacky HALDEBUG(ah, HAL_DEBUG_ANY, 778193323Sed "%s: bad mapping, devdata %u nchans %u\n", 779202375Srdivacky __func__, c->ic_devdata, AH_PRIVATE(ah)->ah_nchan); 780202375Srdivacky HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan); 781202375Srdivacky } else { 782202375Srdivacky HALDEBUG(ah, HAL_DEBUG_ANY, 783202375Srdivacky "%s: no match for %u/0x%x devdata %u channel %u\n", 784193323Sed __func__, c->ic_freq, c->ic_flags, c->ic_devdata, 785198090Srdivacky cc->channel); 786198090Srdivacky HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)); 787198090Srdivacky } 788193323Sed return AH_NULL; 789198090Srdivacky} 790198090Srdivacky#endif /* AH_DEBUG */ 791198090Srdivacky 792199481Srdivacky#define isWwrSKU(_ah) \ 793198090Srdivacky ((getEepromRD((_ah)) & WORLD_SKU_MASK) == WORLD_SKU_PREFIX || \ 794198090Srdivacky getEepromRD(_ah) == WORLD) 795198090Srdivacky 796198090Srdivacky/* 797198090Srdivacky * Return the test group for the specific channel based on 798198090Srdivacky * the current regulatory setup. 799198090Srdivacky */ 800203954Srdivackyu_int 801203954Srdivackyath_hal_getctl(struct ath_hal *ah, const struct ieee80211_channel *c) 802203954Srdivacky{ 803203954Srdivacky u_int ctl; 804203954Srdivacky 805203954Srdivacky if (AH_PRIVATE(ah)->ah_rd2GHz == AH_PRIVATE(ah)->ah_rd5GHz || 806198090Srdivacky (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah))) 807198090Srdivacky ctl = SD_NO_CTL; 808212904Sdim else if (IEEE80211_IS_CHAN_2GHZ(c)) 809212904Sdim ctl = AH_PRIVATE(ah)->ah_rd2GHz->conformanceTestLimit; 810198090Srdivacky else 811198090Srdivacky ctl = AH_PRIVATE(ah)->ah_rd5GHz->conformanceTestLimit; 812198090Srdivacky if (IEEE80211_IS_CHAN_B(c)) 813203954Srdivacky return ctl | CTL_11B; 814198090Srdivacky if (IEEE80211_IS_CHAN_G(c)) 815203954Srdivacky return ctl | CTL_11G; 816193323Sed if (IEEE80211_IS_CHAN_108G(c)) 817198090Srdivacky return ctl | CTL_108G; 818198090Srdivacky if (IEEE80211_IS_CHAN_TURBO(c)) 819198090Srdivacky return ctl | CTL_TURBO; 820226633Sdim if (IEEE80211_IS_CHAN_A(c)) 821226633Sdim return ctl | CTL_11A; 822193323Sed return ctl; 823198090Srdivacky} 824198090Srdivacky 825198090Srdivacky 826198090Srdivacky/* 827203954Srdivacky * Update the current dfsDomain setting based on the given 828198090Srdivacky * country code. 829198090Srdivacky * 830198090Srdivacky * Since FreeBSD/net80211 allows the channel set to change 831193323Sed * after the card has been setup (via ath_hal_init_channels()) 832198090Srdivacky * this function method is needed to update ah_dfsDomain. 833198090Srdivacky */ 834198090Srdivackyvoid 835198090Srdivackyath_hal_update_dfsdomain(struct ath_hal *ah) 836198090Srdivacky{ 837198090Srdivacky const REG_DOMAIN *rd5GHz = AH_PRIVATE(ah)->ah_rd5GHz; 838198090Srdivacky HAL_CTRY_CODE cc = ah->ah_countryCode; 839198090Srdivacky HAL_DFS_DOMAIN dfsDomain = HAL_DFS_UNINIT_DOMAIN; 840198090Srdivacky HAL_REG_DOMAIN regDmn = AH_PRIVATE(ah)->ah_currentRD; 841198090Srdivacky 842198090Srdivacky HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s CC: %d, RegDmn: %d\n",__func__, 843199481Srdivacky cc, regDmn); 844198090Srdivacky if (rd5GHz->dfsMask & DFS_FCC3) 845198090Srdivacky dfsDomain = HAL_DFS_FCC_DOMAIN; 846198090Srdivacky if (rd5GHz->dfsMask & DFS_ETSI) 847198090Srdivacky dfsDomain = HAL_DFS_ETSI_DOMAIN; 848198090Srdivacky if (rd5GHz->dfsMask & DFS_MKK4) 849198090Srdivacky dfsDomain = HAL_DFS_MKK4_DOMAIN; 850193323Sed AH_PRIVATE(ah)->ah_dfsDomain = dfsDomain; 851198090Srdivacky HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s ah_dfsDomain: %d\n", 852193323Sed __func__, AH_PRIVATE(ah)->ah_dfsDomain); 853193323Sed} 854193323Sed 855193323Sed 856226633Sdim/* 857193323Sed * Return the max allowed antenna gain and apply any regulatory 858193323Sed * domain specific changes. 859198090Srdivacky * 860203954Srdivacky * NOTE: a negative reduction is possible in RD's that only 861193323Sed * measure radiated power (e.g., ETSI) which would increase 862193323Sed * that actual conducted output power (though never beyond 863193323Sed * the calibrated target power). 864193323Sed */ 865193323Sedu_int 866226633Sdimath_hal_getantennareduction(struct ath_hal *ah, 867193323Sed const struct ieee80211_channel *chan, u_int twiceGain) 868210299Sed{ 869193323Sed int8_t antennaMax = twiceGain - chan->ic_maxantgain*2; 870198090Srdivacky return (antennaMax < 0) ? 0 : antennaMax; 871203954Srdivacky} 872193323Sed