Path: blob/main/sys/dev/ath/ath_hal/ah_regdomain.c
39536 views
/*-1* SPDX-License-Identifier: ISC2*3* Copyright (c) 2002-2009 Sam Leffler, Errno Consulting4* Copyright (c) 2005-2006 Atheros Communications, Inc.5* All rights reserved.6*7* Permission to use, copy, modify, and/or distribute this software for any8* purpose with or without fee is hereby granted, provided that the above9* copyright notice and this permission notice appear in all copies.10*11* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES12* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF13* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR14* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES15* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN16* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF17* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.18*/19#include "opt_ah.h"2021#include "ah.h"2223#include <net80211/_ieee80211.h>24#include <net80211/ieee80211_regdomain.h>2526#include "ah_internal.h"27#include "ah_eeprom.h"28#include "ah_devid.h"2930#include "ah_regdomain.h"3132/*33* XXX this code needs a audit+review34*/3536/* used throughout this file... */37#define N(a) nitems(a)3839#define HAL_MODE_11A_TURBO HAL_MODE_108A40#define HAL_MODE_11G_TURBO HAL_MODE_108G4142/*43* Mask to check whether a domain is a multidomain or a single domain44*/45#define MULTI_DOMAIN_MASK 0xFF004647/*48* Enumerated Regulatory Domain Information 8 bit values indicate that49* the regdomain is really a pair of unitary regdomains. 12 bit values50* are the real unitary regdomains and are the only ones which have the51* frequency bitmasks and flags set.52*/53#include "ah_regdomain/ah_rd_regenum.h"5455#define WORLD_SKU_MASK 0x00F056#define WORLD_SKU_PREFIX 0x00605758/*59* THE following table is the mapping of regdomain pairs specified by60* an 8 bit regdomain value to the individual unitary reg domains61*/62#include "ah_regdomain/ah_rd_regmap.h"6364/*65* The following tables are the master list for all different freqeuncy66* bands with the complete matrix of all possible flags and settings67* for each band if it is used in ANY reg domain.68*/6970#define COUNTRY_ERD_FLAG 0x800071#define WORLDWIDE_ROAMING_FLAG 0x40007273/*74* This table maps country ISO codes from net80211 into regulatory75* domains which the ath regulatory domain code understands.76*/77#include "ah_regdomain/ah_rd_ctry.h"7879/*80* The frequency band collections are a set of frequency ranges81* with shared properties - max tx power, max antenna gain, channel width,82* channel spacing, DFS requirements and passive scanning requirements.83*84* These are represented as entries in a frequency band bitmask.85* Each regulatory domain entry in ah_regdomain_domains.h uses one86* or more frequency band entries for each of the channel modes87* supported (11bg, 11a, half, quarter, turbo, etc.)88*89*/90#include "ah_regdomain/ah_rd_freqbands.h"9192/*93* This is the main regulatory database. It defines the supported94* set of features and requirements for each of the defined regulatory95* zones. It uses combinations of frequency ranges - represented in96* a bitmask - to determine the requirements and limitations needed.97*/98#include "ah_regdomain/ah_rd_domains.h"99100static const struct cmode modes[] = {101{ HAL_MODE_TURBO, IEEE80211_CHAN_ST, ®Dmn5GhzTurboFreq[0] },102{ HAL_MODE_11A, IEEE80211_CHAN_A, ®Dmn5GhzFreq[0] },103{ HAL_MODE_11B, IEEE80211_CHAN_B, ®Dmn2GhzFreq[0] },104{ HAL_MODE_11G, IEEE80211_CHAN_G, ®Dmn2Ghz11gFreq[0] },105{ HAL_MODE_11G_TURBO, IEEE80211_CHAN_108G, ®Dmn2Ghz11gTurboFreq[0] },106{ HAL_MODE_11A_TURBO, IEEE80211_CHAN_108A, ®Dmn5GhzTurboFreq[0] },107{ HAL_MODE_11A_QUARTER_RATE,108IEEE80211_CHAN_A | IEEE80211_CHAN_QUARTER, ®Dmn5GhzFreq[0] },109{ HAL_MODE_11A_HALF_RATE,110IEEE80211_CHAN_A | IEEE80211_CHAN_HALF, ®Dmn5GhzFreq[0] },111{ HAL_MODE_11G_QUARTER_RATE,112IEEE80211_CHAN_G | IEEE80211_CHAN_QUARTER, ®Dmn2Ghz11gFreq[0] },113{ HAL_MODE_11G_HALF_RATE,114IEEE80211_CHAN_G | IEEE80211_CHAN_HALF, ®Dmn2Ghz11gFreq[0] },115{ HAL_MODE_11NG_HT20,116IEEE80211_CHAN_G | IEEE80211_CHAN_HT20, ®Dmn2Ghz11gFreq[0] },117{ HAL_MODE_11NG_HT40PLUS,118IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U, ®Dmn2Ghz11gFreq[0] },119{ HAL_MODE_11NG_HT40MINUS,120IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D, ®Dmn2Ghz11gFreq[0] },121{ HAL_MODE_11NA_HT20,122IEEE80211_CHAN_A | IEEE80211_CHAN_HT20, ®Dmn5GhzFreq[0] },123{ HAL_MODE_11NA_HT40PLUS,124IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U, ®Dmn5GhzFreq[0] },125{ HAL_MODE_11NA_HT40MINUS,126IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D, ®Dmn5GhzFreq[0] },127};128129static void ath_hal_update_dfsdomain(struct ath_hal *ah);130131static OS_INLINE uint16_t132getEepromRD(struct ath_hal *ah)133{134return AH_PRIVATE(ah)->ah_currentRD &~ WORLDWIDE_ROAMING_FLAG;135}136137/*138* Test to see if the bitmask array is all zeros139*/140static HAL_BOOL141isChanBitMaskZero(const uint64_t *bitmask)142{143#if BMLEN > 2144#error "add more cases"145#endif146#if BMLEN > 1147if (bitmask[1] != 0)148return AH_FALSE;149#endif150return (bitmask[0] == 0);151}152153/*154* Return whether or not the regulatory domain/country in EEPROM155* is acceptable.156*/157static HAL_BOOL158isEepromValid(struct ath_hal *ah)159{160uint16_t rd = getEepromRD(ah);161int i;162163if (rd & COUNTRY_ERD_FLAG) {164uint16_t cc = rd &~ COUNTRY_ERD_FLAG;165for (i = 0; i < N(allCountries); i++)166if (allCountries[i].countryCode == cc)167return AH_TRUE;168} else {169for (i = 0; i < N(regDomainPairs); i++)170if (regDomainPairs[i].regDmnEnum == rd)171return AH_TRUE;172}173174if (rd == FCC_UBNT) {175return AH_TRUE;176}177178HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,179"%s: invalid regulatory domain/country code 0x%x\n", __func__, rd);180return AH_FALSE;181}182183/*184* Find the pointer to the country element in the country table185* corresponding to the country code186*/187static COUNTRY_CODE_TO_ENUM_RD*188findCountry(HAL_CTRY_CODE countryCode)189{190int i;191192for (i = 0; i < N(allCountries); i++) {193if (allCountries[i].countryCode == countryCode)194return &allCountries[i];195}196return AH_NULL;197}198199static REG_DOMAIN *200findRegDmn(int regDmn)201{202int i;203204for (i = 0; i < N(regDomains); i++) {205if (regDomains[i].regDmnEnum == regDmn)206return ®Domains[i];207}208return AH_NULL;209}210211static REG_DMN_PAIR_MAPPING *212findRegDmnPair(int regDmnPair)213{214int i;215216if (regDmnPair != NO_ENUMRD) {217for (i = 0; i < N(regDomainPairs); i++) {218if (regDomainPairs[i].regDmnEnum == regDmnPair)219return ®DomainPairs[i];220}221}222return AH_NULL;223}224225/*226* Calculate a default country based on the EEPROM setting.227*/228static HAL_CTRY_CODE229getDefaultCountry(struct ath_hal *ah)230{231REG_DMN_PAIR_MAPPING *regpair;232uint16_t rd;233234rd = getEepromRD(ah);235if (rd & COUNTRY_ERD_FLAG) {236COUNTRY_CODE_TO_ENUM_RD *country;237uint16_t cc = rd & ~COUNTRY_ERD_FLAG;238country = findCountry(cc);239if (country != AH_NULL)240return cc;241}242/*243* Check reg domains that have only one country244*/245regpair = findRegDmnPair(rd);246return (regpair != AH_NULL) ? regpair->singleCC : CTRY_DEFAULT;247}248249static HAL_BOOL250IS_BIT_SET(int bit, const uint64_t bitmask[])251{252int byteOffset, bitnum;253uint64_t val;254255byteOffset = bit/64;256bitnum = bit - byteOffset*64;257val = ((uint64_t) 1) << bitnum;258return (bitmask[byteOffset] & val) != 0;259}260261static HAL_STATUS262getregstate(struct ath_hal *ah, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,263COUNTRY_CODE_TO_ENUM_RD **pcountry,264REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz)265{266COUNTRY_CODE_TO_ENUM_RD *country;267REG_DOMAIN *rd5GHz, *rd2GHz;268269if (cc == CTRY_DEFAULT && regDmn == SKU_NONE) {270/*271* Validate the EEPROM setting and setup defaults272*/273if (!isEepromValid(ah)) {274/*275* Don't return any channels if the EEPROM has an276* invalid regulatory domain/country code setting.277*/278HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,279"%s: invalid EEPROM contents\n",__func__);280return HAL_EEBADREG;281}282283cc = getDefaultCountry(ah);284country = findCountry(cc);285if (country == AH_NULL) {286HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,287"NULL Country!, cc %d\n", cc);288return HAL_EEBADCC;289}290regDmn = country->regDmnEnum;291HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: EEPROM cc %u rd 0x%x\n",292__func__, cc, regDmn);293294if (country->countryCode == CTRY_DEFAULT) {295/*296* Check EEPROM; SKU may be for a country, single297* domain, or multiple domains (WWR).298*/299uint16_t rdnum = getEepromRD(ah);300if ((rdnum & COUNTRY_ERD_FLAG) == 0 &&301(findRegDmn(rdnum) != AH_NULL ||302findRegDmnPair(rdnum) != AH_NULL)) {303regDmn = rdnum;304HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,305"%s: EEPROM rd 0x%x\n", __func__, rdnum);306}307}308} else {309country = findCountry(cc);310if (country == AH_NULL) {311HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,312"unknown country, cc %d\n", cc);313return HAL_EINVAL;314}315if (regDmn == SKU_NONE)316regDmn = country->regDmnEnum;317HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u rd 0x%x\n",318__func__, cc, regDmn);319}320321/*322* Setup per-band state.323*/324if ((regDmn & MULTI_DOMAIN_MASK) == 0) {325REG_DMN_PAIR_MAPPING *regpair = findRegDmnPair(regDmn);326if (regpair == AH_NULL) {327HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,328"%s: no reg domain pair %u for country %u\n",329__func__, regDmn, country->countryCode);330return HAL_EINVAL;331}332rd5GHz = findRegDmn(regpair->regDmn5GHz);333if (rd5GHz == AH_NULL) {334HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,335"%s: no 5GHz reg domain %u for country %u\n",336__func__, regpair->regDmn5GHz, country->countryCode);337return HAL_EINVAL;338}339rd2GHz = findRegDmn(regpair->regDmn2GHz);340if (rd2GHz == AH_NULL) {341HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,342"%s: no 2GHz reg domain %u for country %u\n",343__func__, regpair->regDmn2GHz, country->countryCode);344return HAL_EINVAL;345}346} else {347rd5GHz = rd2GHz = findRegDmn(regDmn);348if (rd2GHz == AH_NULL) {349HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,350"%s: no unitary reg domain %u for country %u\n",351__func__, regDmn, country->countryCode);352return HAL_EINVAL;353}354}355if (pcountry != AH_NULL)356*pcountry = country;357*prd2GHz = rd2GHz;358*prd5GHz = rd5GHz;359return HAL_OK;360}361362static uint64_t *363getchannelBM(u_int mode, REG_DOMAIN *rd)364{365switch (mode) {366case HAL_MODE_11B:367return (rd->chan11b);368case HAL_MODE_11G_QUARTER_RATE:369return (rd->chan11g_quarter);370case HAL_MODE_11G_HALF_RATE:371return (rd->chan11g_half);372case HAL_MODE_11G:373case HAL_MODE_11NG_HT20:374case HAL_MODE_11NG_HT40PLUS:375case HAL_MODE_11NG_HT40MINUS:376return (rd->chan11g);377case HAL_MODE_11G_TURBO:378return (rd->chan11g_turbo);379case HAL_MODE_11A_QUARTER_RATE:380return (rd->chan11a_quarter);381case HAL_MODE_11A_HALF_RATE:382return (rd->chan11a_half);383case HAL_MODE_11A:384case HAL_MODE_11NA_HT20:385case HAL_MODE_11NA_HT40PLUS:386case HAL_MODE_11NA_HT40MINUS:387return (rd->chan11a);388case HAL_MODE_TURBO:389return (rd->chan11a_turbo);390case HAL_MODE_11A_TURBO:391return (rd->chan11a_dyn_turbo);392default:393return (AH_NULL);394}395}396397static void398setchannelflags(struct ieee80211_channel *c, REG_DMN_FREQ_BAND *fband,399REG_DOMAIN *rd)400{401if (fband->usePassScan & rd->pscan)402c->ic_flags |= IEEE80211_CHAN_PASSIVE;403if (fband->useDfs & rd->dfsMask)404c->ic_flags |= IEEE80211_CHAN_DFS;405if (IEEE80211_IS_CHAN_5GHZ(c) && (rd->flags & DISALLOW_ADHOC_11A))406c->ic_flags |= IEEE80211_CHAN_NOADHOC;407if (IEEE80211_IS_CHAN_TURBO(c) &&408(rd->flags & DISALLOW_ADHOC_11A_TURB))409c->ic_flags |= IEEE80211_CHAN_NOADHOC;410if (rd->flags & NO_HOSTAP)411c->ic_flags |= IEEE80211_CHAN_NOHOSTAP;412if (rd->flags & LIMIT_FRAME_4MS)413c->ic_flags |= IEEE80211_CHAN_4MSXMIT;414if (rd->flags & NEED_NFC)415c->ic_flags |= CHANNEL_NFCREQUIRED;416}417418static int419addchan(struct ath_hal *ah, struct ieee80211_channel chans[],420u_int maxchans, int *nchans, uint16_t freq, uint32_t flags,421REG_DMN_FREQ_BAND *fband, REG_DOMAIN *rd)422{423struct ieee80211_channel *c;424425if (*nchans >= maxchans)426return (HAL_ENOMEM);427428HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,429"%s: %d: freq=%d, flags=0x%08x\n",430__func__, *nchans, (int) freq, flags);431432c = &chans[(*nchans)++];433c->ic_freq = freq;434c->ic_flags = flags;435setchannelflags(c, fband, rd);436c->ic_maxregpower = fband->powerDfs;437ath_hal_getpowerlimits(ah, c);438c->ic_maxantgain = fband->antennaMax;439440return (0);441}442443static int444copychan_prev(struct ath_hal *ah, struct ieee80211_channel chans[],445u_int maxchans, int *nchans, uint16_t freq, uint32_t flags)446{447struct ieee80211_channel *c;448449if (*nchans == 0)450return (HAL_EINVAL);451452if (*nchans >= maxchans)453return (HAL_ENOMEM);454455HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,456"%s: %d: freq=%d, flags=0x%08x\n",457__func__, *nchans, (int) freq, flags);458459c = &chans[(*nchans)++];460c[0] = c[-1];461c->ic_freq = freq;462/* XXX is it needed here? */463ath_hal_getpowerlimits(ah, c);464465return (0);466}467468static int469add_chanlist_band(struct ath_hal *ah, struct ieee80211_channel chans[],470int maxchans, int *nchans, uint16_t freq_lo, uint16_t freq_hi, int step,471uint32_t flags, REG_DMN_FREQ_BAND *fband, REG_DOMAIN *rd)472{473uint16_t freq = freq_lo;474int error;475476if (freq_hi < freq_lo)477return (0);478479HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,480"%s: freq=%d..%d, flags=0x%08x, step=%d\n", __func__,481(int) freq_lo, (int) freq_hi, flags, step);482483error = addchan(ah, chans, maxchans, nchans, freq, flags, fband, rd);484for (freq += step; freq <= freq_hi && error == 0; freq += step)485error = copychan_prev(ah, chans, maxchans, nchans, freq, flags);486487return (error);488}489490static void491adj_freq_ht40(u_int mode, int *low_adj, int *hi_adj, int *channelSep)492{493494*low_adj = *hi_adj = *channelSep = 0;495switch (mode) {496case HAL_MODE_11NA_HT40PLUS:497*channelSep = 40;498/* FALLTHROUGH */499case HAL_MODE_11NG_HT40PLUS:500*hi_adj = -20;501break;502case HAL_MODE_11NA_HT40MINUS:503*channelSep = 40;504/* FALLTHROUGH */505case HAL_MODE_11NG_HT40MINUS:506*low_adj = 20;507break;508}509}510511static void512add_chanlist_mode(struct ath_hal *ah, struct ieee80211_channel chans[],513u_int maxchans, int *nchans, const struct cmode *cm, REG_DOMAIN *rd,514HAL_BOOL enableExtendedChannels)515{516uint64_t *channelBM;517uint16_t freq_lo, freq_hi;518int b, error, low_adj, hi_adj, channelSep;519520if (!ath_hal_getChannelEdges(ah, cm->flags, &freq_lo, &freq_hi)) {521/* channel not supported by hardware, skip it */522HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,523"%s: channels 0x%x not supported by hardware\n",524__func__, cm->flags);525return;526}527528channelBM = getchannelBM(cm->mode, rd);529if (isChanBitMaskZero(channelBM))530return;531532/*533* Setup special handling for HT40 channels; e.g.534* 5G HT40 channels require 40Mhz channel separation.535*/536adj_freq_ht40(cm->mode, &low_adj, &hi_adj, &channelSep);537538for (b = 0; b < 64*BMLEN; b++) {539REG_DMN_FREQ_BAND *fband;540uint16_t bfreq_lo, bfreq_hi;541int step;542543if (!IS_BIT_SET(b, channelBM))544continue;545fband = &cm->freqs[b];546547if ((fband->usePassScan & IS_ECM_CHAN) &&548!enableExtendedChannels) {549HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,550"skip ecm channels\n");551continue;552}553#if 0554if ((fband->useDfs & rd->dfsMask) &&555(cm->flags & IEEE80211_CHAN_HT40)) {556/* NB: DFS and HT40 don't mix */557HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,558"skip HT40 chan, DFS required\n");559continue;560}561#endif562/*563* XXX TODO: handle REG_EXT_FCC_CH_144.564*565* Figure out which instances/uses cause us to not566* be allowed to use channel 144 (pri or sec overlap.)567*/568569bfreq_lo = MAX(fband->lowChannel + low_adj, freq_lo);570bfreq_hi = MIN(fband->highChannel + hi_adj, freq_hi);571572/*573* Don't start the 5GHz channel list at 5120MHz.574*575* Unfortunately (sigh) the HT40 channel creation576* logic will create HT40U channels at 5120, 5160, 5200.577* This means that 36 (5180) isn't considered as a578* HT40 channel, and everything goes messed up from there.579*/580if ((cm->flags & IEEE80211_CHAN_5GHZ) &&581(cm->flags & IEEE80211_CHAN_HT40U)) {582if (bfreq_lo < 5180)583bfreq_lo = 5180;584}585586/*587* Same with HT40D - need to start at 5200 or the low588* channels are all wrong again.589*/590if ((cm->flags & IEEE80211_CHAN_5GHZ) &&591(cm->flags & IEEE80211_CHAN_HT40D)) {592if (bfreq_lo < 5200)593bfreq_lo = 5200;594}595596if (fband->channelSep >= channelSep)597step = fband->channelSep;598else599step = roundup(channelSep, fband->channelSep);600601HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,602"%s: freq_lo=%d, freq_hi=%d, low_adj=%d, hi_adj=%d, "603"bandlo=%d, bandhi=%d, bfreqlo=%d, bfreqhi=%d, step=%d, "604"flags=0x%08x\n",605__func__,606(int) freq_lo,607(int) freq_hi,608(int) low_adj,609(int) hi_adj,610(int) fband->lowChannel,611(int) fband->highChannel,612(int) bfreq_lo,613(int) bfreq_hi,614step,615(int) cm->flags);616617error = add_chanlist_band(ah, chans, maxchans, nchans,618bfreq_lo, bfreq_hi, step, cm->flags, fband, rd);619if (error != 0) {620HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,621"%s: too many channels for channel table\n",622__func__);623return;624}625}626}627628static u_int629getmodesmask(struct ath_hal *ah, REG_DOMAIN *rd5GHz, u_int modeSelect)630{631#define HAL_MODE_11A_ALL \632(HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \633HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE)634u_int modesMask;635636/* get modes that HW is capable of */637modesMask = ath_hal_getWirelessModes(ah);638modesMask &= modeSelect;639/* optimize work below if no 11a channels */640if (isChanBitMaskZero(rd5GHz->chan11a) &&641(modesMask & HAL_MODE_11A_ALL)) {642HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,643"%s: disallow all 11a\n", __func__);644modesMask &= ~HAL_MODE_11A_ALL;645}646647return (modesMask);648#undef HAL_MODE_11A_ALL649}650651/*652* Construct the channel list for the specified regulatory config.653*/654static HAL_STATUS655getchannels(struct ath_hal *ah,656struct ieee80211_channel chans[], u_int maxchans, int *nchans,657u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,658HAL_BOOL enableExtendedChannels,659COUNTRY_CODE_TO_ENUM_RD **pcountry,660REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz)661{662REG_DOMAIN *rd5GHz, *rd2GHz;663u_int modesMask;664const struct cmode *cm;665HAL_STATUS status;666667HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u regDmn 0x%x mode 0x%x%s\n",668__func__, cc, regDmn, modeSelect,669enableExtendedChannels ? " ecm" : "");670671status = getregstate(ah, cc, regDmn, pcountry, &rd2GHz, &rd5GHz);672if (status != HAL_OK)673return status;674675modesMask = getmodesmask(ah, rd5GHz, modeSelect);676/* XXX error? */677if (modesMask == 0)678goto done;679680for (cm = modes; cm < &modes[N(modes)]; cm++) {681REG_DOMAIN *rd;682683if ((cm->mode & modesMask) == 0) {684HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,685"%s: skip mode 0x%x flags 0x%x\n",686__func__, cm->mode, cm->flags);687continue;688}689690if (cm->flags & IEEE80211_CHAN_5GHZ)691rd = rd5GHz;692else if (cm->flags & IEEE80211_CHAN_2GHZ)693rd = rd2GHz;694else {695ath_hal_printf(ah, "%s: Unknown HAL flags 0x%x\n",696__func__, cm->flags);697return HAL_EINVAL;698}699700add_chanlist_mode(ah, chans, maxchans, nchans, cm,701rd, enableExtendedChannels);702if (*nchans >= maxchans)703goto done;704}705done:706/* NB: pcountry set above by getregstate */707if (prd2GHz != AH_NULL)708*prd2GHz = rd2GHz;709if (prd5GHz != AH_NULL)710*prd5GHz = rd5GHz;711return HAL_OK;712}713714/*715* Retrieve a channel list without affecting runtime state.716*/717HAL_STATUS718ath_hal_getchannels(struct ath_hal *ah,719struct ieee80211_channel chans[], u_int maxchans, int *nchans,720u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,721HAL_BOOL enableExtendedChannels)722{723return getchannels(ah, chans, maxchans, nchans, modeSelect,724cc, regDmn, enableExtendedChannels, AH_NULL, AH_NULL, AH_NULL);725}726727/*728* Handle frequency mapping from 900Mhz range to 2.4GHz range729* for GSM radios. This is done when we need the h/w frequency730* and the channel is marked IEEE80211_CHAN_GSM.731*/732static int733ath_hal_mapgsm(int sku, int freq)734{735if (sku == SKU_XR9)736return 1520 + freq;737if (sku == SKU_GZ901)738return 1544 + freq;739if (sku == SKU_SR9)740return 3344 - freq;741if (sku == SKU_XC900M)742return 1517 + freq;743HALDEBUG(AH_NULL, HAL_DEBUG_ANY,744"%s: cannot map freq %u unknown gsm sku %u\n",745__func__, freq, sku);746return freq;747}748749/*750* Setup the internal/private channel state given a table of751* net80211 channels. We collapse entries for the same frequency752* and record the frequency for doing noise floor processing753* where we don't have net80211 channel context.754*/755static HAL_BOOL756assignPrivateChannels(struct ath_hal *ah,757struct ieee80211_channel chans[], int nchans, int sku)758{759HAL_CHANNEL_INTERNAL *ic;760int i, j, next, freq;761762next = 0;763for (i = 0; i < nchans; i++) {764struct ieee80211_channel *c = &chans[i];765for (j = i-1; j >= 0; j--)766if (chans[j].ic_freq == c->ic_freq) {767c->ic_devdata = chans[j].ic_devdata;768break;769}770if (j < 0) {771/* new entry, assign a private channel entry */772if (next >= N(AH_PRIVATE(ah)->ah_channels)) {773HALDEBUG(ah, HAL_DEBUG_ANY,774"%s: too many channels, max %zu\n",775__func__, N(AH_PRIVATE(ah)->ah_channels));776return AH_FALSE;777}778/*779* Handle frequency mapping for 900MHz devices.780* The hardware uses 2.4GHz frequencies that are781* down-converted. The 802.11 layer uses the782* true frequencies.783*/784freq = IEEE80211_IS_CHAN_GSM(c) ?785ath_hal_mapgsm(sku, c->ic_freq) : c->ic_freq;786787HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,788"%s: private[%3u] %u/0x%x -> channel %u\n",789__func__, next, c->ic_freq, c->ic_flags, freq);790791ic = &AH_PRIVATE(ah)->ah_channels[next];792/*793* NB: This clears privFlags which means ancillary794* code like ANI and IQ calibration will be795* restarted and re-setup any per-channel state.796*/797OS_MEMZERO(ic, sizeof(*ic));798ic->channel = freq;799c->ic_devdata = next;800next++;801}802}803AH_PRIVATE(ah)->ah_nchan = next;804HALDEBUG(ah, HAL_DEBUG_ANY, "%s: %u public, %u private channels\n",805__func__, nchans, next);806return AH_TRUE;807}808809/*810* Setup the channel list based on the information in the EEPROM.811*/812HAL_STATUS813ath_hal_init_channels(struct ath_hal *ah,814struct ieee80211_channel chans[], u_int maxchans, int *nchans,815u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,816HAL_BOOL enableExtendedChannels)817{818COUNTRY_CODE_TO_ENUM_RD *country;819REG_DOMAIN *rd5GHz, *rd2GHz;820HAL_STATUS status;821822status = getchannels(ah, chans, maxchans, nchans, modeSelect,823cc, regDmn, enableExtendedChannels, &country, &rd2GHz, &rd5GHz);824if (status == HAL_OK &&825assignPrivateChannels(ah, chans, *nchans, AH_PRIVATE(ah)->ah_currentRD)) {826AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz;827AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz;828829ah->ah_countryCode = country->countryCode;830HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n",831__func__, ah->ah_countryCode);832833/* Update current DFS domain */834ath_hal_update_dfsdomain(ah);835} else836status = HAL_EINVAL;837838return status;839}840841/*842* Set the channel list.843*/844HAL_STATUS845ath_hal_set_channels(struct ath_hal *ah,846struct ieee80211_channel chans[], int nchans,847HAL_CTRY_CODE cc, HAL_REG_DOMAIN rd)848{849COUNTRY_CODE_TO_ENUM_RD *country;850REG_DOMAIN *rd5GHz, *rd2GHz;851HAL_STATUS status;852853switch (rd) {854case SKU_SR9:855case SKU_XR9:856case SKU_GZ901:857case SKU_XC900M:858/*859* Map 900MHz sku's. The frequencies will be mapped860* according to the sku to compensate for the down-converter.861* We use the FCC for these sku's as the mapped channel862* list is known compatible (will need to change if/when863* vendors do different mapping in different locales).864*/865status = getregstate(ah, CTRY_DEFAULT, SKU_FCC,866&country, &rd2GHz, &rd5GHz);867break;868default:869status = getregstate(ah, cc, rd,870&country, &rd2GHz, &rd5GHz);871rd = AH_PRIVATE(ah)->ah_currentRD;872break;873}874if (status == HAL_OK && assignPrivateChannels(ah, chans, nchans, rd)) {875AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz;876AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz;877878ah->ah_countryCode = country->countryCode;879HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n",880__func__, ah->ah_countryCode);881} else882status = HAL_EINVAL;883884if (status == HAL_OK) {885/* Update current DFS domain */886(void) ath_hal_update_dfsdomain(ah);887}888return status;889}890891#ifdef AH_DEBUG892/*893* Return the internal channel corresponding to a public channel.894* NB: normally this routine is inline'd (see ah_internal.h)895*/896HAL_CHANNEL_INTERNAL *897ath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c)898{899HAL_CHANNEL_INTERNAL *cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata];900901if (c->ic_devdata < AH_PRIVATE(ah)->ah_nchan &&902(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)))903return cc;904if (c->ic_devdata >= AH_PRIVATE(ah)->ah_nchan) {905HALDEBUG(ah, HAL_DEBUG_ANY,906"%s: bad mapping, devdata %u nchans %u\n",907__func__, c->ic_devdata, AH_PRIVATE(ah)->ah_nchan);908HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan);909} else {910HALDEBUG(ah, HAL_DEBUG_ANY,911"%s: no match for %u/0x%x devdata %u channel %u\n",912__func__, c->ic_freq, c->ic_flags, c->ic_devdata,913cc->channel);914HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c));915}916return AH_NULL;917}918#endif /* AH_DEBUG */919920#define isWwrSKU(_ah) \921((getEepromRD((_ah)) & WORLD_SKU_MASK) == WORLD_SKU_PREFIX || \922getEepromRD(_ah) == WORLD)923924/*925* Return the test group for the specific channel based on926* the current regulatory setup.927*/928u_int929ath_hal_getctl(struct ath_hal *ah, const struct ieee80211_channel *c)930{931u_int ctl;932933if (AH_PRIVATE(ah)->ah_rd2GHz == AH_PRIVATE(ah)->ah_rd5GHz ||934(ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)))935ctl = SD_NO_CTL;936else if (IEEE80211_IS_CHAN_2GHZ(c))937ctl = AH_PRIVATE(ah)->ah_rd2GHz->conformanceTestLimit;938else939ctl = AH_PRIVATE(ah)->ah_rd5GHz->conformanceTestLimit;940if (IEEE80211_IS_CHAN_B(c))941return ctl | CTL_11B;942if (IEEE80211_IS_CHAN_G(c))943return ctl | CTL_11G;944if (IEEE80211_IS_CHAN_108G(c))945return ctl | CTL_108G;946if (IEEE80211_IS_CHAN_TURBO(c))947return ctl | CTL_TURBO;948if (IEEE80211_IS_CHAN_A(c))949return ctl | CTL_11A;950return ctl;951}952953/*954* Update the current dfsDomain setting based on the given955* country code.956*957* Since FreeBSD/net80211 allows the channel set to change958* after the card has been setup (via ath_hal_init_channels())959* this function method is needed to update ah_dfsDomain.960*/961void962ath_hal_update_dfsdomain(struct ath_hal *ah)963{964const REG_DOMAIN *rd5GHz = AH_PRIVATE(ah)->ah_rd5GHz;965HAL_DFS_DOMAIN dfsDomain = HAL_DFS_UNINIT_DOMAIN;966967if (rd5GHz->dfsMask & DFS_FCC3)968dfsDomain = HAL_DFS_FCC_DOMAIN;969if (rd5GHz->dfsMask & DFS_ETSI)970dfsDomain = HAL_DFS_ETSI_DOMAIN;971if (rd5GHz->dfsMask & DFS_MKK4)972dfsDomain = HAL_DFS_MKK4_DOMAIN;973AH_PRIVATE(ah)->ah_dfsDomain = dfsDomain;974HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s ah_dfsDomain: %d\n",975__func__, AH_PRIVATE(ah)->ah_dfsDomain);976}977978/*979* Return the max allowed antenna gain and apply any regulatory980* domain specific changes.981*982* NOTE: a negative reduction is possible in RD's that only983* measure radiated power (e.g., ETSI) which would increase984* that actual conducted output power (though never beyond985* the calibrated target power).986*/987u_int988ath_hal_getantennareduction(struct ath_hal *ah,989const struct ieee80211_channel *chan, u_int twiceGain)990{991int8_t antennaMax = twiceGain - chan->ic_maxantgain*2;992return (antennaMax < 0) ? 0 : antennaMax;993}994995996