16e778a7eSPedro F. Giffuni /*-
26e778a7eSPedro F. Giffuni * SPDX-License-Identifier: ISC
36e778a7eSPedro F. Giffuni *
459efa8b5SSam Leffler * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
514779705SSam Leffler * Copyright (c) 2005-2006 Atheros Communications, Inc.
614779705SSam Leffler * All rights reserved.
714779705SSam Leffler *
814779705SSam Leffler * Permission to use, copy, modify, and/or distribute this software for any
914779705SSam Leffler * purpose with or without fee is hereby granted, provided that the above
1014779705SSam Leffler * copyright notice and this permission notice appear in all copies.
1114779705SSam Leffler *
1214779705SSam Leffler * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1314779705SSam Leffler * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1414779705SSam Leffler * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1514779705SSam Leffler * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1614779705SSam Leffler * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1714779705SSam Leffler * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1814779705SSam Leffler * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1914779705SSam Leffler */
2014779705SSam Leffler #include "opt_ah.h"
2114779705SSam Leffler
2214779705SSam Leffler #include "ah.h"
2359efa8b5SSam Leffler
2459efa8b5SSam Leffler #include <net80211/_ieee80211.h>
2559efa8b5SSam Leffler #include <net80211/ieee80211_regdomain.h>
2659efa8b5SSam Leffler
2714779705SSam Leffler #include "ah_internal.h"
2814779705SSam Leffler #include "ah_eeprom.h"
2914779705SSam Leffler #include "ah_devid.h"
3014779705SSam Leffler
312836e2aeSAdrian Chadd #include "ah_regdomain.h"
322836e2aeSAdrian Chadd
3314779705SSam Leffler /*
3414779705SSam Leffler * XXX this code needs a audit+review
3514779705SSam Leffler */
3614779705SSam Leffler
3714779705SSam Leffler /* used throughout this file... */
3812d53238SAndriy Voskoboinyk #define N(a) nitems(a)
3914779705SSam Leffler
4014779705SSam Leffler #define HAL_MODE_11A_TURBO HAL_MODE_108A
4114779705SSam Leffler #define HAL_MODE_11G_TURBO HAL_MODE_108G
4214779705SSam Leffler
4314779705SSam Leffler /*
4414779705SSam Leffler * Mask to check whether a domain is a multidomain or a single domain
4514779705SSam Leffler */
4614779705SSam Leffler #define MULTI_DOMAIN_MASK 0xFF00
4714779705SSam Leffler
4814779705SSam Leffler /*
4914779705SSam Leffler * Enumerated Regulatory Domain Information 8 bit values indicate that
5014779705SSam Leffler * the regdomain is really a pair of unitary regdomains. 12 bit values
5114779705SSam Leffler * are the real unitary regdomains and are the only ones which have the
5214779705SSam Leffler * frequency bitmasks and flags set.
5314779705SSam Leffler */
54c48e24c1SAdrian Chadd #include "ah_regdomain/ah_rd_regenum.h"
5514779705SSam Leffler
5614779705SSam Leffler #define WORLD_SKU_MASK 0x00F0
5714779705SSam Leffler #define WORLD_SKU_PREFIX 0x0060
5814779705SSam Leffler
5914779705SSam Leffler /*
6014779705SSam Leffler * THE following table is the mapping of regdomain pairs specified by
6114779705SSam Leffler * an 8 bit regdomain value to the individual unitary reg domains
6214779705SSam Leffler */
63c48e24c1SAdrian Chadd #include "ah_regdomain/ah_rd_regmap.h"
6414779705SSam Leffler
6514779705SSam Leffler /*
6614779705SSam Leffler * The following tables are the master list for all different freqeuncy
6714779705SSam Leffler * bands with the complete matrix of all possible flags and settings
6814779705SSam Leffler * for each band if it is used in ANY reg domain.
6914779705SSam Leffler */
7014779705SSam Leffler
7114779705SSam Leffler #define COUNTRY_ERD_FLAG 0x8000
7214779705SSam Leffler #define WORLDWIDE_ROAMING_FLAG 0x4000
7314779705SSam Leffler
74c48e24c1SAdrian Chadd /*
75c48e24c1SAdrian Chadd * This table maps country ISO codes from net80211 into regulatory
76c48e24c1SAdrian Chadd * domains which the ath regulatory domain code understands.
77c48e24c1SAdrian Chadd */
78c48e24c1SAdrian Chadd #include "ah_regdomain/ah_rd_ctry.h"
7914779705SSam Leffler
8014779705SSam Leffler /*
81c48e24c1SAdrian Chadd * The frequency band collections are a set of frequency ranges
82c48e24c1SAdrian Chadd * with shared properties - max tx power, max antenna gain, channel width,
83c48e24c1SAdrian Chadd * channel spacing, DFS requirements and passive scanning requirements.
8414779705SSam Leffler *
85c48e24c1SAdrian Chadd * These are represented as entries in a frequency band bitmask.
86c48e24c1SAdrian Chadd * Each regulatory domain entry in ah_regdomain_domains.h uses one
87c48e24c1SAdrian Chadd * or more frequency band entries for each of the channel modes
88c48e24c1SAdrian Chadd * supported (11bg, 11a, half, quarter, turbo, etc.)
89c48e24c1SAdrian Chadd *
9014779705SSam Leffler */
91c48e24c1SAdrian Chadd #include "ah_regdomain/ah_rd_freqbands.h"
9214779705SSam Leffler
9314779705SSam Leffler /*
94c48e24c1SAdrian Chadd * This is the main regulatory database. It defines the supported
95c48e24c1SAdrian Chadd * set of features and requirements for each of the defined regulatory
96c48e24c1SAdrian Chadd * zones. It uses combinations of frequency ranges - represented in
97c48e24c1SAdrian Chadd * a bitmask - to determine the requirements and limitations needed.
9814779705SSam Leffler */
99c48e24c1SAdrian Chadd #include "ah_regdomain/ah_rd_domains.h"
10014779705SSam Leffler
10114779705SSam Leffler static const struct cmode modes[] = {
10212d53238SAndriy Voskoboinyk { HAL_MODE_TURBO, IEEE80211_CHAN_ST, ®Dmn5GhzTurboFreq[0] },
10312d53238SAndriy Voskoboinyk { HAL_MODE_11A, IEEE80211_CHAN_A, ®Dmn5GhzFreq[0] },
10412d53238SAndriy Voskoboinyk { HAL_MODE_11B, IEEE80211_CHAN_B, ®Dmn2GhzFreq[0] },
10512d53238SAndriy Voskoboinyk { HAL_MODE_11G, IEEE80211_CHAN_G, ®Dmn2Ghz11gFreq[0] },
10612d53238SAndriy Voskoboinyk { HAL_MODE_11G_TURBO, IEEE80211_CHAN_108G, ®Dmn2Ghz11gTurboFreq[0] },
10712d53238SAndriy Voskoboinyk { HAL_MODE_11A_TURBO, IEEE80211_CHAN_108A, ®Dmn5GhzTurboFreq[0] },
10859efa8b5SSam Leffler { HAL_MODE_11A_QUARTER_RATE,
10912d53238SAndriy Voskoboinyk IEEE80211_CHAN_A | IEEE80211_CHAN_QUARTER, ®Dmn5GhzFreq[0] },
11059efa8b5SSam Leffler { HAL_MODE_11A_HALF_RATE,
11112d53238SAndriy Voskoboinyk IEEE80211_CHAN_A | IEEE80211_CHAN_HALF, ®Dmn5GhzFreq[0] },
11259efa8b5SSam Leffler { HAL_MODE_11G_QUARTER_RATE,
11312d53238SAndriy Voskoboinyk IEEE80211_CHAN_G | IEEE80211_CHAN_QUARTER, ®Dmn2Ghz11gFreq[0] },
11459efa8b5SSam Leffler { HAL_MODE_11G_HALF_RATE,
11512d53238SAndriy Voskoboinyk IEEE80211_CHAN_G | IEEE80211_CHAN_HALF, ®Dmn2Ghz11gFreq[0] },
11612d53238SAndriy Voskoboinyk { HAL_MODE_11NG_HT20,
11712d53238SAndriy Voskoboinyk IEEE80211_CHAN_G | IEEE80211_CHAN_HT20, ®Dmn2Ghz11gFreq[0] },
11859efa8b5SSam Leffler { HAL_MODE_11NG_HT40PLUS,
11912d53238SAndriy Voskoboinyk IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U, ®Dmn2Ghz11gFreq[0] },
12059efa8b5SSam Leffler { HAL_MODE_11NG_HT40MINUS,
12112d53238SAndriy Voskoboinyk IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D, ®Dmn2Ghz11gFreq[0] },
12212d53238SAndriy Voskoboinyk { HAL_MODE_11NA_HT20,
12312d53238SAndriy Voskoboinyk IEEE80211_CHAN_A | IEEE80211_CHAN_HT20, ®Dmn5GhzFreq[0] },
12459efa8b5SSam Leffler { HAL_MODE_11NA_HT40PLUS,
12512d53238SAndriy Voskoboinyk IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U, ®Dmn5GhzFreq[0] },
12659efa8b5SSam Leffler { HAL_MODE_11NA_HT40MINUS,
12712d53238SAndriy Voskoboinyk IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D, ®Dmn5GhzFreq[0] },
12814779705SSam Leffler };
12914779705SSam Leffler
1307dd4de13SAdrian Chadd static void ath_hal_update_dfsdomain(struct ath_hal *ah);
1317dd4de13SAdrian Chadd
13259efa8b5SSam Leffler static OS_INLINE uint16_t
getEepromRD(struct ath_hal * ah)13314779705SSam Leffler getEepromRD(struct ath_hal *ah)
13414779705SSam Leffler {
13514779705SSam Leffler return AH_PRIVATE(ah)->ah_currentRD &~ WORLDWIDE_ROAMING_FLAG;
13614779705SSam Leffler }
13714779705SSam Leffler
13814779705SSam Leffler /*
13914779705SSam Leffler * Test to see if the bitmask array is all zeros
14014779705SSam Leffler */
14114779705SSam Leffler static HAL_BOOL
isChanBitMaskZero(const uint64_t * bitmask)14214779705SSam Leffler isChanBitMaskZero(const uint64_t *bitmask)
14314779705SSam Leffler {
14414779705SSam Leffler #if BMLEN > 2
14514779705SSam Leffler #error "add more cases"
14614779705SSam Leffler #endif
14714779705SSam Leffler #if BMLEN > 1
14814779705SSam Leffler if (bitmask[1] != 0)
14914779705SSam Leffler return AH_FALSE;
15014779705SSam Leffler #endif
15114779705SSam Leffler return (bitmask[0] == 0);
15214779705SSam Leffler }
15314779705SSam Leffler
15414779705SSam Leffler /*
15514779705SSam Leffler * Return whether or not the regulatory domain/country in EEPROM
15614779705SSam Leffler * is acceptable.
15714779705SSam Leffler */
15814779705SSam Leffler static HAL_BOOL
isEepromValid(struct ath_hal * ah)15914779705SSam Leffler isEepromValid(struct ath_hal *ah)
16014779705SSam Leffler {
16114779705SSam Leffler uint16_t rd = getEepromRD(ah);
16214779705SSam Leffler int i;
16314779705SSam Leffler
16414779705SSam Leffler if (rd & COUNTRY_ERD_FLAG) {
16514779705SSam Leffler uint16_t cc = rd &~ COUNTRY_ERD_FLAG;
16614779705SSam Leffler for (i = 0; i < N(allCountries); i++)
16714779705SSam Leffler if (allCountries[i].countryCode == cc)
16814779705SSam Leffler return AH_TRUE;
16914779705SSam Leffler } else {
17014779705SSam Leffler for (i = 0; i < N(regDomainPairs); i++)
17114779705SSam Leffler if (regDomainPairs[i].regDmnEnum == rd)
17214779705SSam Leffler return AH_TRUE;
17314779705SSam Leffler }
17492389b27SHiren Panchasara
17592389b27SHiren Panchasara if (rd == FCC_UBNT) {
17692389b27SHiren Panchasara return AH_TRUE;
17792389b27SHiren Panchasara }
17892389b27SHiren Panchasara
1790e56140aSAdrian Chadd HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
18014779705SSam Leffler "%s: invalid regulatory domain/country code 0x%x\n", __func__, rd);
18114779705SSam Leffler return AH_FALSE;
18214779705SSam Leffler }
18314779705SSam Leffler
18414779705SSam Leffler /*
18514779705SSam Leffler * Find the pointer to the country element in the country table
18614779705SSam Leffler * corresponding to the country code
18714779705SSam Leffler */
18814779705SSam Leffler static COUNTRY_CODE_TO_ENUM_RD*
findCountry(HAL_CTRY_CODE countryCode)18914779705SSam Leffler findCountry(HAL_CTRY_CODE countryCode)
19014779705SSam Leffler {
19114779705SSam Leffler int i;
19214779705SSam Leffler
19314779705SSam Leffler for (i = 0; i < N(allCountries); i++) {
19414779705SSam Leffler if (allCountries[i].countryCode == countryCode)
19514779705SSam Leffler return &allCountries[i];
19614779705SSam Leffler }
19759efa8b5SSam Leffler return AH_NULL;
19859efa8b5SSam Leffler }
19959efa8b5SSam Leffler
20059efa8b5SSam Leffler static REG_DOMAIN *
findRegDmn(int regDmn)20159efa8b5SSam Leffler findRegDmn(int regDmn)
20259efa8b5SSam Leffler {
20359efa8b5SSam Leffler int i;
20459efa8b5SSam Leffler
20559efa8b5SSam Leffler for (i = 0; i < N(regDomains); i++) {
20659efa8b5SSam Leffler if (regDomains[i].regDmnEnum == regDmn)
20759efa8b5SSam Leffler return ®Domains[i];
20859efa8b5SSam Leffler }
20959efa8b5SSam Leffler return AH_NULL;
21059efa8b5SSam Leffler }
21159efa8b5SSam Leffler
21259efa8b5SSam Leffler static REG_DMN_PAIR_MAPPING *
findRegDmnPair(int regDmnPair)21359efa8b5SSam Leffler findRegDmnPair(int regDmnPair)
21459efa8b5SSam Leffler {
21559efa8b5SSam Leffler int i;
21659efa8b5SSam Leffler
21759efa8b5SSam Leffler if (regDmnPair != NO_ENUMRD) {
21859efa8b5SSam Leffler for (i = 0; i < N(regDomainPairs); i++) {
21959efa8b5SSam Leffler if (regDomainPairs[i].regDmnEnum == regDmnPair)
22059efa8b5SSam Leffler return ®DomainPairs[i];
22159efa8b5SSam Leffler }
22259efa8b5SSam Leffler }
22359efa8b5SSam Leffler return AH_NULL;
22414779705SSam Leffler }
22514779705SSam Leffler
22614779705SSam Leffler /*
22714779705SSam Leffler * Calculate a default country based on the EEPROM setting.
22814779705SSam Leffler */
22914779705SSam Leffler static HAL_CTRY_CODE
getDefaultCountry(struct ath_hal * ah)23014779705SSam Leffler getDefaultCountry(struct ath_hal *ah)
23114779705SSam Leffler {
23259efa8b5SSam Leffler REG_DMN_PAIR_MAPPING *regpair;
23314779705SSam Leffler uint16_t rd;
23414779705SSam Leffler
23514779705SSam Leffler rd = getEepromRD(ah);
23614779705SSam Leffler if (rd & COUNTRY_ERD_FLAG) {
23759efa8b5SSam Leffler COUNTRY_CODE_TO_ENUM_RD *country;
23814779705SSam Leffler uint16_t cc = rd & ~COUNTRY_ERD_FLAG;
23914779705SSam Leffler country = findCountry(cc);
24014779705SSam Leffler if (country != AH_NULL)
24114779705SSam Leffler return cc;
24214779705SSam Leffler }
24314779705SSam Leffler /*
24414779705SSam Leffler * Check reg domains that have only one country
24514779705SSam Leffler */
24659efa8b5SSam Leffler regpair = findRegDmnPair(rd);
24759efa8b5SSam Leffler return (regpair != AH_NULL) ? regpair->singleCC : CTRY_DEFAULT;
24814779705SSam Leffler }
24914779705SSam Leffler
25014779705SSam Leffler static HAL_BOOL
IS_BIT_SET(int bit,const uint64_t bitmask[])25114779705SSam Leffler IS_BIT_SET(int bit, const uint64_t bitmask[])
25214779705SSam Leffler {
25314779705SSam Leffler int byteOffset, bitnum;
25414779705SSam Leffler uint64_t val;
25514779705SSam Leffler
25614779705SSam Leffler byteOffset = bit/64;
25714779705SSam Leffler bitnum = bit - byteOffset*64;
25814779705SSam Leffler val = ((uint64_t) 1) << bitnum;
25914779705SSam Leffler return (bitmask[byteOffset] & val) != 0;
26014779705SSam Leffler }
26114779705SSam Leffler
26259efa8b5SSam Leffler static HAL_STATUS
getregstate(struct ath_hal * ah,HAL_CTRY_CODE cc,HAL_REG_DOMAIN regDmn,COUNTRY_CODE_TO_ENUM_RD ** pcountry,REG_DOMAIN ** prd2GHz,REG_DOMAIN ** prd5GHz)26359efa8b5SSam Leffler getregstate(struct ath_hal *ah, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
26459efa8b5SSam Leffler COUNTRY_CODE_TO_ENUM_RD **pcountry,
26559efa8b5SSam Leffler REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz)
26614779705SSam Leffler {
26759efa8b5SSam Leffler COUNTRY_CODE_TO_ENUM_RD *country;
26859efa8b5SSam Leffler REG_DOMAIN *rd5GHz, *rd2GHz;
26914779705SSam Leffler
27059efa8b5SSam Leffler if (cc == CTRY_DEFAULT && regDmn == SKU_NONE) {
27114779705SSam Leffler /*
27214779705SSam Leffler * Validate the EEPROM setting and setup defaults
27314779705SSam Leffler */
27414779705SSam Leffler if (!isEepromValid(ah)) {
27514779705SSam Leffler /*
27614779705SSam Leffler * Don't return any channels if the EEPROM has an
27714779705SSam Leffler * invalid regulatory domain/country code setting.
27814779705SSam Leffler */
27914779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
28014779705SSam Leffler "%s: invalid EEPROM contents\n",__func__);
28159efa8b5SSam Leffler return HAL_EEBADREG;
28214779705SSam Leffler }
28314779705SSam Leffler
28459efa8b5SSam Leffler cc = getDefaultCountry(ah);
28559efa8b5SSam Leffler country = findCountry(cc);
28614779705SSam Leffler if (country == AH_NULL) {
28714779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
28859efa8b5SSam Leffler "NULL Country!, cc %d\n", cc);
28959efa8b5SSam Leffler return HAL_EEBADCC;
29014779705SSam Leffler }
29159efa8b5SSam Leffler regDmn = country->regDmnEnum;
29259efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: EEPROM cc %u rd 0x%x\n",
29359efa8b5SSam Leffler __func__, cc, regDmn);
29459efa8b5SSam Leffler
29559efa8b5SSam Leffler if (country->countryCode == CTRY_DEFAULT) {
29659efa8b5SSam Leffler /*
29759efa8b5SSam Leffler * Check EEPROM; SKU may be for a country, single
29859efa8b5SSam Leffler * domain, or multiple domains (WWR).
29959efa8b5SSam Leffler */
30059efa8b5SSam Leffler uint16_t rdnum = getEepromRD(ah);
30159efa8b5SSam Leffler if ((rdnum & COUNTRY_ERD_FLAG) == 0 &&
30259efa8b5SSam Leffler (findRegDmn(rdnum) != AH_NULL ||
30359efa8b5SSam Leffler findRegDmnPair(rdnum) != AH_NULL)) {
30459efa8b5SSam Leffler regDmn = rdnum;
30514779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
30659efa8b5SSam Leffler "%s: EEPROM rd 0x%x\n", __func__, rdnum);
30759efa8b5SSam Leffler }
30859efa8b5SSam Leffler }
30959efa8b5SSam Leffler } else {
31059efa8b5SSam Leffler country = findCountry(cc);
31159efa8b5SSam Leffler if (country == AH_NULL) {
31259efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
31359efa8b5SSam Leffler "unknown country, cc %d\n", cc);
31459efa8b5SSam Leffler return HAL_EINVAL;
31559efa8b5SSam Leffler }
31659efa8b5SSam Leffler if (regDmn == SKU_NONE)
31759efa8b5SSam Leffler regDmn = country->regDmnEnum;
31859efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u rd 0x%x\n",
31959efa8b5SSam Leffler __func__, cc, regDmn);
32014779705SSam Leffler }
32114779705SSam Leffler
32259efa8b5SSam Leffler /*
32359efa8b5SSam Leffler * Setup per-band state.
32459efa8b5SSam Leffler */
32559efa8b5SSam Leffler if ((regDmn & MULTI_DOMAIN_MASK) == 0) {
32659efa8b5SSam Leffler REG_DMN_PAIR_MAPPING *regpair = findRegDmnPair(regDmn);
32759efa8b5SSam Leffler if (regpair == AH_NULL) {
32859efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
32959efa8b5SSam Leffler "%s: no reg domain pair %u for country %u\n",
33059efa8b5SSam Leffler __func__, regDmn, country->countryCode);
33159efa8b5SSam Leffler return HAL_EINVAL;
33259efa8b5SSam Leffler }
33359efa8b5SSam Leffler rd5GHz = findRegDmn(regpair->regDmn5GHz);
33459efa8b5SSam Leffler if (rd5GHz == AH_NULL) {
33559efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
33659efa8b5SSam Leffler "%s: no 5GHz reg domain %u for country %u\n",
33759efa8b5SSam Leffler __func__, regpair->regDmn5GHz, country->countryCode);
33859efa8b5SSam Leffler return HAL_EINVAL;
33959efa8b5SSam Leffler }
34059efa8b5SSam Leffler rd2GHz = findRegDmn(regpair->regDmn2GHz);
34159efa8b5SSam Leffler if (rd2GHz == AH_NULL) {
34259efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
34359efa8b5SSam Leffler "%s: no 2GHz reg domain %u for country %u\n",
34459efa8b5SSam Leffler __func__, regpair->regDmn2GHz, country->countryCode);
34559efa8b5SSam Leffler return HAL_EINVAL;
34659efa8b5SSam Leffler }
34759efa8b5SSam Leffler } else {
34859efa8b5SSam Leffler rd5GHz = rd2GHz = findRegDmn(regDmn);
34959efa8b5SSam Leffler if (rd2GHz == AH_NULL) {
35059efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
35159efa8b5SSam Leffler "%s: no unitary reg domain %u for country %u\n",
35259efa8b5SSam Leffler __func__, regDmn, country->countryCode);
35359efa8b5SSam Leffler return HAL_EINVAL;
35459efa8b5SSam Leffler }
35559efa8b5SSam Leffler }
35659efa8b5SSam Leffler if (pcountry != AH_NULL)
35759efa8b5SSam Leffler *pcountry = country;
35859efa8b5SSam Leffler *prd2GHz = rd2GHz;
35959efa8b5SSam Leffler *prd5GHz = rd5GHz;
36059efa8b5SSam Leffler return HAL_OK;
36159efa8b5SSam Leffler }
36214779705SSam Leffler
36312d53238SAndriy Voskoboinyk static uint64_t *
getchannelBM(u_int mode,REG_DOMAIN * rd)36412d53238SAndriy Voskoboinyk getchannelBM(u_int mode, REG_DOMAIN *rd)
36512d53238SAndriy Voskoboinyk {
36612d53238SAndriy Voskoboinyk switch (mode) {
36712d53238SAndriy Voskoboinyk case HAL_MODE_11B:
36812d53238SAndriy Voskoboinyk return (rd->chan11b);
36912d53238SAndriy Voskoboinyk case HAL_MODE_11G_QUARTER_RATE:
37012d53238SAndriy Voskoboinyk return (rd->chan11g_quarter);
37112d53238SAndriy Voskoboinyk case HAL_MODE_11G_HALF_RATE:
37212d53238SAndriy Voskoboinyk return (rd->chan11g_half);
37312d53238SAndriy Voskoboinyk case HAL_MODE_11G:
37412d53238SAndriy Voskoboinyk case HAL_MODE_11NG_HT20:
37512d53238SAndriy Voskoboinyk case HAL_MODE_11NG_HT40PLUS:
37612d53238SAndriy Voskoboinyk case HAL_MODE_11NG_HT40MINUS:
37712d53238SAndriy Voskoboinyk return (rd->chan11g);
37812d53238SAndriy Voskoboinyk case HAL_MODE_11G_TURBO:
37912d53238SAndriy Voskoboinyk return (rd->chan11g_turbo);
38012d53238SAndriy Voskoboinyk case HAL_MODE_11A_QUARTER_RATE:
38112d53238SAndriy Voskoboinyk return (rd->chan11a_quarter);
38212d53238SAndriy Voskoboinyk case HAL_MODE_11A_HALF_RATE:
38312d53238SAndriy Voskoboinyk return (rd->chan11a_half);
38412d53238SAndriy Voskoboinyk case HAL_MODE_11A:
38512d53238SAndriy Voskoboinyk case HAL_MODE_11NA_HT20:
38612d53238SAndriy Voskoboinyk case HAL_MODE_11NA_HT40PLUS:
38712d53238SAndriy Voskoboinyk case HAL_MODE_11NA_HT40MINUS:
38812d53238SAndriy Voskoboinyk return (rd->chan11a);
38912d53238SAndriy Voskoboinyk case HAL_MODE_TURBO:
39012d53238SAndriy Voskoboinyk return (rd->chan11a_turbo);
39112d53238SAndriy Voskoboinyk case HAL_MODE_11A_TURBO:
39212d53238SAndriy Voskoboinyk return (rd->chan11a_dyn_turbo);
39312d53238SAndriy Voskoboinyk default:
39412d53238SAndriy Voskoboinyk return (AH_NULL);
39512d53238SAndriy Voskoboinyk }
39612d53238SAndriy Voskoboinyk }
39712d53238SAndriy Voskoboinyk
39812d53238SAndriy Voskoboinyk static void
setchannelflags(struct ieee80211_channel * c,REG_DMN_FREQ_BAND * fband,REG_DOMAIN * rd)39912d53238SAndriy Voskoboinyk setchannelflags(struct ieee80211_channel *c, REG_DMN_FREQ_BAND *fband,
40012d53238SAndriy Voskoboinyk REG_DOMAIN *rd)
40112d53238SAndriy Voskoboinyk {
40212d53238SAndriy Voskoboinyk if (fband->usePassScan & rd->pscan)
40312d53238SAndriy Voskoboinyk c->ic_flags |= IEEE80211_CHAN_PASSIVE;
40412d53238SAndriy Voskoboinyk if (fband->useDfs & rd->dfsMask)
40512d53238SAndriy Voskoboinyk c->ic_flags |= IEEE80211_CHAN_DFS;
40612d53238SAndriy Voskoboinyk if (IEEE80211_IS_CHAN_5GHZ(c) && (rd->flags & DISALLOW_ADHOC_11A))
40712d53238SAndriy Voskoboinyk c->ic_flags |= IEEE80211_CHAN_NOADHOC;
40812d53238SAndriy Voskoboinyk if (IEEE80211_IS_CHAN_TURBO(c) &&
40912d53238SAndriy Voskoboinyk (rd->flags & DISALLOW_ADHOC_11A_TURB))
41012d53238SAndriy Voskoboinyk c->ic_flags |= IEEE80211_CHAN_NOADHOC;
41112d53238SAndriy Voskoboinyk if (rd->flags & NO_HOSTAP)
41212d53238SAndriy Voskoboinyk c->ic_flags |= IEEE80211_CHAN_NOHOSTAP;
41312d53238SAndriy Voskoboinyk if (rd->flags & LIMIT_FRAME_4MS)
41412d53238SAndriy Voskoboinyk c->ic_flags |= IEEE80211_CHAN_4MSXMIT;
41512d53238SAndriy Voskoboinyk if (rd->flags & NEED_NFC)
41612d53238SAndriy Voskoboinyk c->ic_flags |= CHANNEL_NFCREQUIRED;
41712d53238SAndriy Voskoboinyk }
41812d53238SAndriy Voskoboinyk
41912d53238SAndriy Voskoboinyk static int
addchan(struct ath_hal * ah,struct ieee80211_channel chans[],u_int maxchans,int * nchans,uint16_t freq,uint32_t flags,REG_DMN_FREQ_BAND * fband,REG_DOMAIN * rd)42012d53238SAndriy Voskoboinyk addchan(struct ath_hal *ah, struct ieee80211_channel chans[],
42112d53238SAndriy Voskoboinyk u_int maxchans, int *nchans, uint16_t freq, uint32_t flags,
42212d53238SAndriy Voskoboinyk REG_DMN_FREQ_BAND *fband, REG_DOMAIN *rd)
42312d53238SAndriy Voskoboinyk {
42412d53238SAndriy Voskoboinyk struct ieee80211_channel *c;
42512d53238SAndriy Voskoboinyk
42612d53238SAndriy Voskoboinyk if (*nchans >= maxchans)
427b1940debSAdrian Chadd return (HAL_ENOMEM);
42812d53238SAndriy Voskoboinyk
429eee8e362SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
430eee8e362SAdrian Chadd "%s: %d: freq=%d, flags=0x%08x\n",
431eee8e362SAdrian Chadd __func__, *nchans, (int) freq, flags);
432eee8e362SAdrian Chadd
43312d53238SAndriy Voskoboinyk c = &chans[(*nchans)++];
43412d53238SAndriy Voskoboinyk c->ic_freq = freq;
43512d53238SAndriy Voskoboinyk c->ic_flags = flags;
43612d53238SAndriy Voskoboinyk setchannelflags(c, fband, rd);
43712d53238SAndriy Voskoboinyk c->ic_maxregpower = fband->powerDfs;
43812d53238SAndriy Voskoboinyk ath_hal_getpowerlimits(ah, c);
43912d53238SAndriy Voskoboinyk c->ic_maxantgain = fband->antennaMax;
44012d53238SAndriy Voskoboinyk
44112d53238SAndriy Voskoboinyk return (0);
44212d53238SAndriy Voskoboinyk }
44312d53238SAndriy Voskoboinyk
44412d53238SAndriy Voskoboinyk static int
copychan_prev(struct ath_hal * ah,struct ieee80211_channel chans[],u_int maxchans,int * nchans,uint16_t freq,uint32_t flags)44512d53238SAndriy Voskoboinyk copychan_prev(struct ath_hal *ah, struct ieee80211_channel chans[],
446eee8e362SAdrian Chadd u_int maxchans, int *nchans, uint16_t freq, uint32_t flags)
44712d53238SAndriy Voskoboinyk {
44812d53238SAndriy Voskoboinyk struct ieee80211_channel *c;
44912d53238SAndriy Voskoboinyk
450b1940debSAdrian Chadd if (*nchans == 0)
451b1940debSAdrian Chadd return (HAL_EINVAL);
45212d53238SAndriy Voskoboinyk
45312d53238SAndriy Voskoboinyk if (*nchans >= maxchans)
454b1940debSAdrian Chadd return (HAL_ENOMEM);
45512d53238SAndriy Voskoboinyk
456eee8e362SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
457eee8e362SAdrian Chadd "%s: %d: freq=%d, flags=0x%08x\n",
458eee8e362SAdrian Chadd __func__, *nchans, (int) freq, flags);
459eee8e362SAdrian Chadd
46012d53238SAndriy Voskoboinyk c = &chans[(*nchans)++];
46112d53238SAndriy Voskoboinyk c[0] = c[-1];
46212d53238SAndriy Voskoboinyk c->ic_freq = freq;
46312d53238SAndriy Voskoboinyk /* XXX is it needed here? */
46412d53238SAndriy Voskoboinyk ath_hal_getpowerlimits(ah, c);
46512d53238SAndriy Voskoboinyk
46612d53238SAndriy Voskoboinyk return (0);
46712d53238SAndriy Voskoboinyk }
46812d53238SAndriy Voskoboinyk
46912d53238SAndriy Voskoboinyk static int
add_chanlist_band(struct ath_hal * ah,struct ieee80211_channel chans[],int maxchans,int * nchans,uint16_t freq_lo,uint16_t freq_hi,int step,uint32_t flags,REG_DMN_FREQ_BAND * fband,REG_DOMAIN * rd)47012d53238SAndriy Voskoboinyk add_chanlist_band(struct ath_hal *ah, struct ieee80211_channel chans[],
47112d53238SAndriy Voskoboinyk int maxchans, int *nchans, uint16_t freq_lo, uint16_t freq_hi, int step,
47212d53238SAndriy Voskoboinyk uint32_t flags, REG_DMN_FREQ_BAND *fband, REG_DOMAIN *rd)
47312d53238SAndriy Voskoboinyk {
47412d53238SAndriy Voskoboinyk uint16_t freq = freq_lo;
47512d53238SAndriy Voskoboinyk int error;
47612d53238SAndriy Voskoboinyk
47712d53238SAndriy Voskoboinyk if (freq_hi < freq_lo)
47812d53238SAndriy Voskoboinyk return (0);
47912d53238SAndriy Voskoboinyk
480eee8e362SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
481eee8e362SAdrian Chadd "%s: freq=%d..%d, flags=0x%08x, step=%d\n", __func__,
482eee8e362SAdrian Chadd (int) freq_lo, (int) freq_hi, flags, step);
483eee8e362SAdrian Chadd
48412d53238SAndriy Voskoboinyk error = addchan(ah, chans, maxchans, nchans, freq, flags, fband, rd);
48512d53238SAndriy Voskoboinyk for (freq += step; freq <= freq_hi && error == 0; freq += step)
486eee8e362SAdrian Chadd error = copychan_prev(ah, chans, maxchans, nchans, freq, flags);
48712d53238SAndriy Voskoboinyk
48812d53238SAndriy Voskoboinyk return (error);
48912d53238SAndriy Voskoboinyk }
49012d53238SAndriy Voskoboinyk
49112d53238SAndriy Voskoboinyk static void
adj_freq_ht40(u_int mode,int * low_adj,int * hi_adj,int * channelSep)49212d53238SAndriy Voskoboinyk adj_freq_ht40(u_int mode, int *low_adj, int *hi_adj, int *channelSep)
49312d53238SAndriy Voskoboinyk {
49412d53238SAndriy Voskoboinyk
49512d53238SAndriy Voskoboinyk *low_adj = *hi_adj = *channelSep = 0;
49612d53238SAndriy Voskoboinyk switch (mode) {
49712d53238SAndriy Voskoboinyk case HAL_MODE_11NA_HT40PLUS:
49812d53238SAndriy Voskoboinyk *channelSep = 40;
49912d53238SAndriy Voskoboinyk /* FALLTHROUGH */
50012d53238SAndriy Voskoboinyk case HAL_MODE_11NG_HT40PLUS:
50112d53238SAndriy Voskoboinyk *hi_adj = -20;
50212d53238SAndriy Voskoboinyk break;
50312d53238SAndriy Voskoboinyk case HAL_MODE_11NA_HT40MINUS:
50412d53238SAndriy Voskoboinyk *channelSep = 40;
50512d53238SAndriy Voskoboinyk /* FALLTHROUGH */
50612d53238SAndriy Voskoboinyk case HAL_MODE_11NG_HT40MINUS:
50712d53238SAndriy Voskoboinyk *low_adj = 20;
50812d53238SAndriy Voskoboinyk break;
50912d53238SAndriy Voskoboinyk }
51012d53238SAndriy Voskoboinyk }
51112d53238SAndriy Voskoboinyk
51212d53238SAndriy Voskoboinyk static void
add_chanlist_mode(struct ath_hal * ah,struct ieee80211_channel chans[],u_int maxchans,int * nchans,const struct cmode * cm,REG_DOMAIN * rd,HAL_BOOL enableExtendedChannels)51312d53238SAndriy Voskoboinyk add_chanlist_mode(struct ath_hal *ah, struct ieee80211_channel chans[],
51412d53238SAndriy Voskoboinyk u_int maxchans, int *nchans, const struct cmode *cm, REG_DOMAIN *rd,
51512d53238SAndriy Voskoboinyk HAL_BOOL enableExtendedChannels)
51612d53238SAndriy Voskoboinyk {
51712d53238SAndriy Voskoboinyk uint64_t *channelBM;
51812d53238SAndriy Voskoboinyk uint16_t freq_lo, freq_hi;
51912d53238SAndriy Voskoboinyk int b, error, low_adj, hi_adj, channelSep;
52012d53238SAndriy Voskoboinyk
52112d53238SAndriy Voskoboinyk if (!ath_hal_getChannelEdges(ah, cm->flags, &freq_lo, &freq_hi)) {
52212d53238SAndriy Voskoboinyk /* channel not supported by hardware, skip it */
52312d53238SAndriy Voskoboinyk HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
52412d53238SAndriy Voskoboinyk "%s: channels 0x%x not supported by hardware\n",
52512d53238SAndriy Voskoboinyk __func__, cm->flags);
52612d53238SAndriy Voskoboinyk return;
52712d53238SAndriy Voskoboinyk }
52812d53238SAndriy Voskoboinyk
52912d53238SAndriy Voskoboinyk channelBM = getchannelBM(cm->mode, rd);
53012d53238SAndriy Voskoboinyk if (isChanBitMaskZero(channelBM))
53112d53238SAndriy Voskoboinyk return;
53212d53238SAndriy Voskoboinyk
53312d53238SAndriy Voskoboinyk /*
53412d53238SAndriy Voskoboinyk * Setup special handling for HT40 channels; e.g.
53512d53238SAndriy Voskoboinyk * 5G HT40 channels require 40Mhz channel separation.
53612d53238SAndriy Voskoboinyk */
53712d53238SAndriy Voskoboinyk adj_freq_ht40(cm->mode, &low_adj, &hi_adj, &channelSep);
53812d53238SAndriy Voskoboinyk
53912d53238SAndriy Voskoboinyk for (b = 0; b < 64*BMLEN; b++) {
54012d53238SAndriy Voskoboinyk REG_DMN_FREQ_BAND *fband;
54112d53238SAndriy Voskoboinyk uint16_t bfreq_lo, bfreq_hi;
54212d53238SAndriy Voskoboinyk int step;
54312d53238SAndriy Voskoboinyk
54412d53238SAndriy Voskoboinyk if (!IS_BIT_SET(b, channelBM))
54512d53238SAndriy Voskoboinyk continue;
54612d53238SAndriy Voskoboinyk fband = &cm->freqs[b];
54712d53238SAndriy Voskoboinyk
54812d53238SAndriy Voskoboinyk if ((fband->usePassScan & IS_ECM_CHAN) &&
54912d53238SAndriy Voskoboinyk !enableExtendedChannels) {
55012d53238SAndriy Voskoboinyk HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
55112d53238SAndriy Voskoboinyk "skip ecm channels\n");
55212d53238SAndriy Voskoboinyk continue;
55312d53238SAndriy Voskoboinyk }
55412d53238SAndriy Voskoboinyk #if 0
55512d53238SAndriy Voskoboinyk if ((fband->useDfs & rd->dfsMask) &&
55612d53238SAndriy Voskoboinyk (cm->flags & IEEE80211_CHAN_HT40)) {
55712d53238SAndriy Voskoboinyk /* NB: DFS and HT40 don't mix */
55812d53238SAndriy Voskoboinyk HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
55912d53238SAndriy Voskoboinyk "skip HT40 chan, DFS required\n");
56012d53238SAndriy Voskoboinyk continue;
56112d53238SAndriy Voskoboinyk }
56212d53238SAndriy Voskoboinyk #endif
5635b06614cSAdrian Chadd /*
5645b06614cSAdrian Chadd * XXX TODO: handle REG_EXT_FCC_CH_144.
5655b06614cSAdrian Chadd *
5665b06614cSAdrian Chadd * Figure out which instances/uses cause us to not
5675b06614cSAdrian Chadd * be allowed to use channel 144 (pri or sec overlap.)
5685b06614cSAdrian Chadd */
5695b06614cSAdrian Chadd
57012d53238SAndriy Voskoboinyk bfreq_lo = MAX(fband->lowChannel + low_adj, freq_lo);
57112d53238SAndriy Voskoboinyk bfreq_hi = MIN(fband->highChannel + hi_adj, freq_hi);
572eee8e362SAdrian Chadd
573eee8e362SAdrian Chadd /*
574eee8e362SAdrian Chadd * Don't start the 5GHz channel list at 5120MHz.
575eee8e362SAdrian Chadd *
576eee8e362SAdrian Chadd * Unfortunately (sigh) the HT40 channel creation
577eee8e362SAdrian Chadd * logic will create HT40U channels at 5120, 5160, 5200.
578eee8e362SAdrian Chadd * This means that 36 (5180) isn't considered as a
579eee8e362SAdrian Chadd * HT40 channel, and everything goes messed up from there.
580eee8e362SAdrian Chadd */
581eee8e362SAdrian Chadd if ((cm->flags & IEEE80211_CHAN_5GHZ) &&
582eee8e362SAdrian Chadd (cm->flags & IEEE80211_CHAN_HT40U)) {
583eee8e362SAdrian Chadd if (bfreq_lo < 5180)
584eee8e362SAdrian Chadd bfreq_lo = 5180;
585eee8e362SAdrian Chadd }
586eee8e362SAdrian Chadd
587eee8e362SAdrian Chadd /*
588eee8e362SAdrian Chadd * Same with HT40D - need to start at 5200 or the low
589eee8e362SAdrian Chadd * channels are all wrong again.
590eee8e362SAdrian Chadd */
591eee8e362SAdrian Chadd if ((cm->flags & IEEE80211_CHAN_5GHZ) &&
592eee8e362SAdrian Chadd (cm->flags & IEEE80211_CHAN_HT40D)) {
593eee8e362SAdrian Chadd if (bfreq_lo < 5200)
594eee8e362SAdrian Chadd bfreq_lo = 5200;
595eee8e362SAdrian Chadd }
596eee8e362SAdrian Chadd
59712d53238SAndriy Voskoboinyk if (fband->channelSep >= channelSep)
59812d53238SAndriy Voskoboinyk step = fband->channelSep;
59912d53238SAndriy Voskoboinyk else
60012d53238SAndriy Voskoboinyk step = roundup(channelSep, fband->channelSep);
60112d53238SAndriy Voskoboinyk
602eee8e362SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
603eee8e362SAdrian Chadd "%s: freq_lo=%d, freq_hi=%d, low_adj=%d, hi_adj=%d, "
604eee8e362SAdrian Chadd "bandlo=%d, bandhi=%d, bfreqlo=%d, bfreqhi=%d, step=%d, "
605eee8e362SAdrian Chadd "flags=0x%08x\n",
606eee8e362SAdrian Chadd __func__,
607eee8e362SAdrian Chadd (int) freq_lo,
608eee8e362SAdrian Chadd (int) freq_hi,
609eee8e362SAdrian Chadd (int) low_adj,
610eee8e362SAdrian Chadd (int) hi_adj,
611eee8e362SAdrian Chadd (int) fband->lowChannel,
612eee8e362SAdrian Chadd (int) fband->highChannel,
613eee8e362SAdrian Chadd (int) bfreq_lo,
614eee8e362SAdrian Chadd (int) bfreq_hi,
615eee8e362SAdrian Chadd step,
616eee8e362SAdrian Chadd (int) cm->flags);
617eee8e362SAdrian Chadd
61812d53238SAndriy Voskoboinyk error = add_chanlist_band(ah, chans, maxchans, nchans,
61912d53238SAndriy Voskoboinyk bfreq_lo, bfreq_hi, step, cm->flags, fband, rd);
62012d53238SAndriy Voskoboinyk if (error != 0) {
62112d53238SAndriy Voskoboinyk HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
62212d53238SAndriy Voskoboinyk "%s: too many channels for channel table\n",
62312d53238SAndriy Voskoboinyk __func__);
62412d53238SAndriy Voskoboinyk return;
62512d53238SAndriy Voskoboinyk }
62612d53238SAndriy Voskoboinyk }
62712d53238SAndriy Voskoboinyk }
62812d53238SAndriy Voskoboinyk
62912d53238SAndriy Voskoboinyk static u_int
getmodesmask(struct ath_hal * ah,REG_DOMAIN * rd5GHz,u_int modeSelect)63012d53238SAndriy Voskoboinyk getmodesmask(struct ath_hal *ah, REG_DOMAIN *rd5GHz, u_int modeSelect)
63112d53238SAndriy Voskoboinyk {
63212d53238SAndriy Voskoboinyk #define HAL_MODE_11A_ALL \
63312d53238SAndriy Voskoboinyk (HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \
63412d53238SAndriy Voskoboinyk HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE)
63512d53238SAndriy Voskoboinyk u_int modesMask;
63612d53238SAndriy Voskoboinyk
63712d53238SAndriy Voskoboinyk /* get modes that HW is capable of */
63812d53238SAndriy Voskoboinyk modesMask = ath_hal_getWirelessModes(ah);
63912d53238SAndriy Voskoboinyk modesMask &= modeSelect;
64012d53238SAndriy Voskoboinyk /* optimize work below if no 11a channels */
64112d53238SAndriy Voskoboinyk if (isChanBitMaskZero(rd5GHz->chan11a) &&
64212d53238SAndriy Voskoboinyk (modesMask & HAL_MODE_11A_ALL)) {
64312d53238SAndriy Voskoboinyk HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
64412d53238SAndriy Voskoboinyk "%s: disallow all 11a\n", __func__);
64512d53238SAndriy Voskoboinyk modesMask &= ~HAL_MODE_11A_ALL;
64612d53238SAndriy Voskoboinyk }
64712d53238SAndriy Voskoboinyk
64812d53238SAndriy Voskoboinyk return (modesMask);
64912d53238SAndriy Voskoboinyk #undef HAL_MODE_11A_ALL
65012d53238SAndriy Voskoboinyk }
65112d53238SAndriy Voskoboinyk
65259efa8b5SSam Leffler /*
65359efa8b5SSam Leffler * Construct the channel list for the specified regulatory config.
65459efa8b5SSam Leffler */
65559efa8b5SSam Leffler static HAL_STATUS
getchannels(struct ath_hal * ah,struct ieee80211_channel chans[],u_int maxchans,int * nchans,u_int modeSelect,HAL_CTRY_CODE cc,HAL_REG_DOMAIN regDmn,HAL_BOOL enableExtendedChannels,COUNTRY_CODE_TO_ENUM_RD ** pcountry,REG_DOMAIN ** prd2GHz,REG_DOMAIN ** prd5GHz)65659efa8b5SSam Leffler getchannels(struct ath_hal *ah,
65759efa8b5SSam Leffler struct ieee80211_channel chans[], u_int maxchans, int *nchans,
65859efa8b5SSam Leffler u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
65959efa8b5SSam Leffler HAL_BOOL enableExtendedChannels,
66059efa8b5SSam Leffler COUNTRY_CODE_TO_ENUM_RD **pcountry,
66159efa8b5SSam Leffler REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz)
66259efa8b5SSam Leffler {
66359efa8b5SSam Leffler REG_DOMAIN *rd5GHz, *rd2GHz;
66412d53238SAndriy Voskoboinyk u_int modesMask;
66559efa8b5SSam Leffler const struct cmode *cm;
66659efa8b5SSam Leffler HAL_STATUS status;
66759efa8b5SSam Leffler
66859efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u regDmn 0x%x mode 0x%x%s\n",
66959efa8b5SSam Leffler __func__, cc, regDmn, modeSelect,
67059efa8b5SSam Leffler enableExtendedChannels ? " ecm" : "");
67159efa8b5SSam Leffler
67259efa8b5SSam Leffler status = getregstate(ah, cc, regDmn, pcountry, &rd2GHz, &rd5GHz);
67359efa8b5SSam Leffler if (status != HAL_OK)
67459efa8b5SSam Leffler return status;
67559efa8b5SSam Leffler
67612d53238SAndriy Voskoboinyk modesMask = getmodesmask(ah, rd5GHz, modeSelect);
67712d53238SAndriy Voskoboinyk /* XXX error? */
67812d53238SAndriy Voskoboinyk if (modesMask == 0)
67912d53238SAndriy Voskoboinyk goto done;
68059efa8b5SSam Leffler
68114779705SSam Leffler for (cm = modes; cm < &modes[N(modes)]; cm++) {
68212d53238SAndriy Voskoboinyk REG_DOMAIN *rd;
68314779705SSam Leffler
68412d53238SAndriy Voskoboinyk if ((cm->mode & modesMask) == 0) {
68514779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
68614779705SSam Leffler "%s: skip mode 0x%x flags 0x%x\n",
68714779705SSam Leffler __func__, cm->mode, cm->flags);
68814779705SSam Leffler continue;
68914779705SSam Leffler }
69014779705SSam Leffler
69112d53238SAndriy Voskoboinyk if (cm->flags & IEEE80211_CHAN_5GHZ)
69212d53238SAndriy Voskoboinyk rd = rd5GHz;
69312d53238SAndriy Voskoboinyk else if (cm->flags & IEEE80211_CHAN_2GHZ)
69412d53238SAndriy Voskoboinyk rd = rd2GHz;
69512d53238SAndriy Voskoboinyk else {
696*7e03a82bSRebecca Cran ath_hal_printf(ah, "%s: Unknown HAL flags 0x%x\n",
697b1940debSAdrian Chadd __func__, cm->flags);
69812d53238SAndriy Voskoboinyk return HAL_EINVAL;
69914779705SSam Leffler }
70012d53238SAndriy Voskoboinyk
70112d53238SAndriy Voskoboinyk add_chanlist_mode(ah, chans, maxchans, nchans, cm,
70212d53238SAndriy Voskoboinyk rd, enableExtendedChannels);
70312d53238SAndriy Voskoboinyk if (*nchans >= maxchans)
70414779705SSam Leffler goto done;
70514779705SSam Leffler }
70614779705SSam Leffler done:
70759efa8b5SSam Leffler /* NB: pcountry set above by getregstate */
70859efa8b5SSam Leffler if (prd2GHz != AH_NULL)
70959efa8b5SSam Leffler *prd2GHz = rd2GHz;
71059efa8b5SSam Leffler if (prd5GHz != AH_NULL)
71159efa8b5SSam Leffler *prd5GHz = rd5GHz;
71259efa8b5SSam Leffler return HAL_OK;
71314779705SSam Leffler }
71414779705SSam Leffler
71514779705SSam Leffler /*
71659efa8b5SSam Leffler * Retrieve a channel list without affecting runtime state.
71714779705SSam Leffler */
71859efa8b5SSam Leffler HAL_STATUS
ath_hal_getchannels(struct ath_hal * ah,struct ieee80211_channel chans[],u_int maxchans,int * nchans,u_int modeSelect,HAL_CTRY_CODE cc,HAL_REG_DOMAIN regDmn,HAL_BOOL enableExtendedChannels)71959efa8b5SSam Leffler ath_hal_getchannels(struct ath_hal *ah,
72059efa8b5SSam Leffler struct ieee80211_channel chans[], u_int maxchans, int *nchans,
72159efa8b5SSam Leffler u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
72259efa8b5SSam Leffler HAL_BOOL enableExtendedChannels)
72314779705SSam Leffler {
72459efa8b5SSam Leffler return getchannels(ah, chans, maxchans, nchans, modeSelect,
72559efa8b5SSam Leffler cc, regDmn, enableExtendedChannels, AH_NULL, AH_NULL, AH_NULL);
72659efa8b5SSam Leffler }
72714779705SSam Leffler
72814779705SSam Leffler /*
72959efa8b5SSam Leffler * Handle frequency mapping from 900Mhz range to 2.4GHz range
73059efa8b5SSam Leffler * for GSM radios. This is done when we need the h/w frequency
73159efa8b5SSam Leffler * and the channel is marked IEEE80211_CHAN_GSM.
73214779705SSam Leffler */
73359efa8b5SSam Leffler static int
ath_hal_mapgsm(int sku,int freq)73459efa8b5SSam Leffler ath_hal_mapgsm(int sku, int freq)
73559efa8b5SSam Leffler {
73659efa8b5SSam Leffler if (sku == SKU_XR9)
73759efa8b5SSam Leffler return 1520 + freq;
73859efa8b5SSam Leffler if (sku == SKU_GZ901)
73959efa8b5SSam Leffler return 1544 + freq;
74059efa8b5SSam Leffler if (sku == SKU_SR9)
74159efa8b5SSam Leffler return 3344 - freq;
7422992cd22SAdrian Chadd if (sku == SKU_XC900M)
7432992cd22SAdrian Chadd return 1517 + freq;
7440e56140aSAdrian Chadd HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
74559efa8b5SSam Leffler "%s: cannot map freq %u unknown gsm sku %u\n",
74659efa8b5SSam Leffler __func__, freq, sku);
74759efa8b5SSam Leffler return freq;
74814779705SSam Leffler }
74914779705SSam Leffler
75059efa8b5SSam Leffler /*
75159efa8b5SSam Leffler * Setup the internal/private channel state given a table of
75259efa8b5SSam Leffler * net80211 channels. We collapse entries for the same frequency
75359efa8b5SSam Leffler * and record the frequency for doing noise floor processing
75459efa8b5SSam Leffler * where we don't have net80211 channel context.
75559efa8b5SSam Leffler */
75659efa8b5SSam Leffler static HAL_BOOL
assignPrivateChannels(struct ath_hal * ah,struct ieee80211_channel chans[],int nchans,int sku)75759efa8b5SSam Leffler assignPrivateChannels(struct ath_hal *ah,
75859efa8b5SSam Leffler struct ieee80211_channel chans[], int nchans, int sku)
75959efa8b5SSam Leffler {
76059efa8b5SSam Leffler HAL_CHANNEL_INTERNAL *ic;
76159efa8b5SSam Leffler int i, j, next, freq;
76259efa8b5SSam Leffler
76359efa8b5SSam Leffler next = 0;
76459efa8b5SSam Leffler for (i = 0; i < nchans; i++) {
76559efa8b5SSam Leffler struct ieee80211_channel *c = &chans[i];
76659efa8b5SSam Leffler for (j = i-1; j >= 0; j--)
76759efa8b5SSam Leffler if (chans[j].ic_freq == c->ic_freq) {
76859efa8b5SSam Leffler c->ic_devdata = chans[j].ic_devdata;
76959efa8b5SSam Leffler break;
77059efa8b5SSam Leffler }
77159efa8b5SSam Leffler if (j < 0) {
77259efa8b5SSam Leffler /* new entry, assign a private channel entry */
77359efa8b5SSam Leffler if (next >= N(AH_PRIVATE(ah)->ah_channels)) {
77459efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
77550d5ad0eSSam Leffler "%s: too many channels, max %zu\n",
77659efa8b5SSam Leffler __func__, N(AH_PRIVATE(ah)->ah_channels));
77759efa8b5SSam Leffler return AH_FALSE;
77859efa8b5SSam Leffler }
77959efa8b5SSam Leffler /*
78059efa8b5SSam Leffler * Handle frequency mapping for 900MHz devices.
78159efa8b5SSam Leffler * The hardware uses 2.4GHz frequencies that are
78259efa8b5SSam Leffler * down-converted. The 802.11 layer uses the
78359efa8b5SSam Leffler * true frequencies.
78459efa8b5SSam Leffler */
78559efa8b5SSam Leffler freq = IEEE80211_IS_CHAN_GSM(c) ?
78659efa8b5SSam Leffler ath_hal_mapgsm(sku, c->ic_freq) : c->ic_freq;
78759efa8b5SSam Leffler
78859efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
78959efa8b5SSam Leffler "%s: private[%3u] %u/0x%x -> channel %u\n",
79059efa8b5SSam Leffler __func__, next, c->ic_freq, c->ic_flags, freq);
79159efa8b5SSam Leffler
79259efa8b5SSam Leffler ic = &AH_PRIVATE(ah)->ah_channels[next];
79359efa8b5SSam Leffler /*
79459efa8b5SSam Leffler * NB: This clears privFlags which means ancillary
79559efa8b5SSam Leffler * code like ANI and IQ calibration will be
79659efa8b5SSam Leffler * restarted and re-setup any per-channel state.
79759efa8b5SSam Leffler */
79859efa8b5SSam Leffler OS_MEMZERO(ic, sizeof(*ic));
79959efa8b5SSam Leffler ic->channel = freq;
80059efa8b5SSam Leffler c->ic_devdata = next;
80159efa8b5SSam Leffler next++;
80259efa8b5SSam Leffler }
80359efa8b5SSam Leffler }
80459efa8b5SSam Leffler AH_PRIVATE(ah)->ah_nchan = next;
80559efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: %u public, %u private channels\n",
80659efa8b5SSam Leffler __func__, nchans, next);
80759efa8b5SSam Leffler return AH_TRUE;
80859efa8b5SSam Leffler }
80959efa8b5SSam Leffler
81059efa8b5SSam Leffler /*
81159efa8b5SSam Leffler * Setup the channel list based on the information in the EEPROM.
81259efa8b5SSam Leffler */
81359efa8b5SSam Leffler HAL_STATUS
ath_hal_init_channels(struct ath_hal * ah,struct ieee80211_channel chans[],u_int maxchans,int * nchans,u_int modeSelect,HAL_CTRY_CODE cc,HAL_REG_DOMAIN regDmn,HAL_BOOL enableExtendedChannels)81459efa8b5SSam Leffler ath_hal_init_channels(struct ath_hal *ah,
81559efa8b5SSam Leffler struct ieee80211_channel chans[], u_int maxchans, int *nchans,
81659efa8b5SSam Leffler u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
81759efa8b5SSam Leffler HAL_BOOL enableExtendedChannels)
81859efa8b5SSam Leffler {
81959efa8b5SSam Leffler COUNTRY_CODE_TO_ENUM_RD *country;
8207dd4de13SAdrian Chadd REG_DOMAIN *rd5GHz, *rd2GHz;
82159efa8b5SSam Leffler HAL_STATUS status;
82259efa8b5SSam Leffler
82359efa8b5SSam Leffler status = getchannels(ah, chans, maxchans, nchans, modeSelect,
82459efa8b5SSam Leffler cc, regDmn, enableExtendedChannels, &country, &rd2GHz, &rd5GHz);
82559efa8b5SSam Leffler if (status == HAL_OK &&
82659efa8b5SSam Leffler assignPrivateChannels(ah, chans, *nchans, AH_PRIVATE(ah)->ah_currentRD)) {
82759efa8b5SSam Leffler AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz;
82859efa8b5SSam Leffler AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz;
82959efa8b5SSam Leffler
83059efa8b5SSam Leffler ah->ah_countryCode = country->countryCode;
83159efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n",
83259efa8b5SSam Leffler __func__, ah->ah_countryCode);
8337dd4de13SAdrian Chadd
8347dd4de13SAdrian Chadd /* Update current DFS domain */
8357dd4de13SAdrian Chadd ath_hal_update_dfsdomain(ah);
83659efa8b5SSam Leffler } else
83759efa8b5SSam Leffler status = HAL_EINVAL;
8388db87e40SAdrian Chadd
83959efa8b5SSam Leffler return status;
84059efa8b5SSam Leffler }
84159efa8b5SSam Leffler
84259efa8b5SSam Leffler /*
84359efa8b5SSam Leffler * Set the channel list.
84459efa8b5SSam Leffler */
84559efa8b5SSam Leffler HAL_STATUS
ath_hal_set_channels(struct ath_hal * ah,struct ieee80211_channel chans[],int nchans,HAL_CTRY_CODE cc,HAL_REG_DOMAIN rd)84659efa8b5SSam Leffler ath_hal_set_channels(struct ath_hal *ah,
84759efa8b5SSam Leffler struct ieee80211_channel chans[], int nchans,
84859efa8b5SSam Leffler HAL_CTRY_CODE cc, HAL_REG_DOMAIN rd)
84959efa8b5SSam Leffler {
85059efa8b5SSam Leffler COUNTRY_CODE_TO_ENUM_RD *country;
85159efa8b5SSam Leffler REG_DOMAIN *rd5GHz, *rd2GHz;
85259efa8b5SSam Leffler HAL_STATUS status;
85359efa8b5SSam Leffler
85459efa8b5SSam Leffler switch (rd) {
85559efa8b5SSam Leffler case SKU_SR9:
85659efa8b5SSam Leffler case SKU_XR9:
85759efa8b5SSam Leffler case SKU_GZ901:
8582992cd22SAdrian Chadd case SKU_XC900M:
85959efa8b5SSam Leffler /*
86059efa8b5SSam Leffler * Map 900MHz sku's. The frequencies will be mapped
86159efa8b5SSam Leffler * according to the sku to compensate for the down-converter.
86259efa8b5SSam Leffler * We use the FCC for these sku's as the mapped channel
86359efa8b5SSam Leffler * list is known compatible (will need to change if/when
86459efa8b5SSam Leffler * vendors do different mapping in different locales).
86559efa8b5SSam Leffler */
86659efa8b5SSam Leffler status = getregstate(ah, CTRY_DEFAULT, SKU_FCC,
86759efa8b5SSam Leffler &country, &rd2GHz, &rd5GHz);
86859efa8b5SSam Leffler break;
86959efa8b5SSam Leffler default:
87059efa8b5SSam Leffler status = getregstate(ah, cc, rd,
87159efa8b5SSam Leffler &country, &rd2GHz, &rd5GHz);
87259efa8b5SSam Leffler rd = AH_PRIVATE(ah)->ah_currentRD;
87359efa8b5SSam Leffler break;
87459efa8b5SSam Leffler }
87559efa8b5SSam Leffler if (status == HAL_OK && assignPrivateChannels(ah, chans, nchans, rd)) {
87659efa8b5SSam Leffler AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz;
87759efa8b5SSam Leffler AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz;
87859efa8b5SSam Leffler
87959efa8b5SSam Leffler ah->ah_countryCode = country->countryCode;
88059efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n",
88159efa8b5SSam Leffler __func__, ah->ah_countryCode);
88259efa8b5SSam Leffler } else
88359efa8b5SSam Leffler status = HAL_EINVAL;
8847dd4de13SAdrian Chadd
8857dd4de13SAdrian Chadd if (status == HAL_OK) {
8867dd4de13SAdrian Chadd /* Update current DFS domain */
8877dd4de13SAdrian Chadd (void) ath_hal_update_dfsdomain(ah);
8887dd4de13SAdrian Chadd }
88959efa8b5SSam Leffler return status;
89059efa8b5SSam Leffler }
89159efa8b5SSam Leffler
89259efa8b5SSam Leffler #ifdef AH_DEBUG
89359efa8b5SSam Leffler /*
89459efa8b5SSam Leffler * Return the internal channel corresponding to a public channel.
89559efa8b5SSam Leffler * NB: normally this routine is inline'd (see ah_internal.h)
89659efa8b5SSam Leffler */
89759efa8b5SSam Leffler HAL_CHANNEL_INTERNAL *
ath_hal_checkchannel(struct ath_hal * ah,const struct ieee80211_channel * c)89859efa8b5SSam Leffler ath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c)
89959efa8b5SSam Leffler {
90059efa8b5SSam Leffler HAL_CHANNEL_INTERNAL *cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata];
90159efa8b5SSam Leffler
90259efa8b5SSam Leffler if (c->ic_devdata < AH_PRIVATE(ah)->ah_nchan &&
90359efa8b5SSam Leffler (c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)))
90414779705SSam Leffler return cc;
90559efa8b5SSam Leffler if (c->ic_devdata >= AH_PRIVATE(ah)->ah_nchan) {
90659efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
90759efa8b5SSam Leffler "%s: bad mapping, devdata %u nchans %u\n",
90859efa8b5SSam Leffler __func__, c->ic_devdata, AH_PRIVATE(ah)->ah_nchan);
90959efa8b5SSam Leffler HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan);
91059efa8b5SSam Leffler } else {
91159efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
91259efa8b5SSam Leffler "%s: no match for %u/0x%x devdata %u channel %u\n",
91359efa8b5SSam Leffler __func__, c->ic_freq, c->ic_flags, c->ic_devdata,
91459efa8b5SSam Leffler cc->channel);
91559efa8b5SSam Leffler HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c));
91614779705SSam Leffler }
91714779705SSam Leffler return AH_NULL;
91859efa8b5SSam Leffler }
91959efa8b5SSam Leffler #endif /* AH_DEBUG */
92059efa8b5SSam Leffler
92159efa8b5SSam Leffler #define isWwrSKU(_ah) \
92259efa8b5SSam Leffler ((getEepromRD((_ah)) & WORLD_SKU_MASK) == WORLD_SKU_PREFIX || \
92359efa8b5SSam Leffler getEepromRD(_ah) == WORLD)
92459efa8b5SSam Leffler
92559efa8b5SSam Leffler /*
92659efa8b5SSam Leffler * Return the test group for the specific channel based on
92759efa8b5SSam Leffler * the current regulatory setup.
92859efa8b5SSam Leffler */
92959efa8b5SSam Leffler u_int
ath_hal_getctl(struct ath_hal * ah,const struct ieee80211_channel * c)93059efa8b5SSam Leffler ath_hal_getctl(struct ath_hal *ah, const struct ieee80211_channel *c)
93159efa8b5SSam Leffler {
93259efa8b5SSam Leffler u_int ctl;
93359efa8b5SSam Leffler
93459efa8b5SSam Leffler if (AH_PRIVATE(ah)->ah_rd2GHz == AH_PRIVATE(ah)->ah_rd5GHz ||
93559efa8b5SSam Leffler (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)))
93659efa8b5SSam Leffler ctl = SD_NO_CTL;
93759efa8b5SSam Leffler else if (IEEE80211_IS_CHAN_2GHZ(c))
93859efa8b5SSam Leffler ctl = AH_PRIVATE(ah)->ah_rd2GHz->conformanceTestLimit;
93959efa8b5SSam Leffler else
94059efa8b5SSam Leffler ctl = AH_PRIVATE(ah)->ah_rd5GHz->conformanceTestLimit;
94159efa8b5SSam Leffler if (IEEE80211_IS_CHAN_B(c))
94259efa8b5SSam Leffler return ctl | CTL_11B;
94359efa8b5SSam Leffler if (IEEE80211_IS_CHAN_G(c))
94459efa8b5SSam Leffler return ctl | CTL_11G;
94559efa8b5SSam Leffler if (IEEE80211_IS_CHAN_108G(c))
94659efa8b5SSam Leffler return ctl | CTL_108G;
94759efa8b5SSam Leffler if (IEEE80211_IS_CHAN_TURBO(c))
94859efa8b5SSam Leffler return ctl | CTL_TURBO;
94959efa8b5SSam Leffler if (IEEE80211_IS_CHAN_A(c))
95059efa8b5SSam Leffler return ctl | CTL_11A;
95159efa8b5SSam Leffler return ctl;
95214779705SSam Leffler }
95314779705SSam Leffler
9547dd4de13SAdrian Chadd /*
9557dd4de13SAdrian Chadd * Update the current dfsDomain setting based on the given
9567dd4de13SAdrian Chadd * country code.
9577dd4de13SAdrian Chadd *
9587dd4de13SAdrian Chadd * Since FreeBSD/net80211 allows the channel set to change
9597dd4de13SAdrian Chadd * after the card has been setup (via ath_hal_init_channels())
9607dd4de13SAdrian Chadd * this function method is needed to update ah_dfsDomain.
9617dd4de13SAdrian Chadd */
9627dd4de13SAdrian Chadd void
ath_hal_update_dfsdomain(struct ath_hal * ah)9637dd4de13SAdrian Chadd ath_hal_update_dfsdomain(struct ath_hal *ah)
9647dd4de13SAdrian Chadd {
9657dd4de13SAdrian Chadd const REG_DOMAIN *rd5GHz = AH_PRIVATE(ah)->ah_rd5GHz;
9667dd4de13SAdrian Chadd HAL_DFS_DOMAIN dfsDomain = HAL_DFS_UNINIT_DOMAIN;
9677dd4de13SAdrian Chadd
9687dd4de13SAdrian Chadd if (rd5GHz->dfsMask & DFS_FCC3)
9697dd4de13SAdrian Chadd dfsDomain = HAL_DFS_FCC_DOMAIN;
9707dd4de13SAdrian Chadd if (rd5GHz->dfsMask & DFS_ETSI)
9717dd4de13SAdrian Chadd dfsDomain = HAL_DFS_ETSI_DOMAIN;
9727dd4de13SAdrian Chadd if (rd5GHz->dfsMask & DFS_MKK4)
9737dd4de13SAdrian Chadd dfsDomain = HAL_DFS_MKK4_DOMAIN;
9747dd4de13SAdrian Chadd AH_PRIVATE(ah)->ah_dfsDomain = dfsDomain;
9757dd4de13SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s ah_dfsDomain: %d\n",
9767dd4de13SAdrian Chadd __func__, AH_PRIVATE(ah)->ah_dfsDomain);
9777dd4de13SAdrian Chadd }
9787dd4de13SAdrian Chadd
97914779705SSam Leffler /*
98014779705SSam Leffler * Return the max allowed antenna gain and apply any regulatory
98114779705SSam Leffler * domain specific changes.
98214779705SSam Leffler *
98314779705SSam Leffler * NOTE: a negative reduction is possible in RD's that only
98414779705SSam Leffler * measure radiated power (e.g., ETSI) which would increase
98514779705SSam Leffler * that actual conducted output power (though never beyond
98614779705SSam Leffler * the calibrated target power).
98714779705SSam Leffler */
98814779705SSam Leffler u_int
ath_hal_getantennareduction(struct ath_hal * ah,const struct ieee80211_channel * chan,u_int twiceGain)98959efa8b5SSam Leffler ath_hal_getantennareduction(struct ath_hal *ah,
99059efa8b5SSam Leffler const struct ieee80211_channel *chan, u_int twiceGain)
99114779705SSam Leffler {
99259efa8b5SSam Leffler int8_t antennaMax = twiceGain - chan->ic_maxantgain*2;
99314779705SSam Leffler return (antennaMax < 0) ? 0 : antennaMax;
99414779705SSam Leffler }
995