/*- * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD: src/tools/tools/ath/athrd/athrd.c,v 1.2 2009/01/29 23:24:21 sam Exp $ */ #include "opt_ah.h" #include "ah.h" #include #include #include "ah_internal.h" #include "ah_eeprom_v3.h" /* XXX */ #include #include #include #include #include int ath_hal_debug = 0; HAL_CTRY_CODE cc = CTRY_DEFAULT; HAL_REG_DOMAIN rd = 169; /* FCC */ HAL_BOOL Amode = 1; HAL_BOOL Bmode = 1; HAL_BOOL Gmode = 1; HAL_BOOL HT20mode = 1; HAL_BOOL HT40mode = 1; HAL_BOOL turbo5Disable = AH_FALSE; HAL_BOOL turbo2Disable = AH_FALSE; u_int16_t _numCtls = 8; u_int16_t _ctl[32] = { 0x10, 0x13, 0x40, 0x30, 0x11, 0x31, 0x12, 0x32 }; RD_EDGES_POWER _rdEdgesPower[NUM_EDGES*NUM_CTLS] = { { 5180, 28, 0 }, /* 0x10 */ { 5240, 60, 0 }, { 5260, 36, 0 }, { 5320, 27, 0 }, { 5745, 36, 0 }, { 5765, 36, 0 }, { 5805, 36, 0 }, { 5825, 36, 0 }, { 5210, 28, 0 }, /* 0x13 */ { 5250, 28, 0 }, { 5290, 30, 0 }, { 5760, 36, 0 }, { 5800, 36, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 5170, 60, 0 }, /* 0x40 */ { 5230, 60, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 5180, 33, 0 }, /* 0x30 */ { 5320, 33, 0 }, { 5500, 34, 0 }, { 5700, 34, 0 }, { 5745, 35, 0 }, { 5765, 35, 0 }, { 5785, 35, 0 }, { 5825, 35, 0 }, { 2412, 36, 0 }, /* 0x11 */ { 2417, 36, 0 }, { 2422, 36, 0 }, { 2432, 36, 0 }, { 2442, 36, 0 }, { 2457, 36, 0 }, { 2467, 36, 0 }, { 2472, 36, 0 }, { 2412, 36, 0 }, /* 0x31 */ { 2417, 36, 0 }, { 2422, 36, 0 }, { 2432, 36, 0 }, { 2442, 36, 0 }, { 2457, 36, 0 }, { 2467, 36, 0 }, { 2472, 36, 0 }, { 2412, 36, 0 }, /* 0x12 */ { 2417, 36, 0 }, { 2422, 36, 0 }, { 2432, 36, 0 }, { 2442, 36, 0 }, { 2457, 36, 0 }, { 2467, 36, 0 }, { 2472, 36, 0 }, { 2412, 28, 0 }, /* 0x32 */ { 2417, 28, 0 }, { 2422, 28, 0 }, { 2432, 28, 0 }, { 2442, 28, 0 }, { 2457, 28, 0 }, { 2467, 28, 0 }, { 2472, 28, 0 }, }; u_int16_t turbo2WMaxPower5 = 32; u_int16_t turbo2WMaxPower2; int8_t antennaGainMax[2] = { 0, 0 }; /* XXX */ int eeversion = AR_EEPROM_VER3_1; TRGT_POWER_ALL_MODES tpow = { 8, { { 22, 24, 28, 32, 5180 }, { 22, 24, 28, 32, 5200 }, { 22, 24, 28, 32, 5320 }, { 26, 30, 34, 34, 5500 }, { 26, 30, 34, 34, 5700 }, { 20, 30, 34, 36, 5745 }, { 20, 30, 34, 36, 5825 }, { 20, 30, 34, 36, 5850 }, }, 2, { { 23, 27, 31, 34, 2412 }, { 23, 27, 31, 34, 2447 }, }, 2, { { 36, 36, 36, 36, 2412 }, { 36, 36, 36, 36, 2484 }, } }; #define numTargetPwr_11a tpow.numTargetPwr_11a #define trgtPwr_11a tpow.trgtPwr_11a #define numTargetPwr_11g tpow.numTargetPwr_11g #define trgtPwr_11g tpow.trgtPwr_11g #define numTargetPwr_11b tpow.numTargetPwr_11b #define trgtPwr_11b tpow.trgtPwr_11b static HAL_BOOL getChannelEdges(struct ath_hal *ah, u_int16_t flags, u_int16_t *low, u_int16_t *high) { struct ath_hal_private *ahp = AH_PRIVATE(ah); HAL_CAPABILITIES *pCap = &ahp->ah_caps; if (flags & IEEE80211_CHAN_5GHZ) { *low = pCap->halLow5GhzChan; *high = pCap->halHigh5GhzChan; return AH_TRUE; } if (flags & IEEE80211_CHAN_2GHZ) { *low = pCap->halLow2GhzChan; *high = pCap->halHigh2GhzChan; return AH_TRUE; } return AH_FALSE; } static u_int getWirelessModes(struct ath_hal *ah) { u_int mode = 0; if (Amode) { mode = HAL_MODE_11A; if (!turbo5Disable) mode |= HAL_MODE_TURBO; } if (Bmode) mode |= HAL_MODE_11B; if (Gmode) { mode |= HAL_MODE_11G; if (!turbo2Disable) mode |= HAL_MODE_108G; } if (HT20mode) mode |= HAL_MODE_11NG_HT20|HAL_MODE_11NA_HT20; if (HT40mode) mode |= HAL_MODE_11NG_HT40PLUS|HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NG_HT40MINUS|HAL_MODE_11NA_HT40MINUS ; return mode; } /* Enumerated Regulatory Domain Information 8 bit values indicate that * the regdomain is really a pair of unitary regdomains. 12 bit values * are the real unitary regdomains and are the only ones which have the * frequency bitmasks and flags set. */ enum EnumRd { /* * The following regulatory domain definitions are * found in the EEPROM. Each regulatory domain * can operate in either a 5GHz or 2.4GHz wireless mode or * both 5GHz and 2.4GHz wireless modes. * In general, the value holds no special * meaning and is used to decode into either specific * 2.4GHz or 5GHz wireless mode for that particular * regulatory domain. */ NO_ENUMRD = 0x00, NULL1_WORLD = 0x03, /* For 11b-only countries (no 11a allowed) */ NULL1_ETSIB = 0x07, /* Israel */ NULL1_ETSIC = 0x08, FCC1_FCCA = 0x10, /* USA */ FCC1_WORLD = 0x11, /* Hong Kong */ FCC4_FCCA = 0x12, /* USA - Public Safety */ FCC2_FCCA = 0x20, /* Canada */ FCC2_WORLD = 0x21, /* Australia & HK */ FCC2_ETSIC = 0x22, FRANCE_RES = 0x31, /* Legacy France for OEM */ FCC3_FCCA = 0x3A, /* USA & Canada w/5470 band, 11h, DFS enabled */ FCC3_WORLD = 0x3B, /* USA & Canada w/5470 band, 11h, DFS enabled */ ETSI1_WORLD = 0x37, ETSI3_ETSIA = 0x32, /* France (optional) */ ETSI2_WORLD = 0x35, /* Hungary & others */ ETSI3_WORLD = 0x36, /* France & others */ ETSI4_WORLD = 0x30, ETSI4_ETSIC = 0x38, ETSI5_WORLD = 0x39, ETSI6_WORLD = 0x34, /* Bulgaria */ ETSI_RESERVED = 0x33, /* Reserved (Do not used) */ MKK1_MKKA = 0x40, /* Japan (JP1) */ MKK1_MKKB = 0x41, /* Japan (JP0) */ APL4_WORLD = 0x42, /* Singapore */ MKK2_MKKA = 0x43, /* Japan with 4.9G channels */ APL_RESERVED = 0x44, /* Reserved (Do not used) */ APL2_WORLD = 0x45, /* Korea */ APL2_APLC = 0x46, APL3_WORLD = 0x47, MKK1_FCCA = 0x48, /* Japan (JP1-1) */ APL2_APLD = 0x49, /* Korea with 2.3G channels */ MKK1_MKKA1 = 0x4A, /* Japan (JE1) */ MKK1_MKKA2 = 0x4B, /* Japan (JE2) */ MKK1_MKKC = 0x4C, /* Japan (MKK1_MKKA,except Ch14) */ APL3_FCCA = 0x50, APL1_WORLD = 0x52, /* Latin America */ APL1_FCCA = 0x53, APL1_APLA = 0x54, APL1_ETSIC = 0x55, APL2_ETSIC = 0x56, /* Venezuela */ APL5_WORLD = 0x58, /* Chile */ APL6_WORLD = 0x5B, /* Singapore */ APL7_FCCA = 0x5C, /* Taiwan 5.47 Band */ APL8_WORLD = 0x5D, /* Malaysia 5GHz */ APL9_WORLD = 0x5E, /* Korea 5GHz */ /* * World mode SKUs */ WOR0_WORLD = 0x60, /* World0 (WO0 SKU) */ WOR1_WORLD = 0x61, /* World1 (WO1 SKU) */ WOR2_WORLD = 0x62, /* World2 (WO2 SKU) */ WOR3_WORLD = 0x63, /* World3 (WO3 SKU) */ WOR4_WORLD = 0x64, /* World4 (WO4 SKU) */ WOR5_ETSIC = 0x65, /* World5 (WO5 SKU) */ WOR01_WORLD = 0x66, /* World0-1 (WW0-1 SKU) */ WOR02_WORLD = 0x67, /* World0-2 (WW0-2 SKU) */ EU1_WORLD = 0x68, /* Same as World0-2 (WW0-2 SKU), except active scan ch1-13. No ch14 */ WOR9_WORLD = 0x69, /* World9 (WO9 SKU) */ WORA_WORLD = 0x6A, /* WorldA (WOA SKU) */ MKK3_MKKB = 0x80, /* Japan UNI-1 even + MKKB */ MKK3_MKKA2 = 0x81, /* Japan UNI-1 even + MKKA2 */ MKK3_MKKC = 0x82, /* Japan UNI-1 even + MKKC */ MKK4_MKKB = 0x83, /* Japan UNI-1 even + UNI-2 + MKKB */ MKK4_MKKA2 = 0x84, /* Japan UNI-1 even + UNI-2 + MKKA2 */ MKK4_MKKC = 0x85, /* Japan UNI-1 even + UNI-2 + MKKC */ MKK5_MKKB = 0x86, /* Japan UNI-1 even + UNI-2 + mid-band + MKKB */ MKK5_MKKA2 = 0x87, /* Japan UNI-1 even + UNI-2 + mid-band + MKKA2 */ MKK5_MKKC = 0x88, /* Japan UNI-1 even + UNI-2 + mid-band + MKKC */ MKK6_MKKB = 0x89, /* Japan UNI-1 even + UNI-1 odd MKKB */ MKK6_MKKA2 = 0x8A, /* Japan UNI-1 even + UNI-1 odd + MKKA2 */ MKK6_MKKC = 0x8B, /* Japan UNI-1 even + UNI-1 odd + MKKC */ MKK7_MKKB = 0x8C, /* Japan UNI-1 even + UNI-1 odd + UNI-2 + MKKB */ MKK7_MKKA2 = 0x8D, /* Japan UNI-1 even + UNI-1 odd + UNI-2 + MKKA2 */ MKK7_MKKC = 0x8E, /* Japan UNI-1 even + UNI-1 odd + UNI-2 + MKKC */ MKK8_MKKB = 0x8F, /* Japan UNI-1 even + UNI-1 odd + UNI-2 + mid-band + MKKB */ MKK8_MKKA2 = 0x90, /* Japan UNI-1 even + UNI-1 odd + UNI-2 + mid-band + MKKA2 */ MKK8_MKKC = 0x91, /* Japan UNI-1 even + UNI-1 odd + UNI-2 + mid-band + MKKC */ /* Following definitions are used only by s/w to map old * Japan SKUs. */ MKK3_MKKA = 0xF0, /* Japan UNI-1 even + MKKA */ MKK3_MKKA1 = 0xF1, /* Japan UNI-1 even + MKKA1 */ MKK3_FCCA = 0xF2, /* Japan UNI-1 even + FCCA */ MKK4_MKKA = 0xF3, /* Japan UNI-1 even + UNI-2 + MKKA */ MKK4_MKKA1 = 0xF4, /* Japan UNI-1 even + UNI-2 + MKKA1 */ MKK4_FCCA = 0xF5, /* Japan UNI-1 even + UNI-2 + FCCA */ MKK9_MKKA = 0xF6, /* Japan UNI-1 even + 4.9GHz */ MKK10_MKKA = 0xF7, /* Japan UNI-1 even + UNI-2 + 4.9GHz */ /* * Regulator domains ending in a number (e.g. APL1, * MK1, ETSI4, etc) apply to 5GHz channel and power * information. Regulator domains ending in a letter * (e.g. APLA, FCCA, etc) apply to 2.4GHz channel and * power information. */ APL1 = 0x0150, /* LAT & Asia */ APL2 = 0x0250, /* LAT & Asia */ APL3 = 0x0350, /* Taiwan */ APL4 = 0x0450, /* Jordan */ APL5 = 0x0550, /* Chile */ APL6 = 0x0650, /* Singapore */ APL8 = 0x0850, /* Malaysia */ APL9 = 0x0950, /* Korea (South) ROC 3 */ ETSI1 = 0x0130, /* Europe & others */ ETSI2 = 0x0230, /* Europe & others */ ETSI3 = 0x0330, /* Europe & others */ ETSI4 = 0x0430, /* Europe & others */ ETSI5 = 0x0530, /* Europe & others */ ETSI6 = 0x0630, /* Europe & others */ ETSIA = 0x0A30, /* France */ ETSIB = 0x0B30, /* Israel */ ETSIC = 0x0C30, /* Latin America */ FCC1 = 0x0110, /* US & others */ FCC2 = 0x0120, /* Canada, Australia & New Zealand */ FCC3 = 0x0160, /* US w/new middle band & DFS */ FCC4 = 0x0165, /* US Public Safety */ FCCA = 0x0A10, APLD = 0x0D50, /* South Korea */ MKK1 = 0x0140, /* Japan (UNI-1 odd)*/ MKK2 = 0x0240, /* Japan (4.9 GHz + UNI-1 odd) */ MKK3 = 0x0340, /* Japan (UNI-1 even) */ MKK4 = 0x0440, /* Japan (UNI-1 even + UNI-2) */ MKK5 = 0x0540, /* Japan (UNI-1 even + UNI-2 + mid-band) */ MKK6 = 0x0640, /* Japan (UNI-1 odd + UNI-1 even) */ MKK7 = 0x0740, /* Japan (UNI-1 odd + UNI-1 even + UNI-2 */ MKK8 = 0x0840, /* Japan (UNI-1 odd + UNI-1 even + UNI-2 + mid-band) */ MKK9 = 0x0940, /* Japan (UNI-1 even + 4.9 GHZ) */ MKK10 = 0x0B40, /* Japan (UNI-1 even + UNI-2 + 4.9 GHZ) */ MKKA = 0x0A40, /* Japan */ MKKC = 0x0A50, NULL1 = 0x0198, WORLD = 0x0199, DEBUG_REG_DMN = 0x01ff, }; #define DEF_REGDMN FCC1_FCCA static struct { const char *name; HAL_REG_DOMAIN rd; } domains[] = { #define D(_x) { #_x, _x } D(NO_ENUMRD), D(NULL1_WORLD), /* For 11b-only countries (no 11a allowed) */ D(NULL1_ETSIB), /* Israel */ D(NULL1_ETSIC), D(FCC1_FCCA), /* USA */ D(FCC1_WORLD), /* Hong Kong */ D(FCC4_FCCA), /* USA - Public Safety */ D(FCC2_FCCA), /* Canada */ D(FCC2_WORLD), /* Australia & HK */ D(FCC2_ETSIC), D(FRANCE_RES), /* Legacy France for OEM */ D(FCC3_FCCA), D(FCC3_WORLD), D(ETSI1_WORLD), D(ETSI3_ETSIA), /* France (optional) */ D(ETSI2_WORLD), /* Hungary & others */ D(ETSI3_WORLD), /* France & others */ D(ETSI4_WORLD), D(ETSI4_ETSIC), D(ETSI5_WORLD), D(ETSI6_WORLD), /* Bulgaria */ D(ETSI_RESERVED), /* Reserved (Do not used) */ D(MKK1_MKKA), /* Japan (JP1) */ D(MKK1_MKKB), /* Japan (JP0) */ D(APL4_WORLD), /* Singapore */ D(MKK2_MKKA), /* Japan with 4.9G channels */ D(APL_RESERVED), /* Reserved (Do not used) */ D(APL2_WORLD), /* Korea */ D(APL2_APLC), D(APL3_WORLD), D(MKK1_FCCA), /* Japan (JP1-1) */ D(APL2_APLD), /* Korea with 2.3G channels */ D(MKK1_MKKA1), /* Japan (JE1) */ D(MKK1_MKKA2), /* Japan (JE2) */ D(MKK1_MKKC), D(APL3_FCCA), D(APL1_WORLD), /* Latin America */ D(APL1_FCCA), D(APL1_APLA), D(APL1_ETSIC), D(APL2_ETSIC), /* Venezuela */ D(APL5_WORLD), /* Chile */ D(APL6_WORLD), /* Singapore */ D(APL7_FCCA), /* Taiwan 5.47 Band */ D(APL8_WORLD), /* Malaysia 5GHz */ D(APL9_WORLD), /* Korea 5GHz */ D(WOR0_WORLD), /* World0 (WO0 SKU) */ D(WOR1_WORLD), /* World1 (WO1 SKU) */ D(WOR2_WORLD), /* World2 (WO2 SKU) */ D(WOR3_WORLD), /* World3 (WO3 SKU) */ D(WOR4_WORLD), /* World4 (WO4 SKU) */ D(WOR5_ETSIC), /* World5 (WO5 SKU) */ D(WOR01_WORLD), /* World0-1 (WW0-1 SKU) */ D(WOR02_WORLD), /* World0-2 (WW0-2 SKU) */ D(EU1_WORLD), D(WOR9_WORLD), /* World9 (WO9 SKU) */ D(WORA_WORLD), /* WorldA (WOA SKU) */ D(MKK3_MKKB), /* Japan UNI-1 even + MKKB */ D(MKK3_MKKA2), /* Japan UNI-1 even + MKKA2 */ D(MKK3_MKKC), /* Japan UNI-1 even + MKKC */ D(MKK4_MKKB), /* Japan UNI-1 even + UNI-2 + MKKB */ D(MKK4_MKKA2), /* Japan UNI-1 even + UNI-2 + MKKA2 */ D(MKK4_MKKC), /* Japan UNI-1 even + UNI-2 + MKKC */ D(MKK5_MKKB), /* Japan UNI-1 even + UNI-2 + mid-band + MKKB */ D(MKK5_MKKA2), /* Japan UNI-1 even + UNI-2 + mid-band + MKKA2 */ D(MKK5_MKKC), /* Japan UNI-1 even + UNI-2 + mid-band + MKKC */ D(MKK6_MKKB), /* Japan UNI-1 even + UNI-1 odd MKKB */ D(MKK6_MKKA2), /* Japan UNI-1 even + UNI-1 odd + MKKA2 */ D(MKK6_MKKC), /* Japan UNI-1 even + UNI-1 odd + MKKC */ D(MKK7_MKKB), /* Japan UNI-1 even + UNI-1 odd + UNI-2 + MKKB */ D(MKK7_MKKA2), /* Japan UNI-1 even + UNI-1 odd + UNI-2 + MKKA2 */ D(MKK7_MKKC), /* Japan UNI-1 even + UNI-1 odd + UNI-2 + MKKC */ D(MKK8_MKKB), /* Japan UNI-1 even + UNI-1 odd + UNI-2 + mid-band + MKKB */ D(MKK8_MKKA2), /* Japan UNI-1 even + UNI-1 odd + UNI-2 + mid-band + MKKA2 */ D(MKK8_MKKC), /* Japan UNI-1 even + UNI-1 odd + UNI-2 + mid-band + MKKC */ D(MKK3_MKKA), /* Japan UNI-1 even + MKKA */ D(MKK3_MKKA1), /* Japan UNI-1 even + MKKA1 */ D(MKK3_FCCA), /* Japan UNI-1 even + FCCA */ D(MKK4_MKKA), /* Japan UNI-1 even + UNI-2 + MKKA */ D(MKK4_MKKA1), /* Japan UNI-1 even + UNI-2 + MKKA1 */ D(MKK4_FCCA), /* Japan UNI-1 even + UNI-2 + FCCA */ D(MKK9_MKKA), /* Japan UNI-1 even + 4.9GHz */ D(MKK10_MKKA), /* Japan UNI-1 even + UNI-2 + 4.9GHz */ D(APL1), /* LAT & Asia */ D(APL2), /* LAT & Asia */ D(APL3), /* Taiwan */ D(APL4), /* Jordan */ D(APL5), /* Chile */ D(APL6), /* Singapore */ D(APL8), /* Malaysia */ D(APL9), /* Korea (South) ROC 3 */ D(ETSI1), /* Europe & others */ D(ETSI2), /* Europe & others */ D(ETSI3), /* Europe & others */ D(ETSI4), /* Europe & others */ D(ETSI5), /* Europe & others */ D(ETSI6), /* Europe & others */ D(ETSIA), /* France */ D(ETSIB), /* Israel */ D(ETSIC), /* Latin America */ D(FCC1), /* US & others */ D(FCC2), D(FCC3), /* US w/new middle band & DFS */ D(FCC4), /* US Public Safety */ D(FCCA), D(APLD), /* South Korea */ D(MKK1), /* Japan (UNI-1 odd)*/ D(MKK2), /* Japan (4.9 GHz + UNI-1 odd) */ D(MKK3), /* Japan (UNI-1 even) */ D(MKK4), /* Japan (UNI-1 even + UNI-2) */ D(MKK5), /* Japan (UNI-1 even + UNI-2 + mid-band) */ D(MKK6), /* Japan (UNI-1 odd + UNI-1 even) */ D(MKK7), /* Japan (UNI-1 odd + UNI-1 even + UNI-2 */ D(MKK8), /* Japan (UNI-1 odd + UNI-1 even + UNI-2 + mid-band) */ D(MKK9), /* Japan (UNI-1 even + 4.9 GHZ) */ D(MKK10), /* Japan (UNI-1 even + UNI-2 + 4.9 GHZ) */ D(MKKA), /* Japan */ D(MKKC), D(NULL1), D(WORLD), D(DEBUG_REG_DMN), #undef D }; static HAL_BOOL rdlookup(const char *name, HAL_REG_DOMAIN *rd) { #define N(a) (sizeof(a)/sizeof(a[0])) int i; for (i = 0; i < N(domains); i++) if (strcasecmp(domains[i].name, name) == 0) { *rd = domains[i].rd; return AH_TRUE; } return AH_FALSE; #undef N } static const char * getrdname(HAL_REG_DOMAIN rd) { #define N(a) (sizeof(a)/sizeof(a[0])) int i; for (i = 0; i < N(domains); i++) if (domains[i].rd == rd) return domains[i].name; return NULL; #undef N } static void rdlist() { #define N(a) (sizeof(a)/sizeof(a[0])) int i; printf("\nRegulatory domains:\n\n"); for (i = 0; i < N(domains); i++) printf("%-15s%s", domains[i].name, ((i+1)%5) == 0 ? "\n" : ""); printf("\n"); #undef N } typedef struct { HAL_CTRY_CODE countryCode; HAL_REG_DOMAIN regDmnEnum; const char* isoName; const char* name; } COUNTRY_CODE_TO_ENUM_RD; /* * Country Code Table to Enumerated RD */ static COUNTRY_CODE_TO_ENUM_RD allCountries[] = { {CTRY_DEBUG, NO_ENUMRD, "DB", "DEBUG" }, {CTRY_DEFAULT, DEF_REGDMN, "NA", "NO_COUNTRY_SET" }, {CTRY_ALBANIA, NULL1_WORLD, "AL", "ALBANIA" }, {CTRY_ALGERIA, NULL1_WORLD, "DZ", "ALGERIA" }, {CTRY_ARGENTINA, APL3_WORLD, "AR", "ARGENTINA" }, {CTRY_ARMENIA, ETSI4_WORLD, "AM", "ARMENIA" }, {CTRY_AUSTRALIA, FCC2_WORLD, "AU", "AUSTRALIA" }, {CTRY_AUSTRIA, ETSI1_WORLD, "AT", "AUSTRIA" }, {CTRY_AZERBAIJAN, ETSI4_WORLD, "AZ", "AZERBAIJAN" }, {CTRY_BAHRAIN, APL6_WORLD, "BH", "BAHRAIN" }, {CTRY_BELARUS, NULL1_WORLD, "BY", "BELARUS" }, {CTRY_BELGIUM, ETSI1_WORLD, "BE", "BELGIUM" }, {CTRY_BELIZE, APL1_ETSIC, "BZ", "BELIZE" }, {CTRY_BOLIVIA, APL1_ETSIC, "BO", "BOLVIA" }, {CTRY_BRAZIL, FCC3_WORLD, "BR", "BRAZIL" }, {CTRY_BRUNEI_DARUSSALAM,APL1_WORLD,"BN", "BRUNEI DARUSSALAM" }, {CTRY_BULGARIA, ETSI6_WORLD, "BG", "BULGARIA" }, {CTRY_CANADA, FCC2_FCCA, "CA", "CANADA" }, {CTRY_CHILE, APL6_WORLD, "CL", "CHILE" }, {CTRY_CHINA, APL1_WORLD, "CN", "CHINA" }, {CTRY_COLOMBIA, FCC1_FCCA, "CO", "COLOMBIA" }, {CTRY_COSTA_RICA, NULL1_WORLD, "CR", "COSTA RICA" }, {CTRY_CROATIA, ETSI3_WORLD, "HR", "CROATIA" }, {CTRY_CYPRUS, ETSI1_WORLD, "CY", "CYPRUS" }, {CTRY_CZECH, ETSI3_WORLD, "CZ", "CZECH REPUBLIC" }, {CTRY_DENMARK, ETSI1_WORLD, "DK", "DENMARK" }, {CTRY_DOMINICAN_REPUBLIC,FCC1_FCCA,"DO", "DOMINICAN REPUBLIC" }, {CTRY_ECUADOR, NULL1_WORLD, "EC", "ECUADOR" }, {CTRY_EGYPT, ETSI3_WORLD, "EG", "EGYPT" }, {CTRY_EL_SALVADOR, NULL1_WORLD, "SV", "EL SALVADOR" }, {CTRY_ESTONIA, ETSI1_WORLD, "EE", "ESTONIA" }, {CTRY_FINLAND, ETSI1_WORLD, "FI", "FINLAND" }, {CTRY_FRANCE, ETSI3_WORLD, "FR", "FRANCE" }, {CTRY_FRANCE2, ETSI3_WORLD, "F2", "FRANCE_RES" }, {CTRY_GEORGIA, ETSI4_WORLD, "GE", "GEORGIA" }, {CTRY_GERMANY, ETSI1_WORLD, "DE", "GERMANY" }, {CTRY_GREECE, ETSI1_WORLD, "GR", "GREECE" }, {CTRY_GUATEMALA, FCC1_FCCA, "GT", "GUATEMALA" }, {CTRY_HONDURAS, NULL1_WORLD, "HN", "HONDURAS" }, {CTRY_HONG_KONG, FCC2_WORLD, "HK", "HONG KONG" }, {CTRY_HUNGARY, ETSI1_WORLD, "HU", "HUNGARY" }, {CTRY_ICELAND, ETSI1_WORLD, "IS", "ICELAND" }, {CTRY_INDIA, APL6_WORLD, "IN", "INDIA" }, {CTRY_INDONESIA, APL1_WORLD, "ID", "INDONESIA" }, {CTRY_IRAN, APL1_WORLD, "IR", "IRAN" }, {CTRY_IRELAND, ETSI1_WORLD, "IE", "IRELAND" }, {CTRY_ISRAEL, NULL1_WORLD, "IL", "ISRAEL" }, {CTRY_ITALY, ETSI1_WORLD, "IT", "ITALY" }, {CTRY_JAPAN, MKK1_MKKA, "JP", "JAPAN" }, {CTRY_JAPAN1, MKK1_MKKB, "JP", "JAPAN1" }, {CTRY_JAPAN2, MKK1_FCCA, "JP", "JAPAN2" }, {CTRY_JAPAN3, MKK2_MKKA, "JP", "JAPAN3" }, {CTRY_JAPAN4, MKK1_MKKA1, "JP", "JAPAN4" }, {CTRY_JAPAN5, MKK1_MKKA2, "JP", "JAPAN5" }, {CTRY_JAPAN6, MKK1_MKKC, "JP", "JAPAN6" }, {CTRY_JAPAN7, MKK3_MKKB, "JP", "JAPAN7" }, {CTRY_JAPAN8, MKK3_MKKA2, "JP", "JAPAN8" }, {CTRY_JAPAN9, MKK3_MKKC, "JP", "JAPAN9" }, {CTRY_JAPAN10, MKK4_MKKB, "JP", "JAPAN10" }, {CTRY_JAPAN11, MKK4_MKKA2, "JP", "JAPAN11" }, {CTRY_JAPAN12, MKK4_MKKC, "JP", "JAPAN12" }, {CTRY_JAPAN13, MKK5_MKKB, "JP", "JAPAN13" }, {CTRY_JAPAN14, MKK5_MKKA2, "JP", "JAPAN14" }, {CTRY_JAPAN15, MKK5_MKKC, "JP", "JAPAN15" }, {CTRY_JAPAN16, MKK6_MKKB, "JP", "JAPAN16" }, {CTRY_JAPAN17, MKK6_MKKA2, "JP", "JAPAN17" }, {CTRY_JAPAN18, MKK6_MKKC, "JP", "JAPAN18" }, {CTRY_JAPAN19, MKK7_MKKB, "JP", "JAPAN19" }, {CTRY_JAPAN20, MKK7_MKKA2, "JP", "JAPAN20" }, {CTRY_JAPAN21, MKK7_MKKC, "JP", "JAPAN21" }, {CTRY_JAPAN22, MKK8_MKKB, "JP", "JAPAN22" }, {CTRY_JAPAN23, MKK8_MKKA2, "JP", "JAPAN23" }, {CTRY_JAPAN24, MKK8_MKKC, "JP", "JAPAN24" }, {CTRY_JORDAN, APL4_WORLD, "JO", "JORDAN" }, {CTRY_KAZAKHSTAN, NULL1_WORLD, "KZ", "KAZAKHSTAN" }, {CTRY_KOREA_NORTH, APL2_WORLD, "KP", "NORTH KOREA" }, {CTRY_KOREA_ROC, APL2_WORLD, "KR", "KOREA REPUBLIC" }, {CTRY_KOREA_ROC2, APL2_WORLD, "K2", "KOREA REPUBLIC2" }, {CTRY_KOREA_ROC3, APL9_WORLD, "K3", "KOREA REPUBLIC3" }, {CTRY_KUWAIT, NULL1_WORLD, "KW", "KUWAIT" }, {CTRY_LATVIA, ETSI1_WORLD, "LV", "LATVIA" }, {CTRY_LEBANON, NULL1_WORLD, "LB", "LEBANON" }, {CTRY_LIECHTENSTEIN,ETSI1_WORLD, "LI", "LIECHTENSTEIN" }, {CTRY_LITHUANIA, ETSI1_WORLD, "LT", "LITHUANIA" }, {CTRY_LUXEMBOURG, ETSI1_WORLD, "LU", "LUXEMBOURG" }, {CTRY_MACAU, FCC2_WORLD, "MO", "MACAU" }, {CTRY_MACEDONIA, NULL1_WORLD, "MK", "MACEDONIA" }, {CTRY_MALAYSIA, APL8_WORLD, "MY", "MALAYSIA" }, {CTRY_MALTA, ETSI1_WORLD, "MT", "MALTA" }, {CTRY_MEXICO, FCC1_FCCA, "MX", "MEXICO" }, {CTRY_MONACO, ETSI4_WORLD, "MC", "MONACO" }, {CTRY_MOROCCO, NULL1_WORLD, "MA", "MOROCCO" }, {CTRY_NETHERLANDS, ETSI1_WORLD, "NL", "NETHERLANDS" }, {CTRY_NEW_ZEALAND, FCC2_ETSIC, "NZ", "NEW ZEALAND" }, {CTRY_NORWAY, ETSI1_WORLD, "NO", "NORWAY" }, {CTRY_OMAN, APL6_WORLD, "OM", "OMAN" }, {CTRY_PAKISTAN, NULL1_WORLD, "PK", "PAKISTAN" }, {CTRY_PANAMA, FCC1_FCCA, "PA", "PANAMA" }, {CTRY_PERU, APL1_WORLD, "PE", "PERU" }, {CTRY_PHILIPPINES, APL1_WORLD, "PH", "PHILIPPINES" }, {CTRY_POLAND, ETSI1_WORLD, "PL", "POLAND" }, {CTRY_PORTUGAL, ETSI1_WORLD, "PT", "PORTUGAL" }, {CTRY_PUERTO_RICO, FCC1_FCCA, "PR", "PUERTO RICO" }, {CTRY_QATAR, NULL1_WORLD, "QA", "QATAR" }, {CTRY_ROMANIA, NULL1_WORLD, "RO", "ROMANIA" }, {CTRY_RUSSIA, NULL1_WORLD, "RU", "RUSSIA" }, {CTRY_SAUDI_ARABIA,NULL1_WORLD, "SA", "SAUDI ARABIA" }, {CTRY_SINGAPORE, APL6_WORLD, "SG", "SINGAPORE" }, {CTRY_SLOVAKIA, ETSI1_WORLD, "SK", "SLOVAK REPUBLIC" }, {CTRY_SLOVENIA, ETSI1_WORLD, "SI", "SLOVENIA" }, {CTRY_SOUTH_AFRICA,FCC3_WORLD, "ZA", "SOUTH AFRICA" }, {CTRY_SPAIN, ETSI1_WORLD, "ES", "SPAIN" }, {CTRY_SWEDEN, ETSI1_WORLD, "SE", "SWEDEN" }, {CTRY_SWITZERLAND, ETSI1_WORLD, "CH", "SWITZERLAND" }, {CTRY_SYRIA, NULL1_WORLD, "SY", "SYRIA" }, {CTRY_TAIWAN, APL3_FCCA, "TW", "TAIWAN" }, {CTRY_THAILAND, NULL1_WORLD, "TH", "THAILAND" }, {CTRY_TRINIDAD_Y_TOBAGO,ETSI4_WORLD,"TT", "TRINIDAD & TOBAGO" }, {CTRY_TUNISIA, ETSI3_WORLD, "TN", "TUNISIA" }, {CTRY_TURKEY, ETSI3_WORLD, "TR", "TURKEY" }, {CTRY_UKRAINE, NULL1_WORLD, "UA", "UKRAINE" }, {CTRY_UAE, NULL1_WORLD, "AE", "UNITED ARAB EMIRATES" }, {CTRY_UNITED_KINGDOM, ETSI1_WORLD,"GB", "UNITED KINGDOM" }, {CTRY_UNITED_STATES, FCC1_FCCA, "US", "UNITED STATES" }, {CTRY_UNITED_STATES_FCC49, FCC4_FCCA, "PS", "UNITED STATES (PUBLIC SAFETY)" }, {CTRY_URUGUAY, APL2_WORLD, "UY", "URUGUAY" }, {CTRY_UZBEKISTAN, FCC3_FCCA, "UZ", "UZBEKISTAN" }, {CTRY_VENEZUELA, APL2_ETSIC, "VE", "VENEZUELA" }, {CTRY_VIET_NAM, NULL1_WORLD, "VN", "VIET NAM" }, {CTRY_YEMEN, NULL1_WORLD, "YE", "YEMEN" }, {CTRY_ZIMBABWE, NULL1_WORLD, "ZW", "ZIMBABWE" } }; static HAL_BOOL cclookup(const char *name, HAL_REG_DOMAIN *rd, HAL_CTRY_CODE *cc) { #define N(a) (sizeof(a)/sizeof(a[0])) int i; for (i = 0; i < N(allCountries); i++) if (strcasecmp(allCountries[i].isoName, name) == 0 || strcasecmp(allCountries[i].name, name) == 0) { *rd = allCountries[i].regDmnEnum; *cc = allCountries[i].countryCode; return AH_TRUE; } return AH_FALSE; #undef N } static const char * getccname(HAL_CTRY_CODE cc) { #define N(a) (sizeof(a)/sizeof(a[0])) int i; for (i = 0; i < N(allCountries); i++) if (allCountries[i].countryCode == cc) return allCountries[i].name; return NULL; #undef N } static const char * getccisoname(HAL_CTRY_CODE cc) { #define N(a) (sizeof(a)/sizeof(a[0])) int i; for (i = 0; i < N(allCountries); i++) if (allCountries[i].countryCode == cc) return allCountries[i].isoName; return NULL; #undef N } static void cclist() { #define N(a) (sizeof(a)/sizeof(a[0])) int i; printf("\nCountry codes:\n"); for (i = 0; i < N(allCountries); i++) printf("%2s %-15.15s%s", allCountries[i].isoName, allCountries[i].name, ((i+1)%4) == 0 ? "\n" : " "); printf("\n"); #undef N } static HAL_BOOL setRateTable(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t tpcScaleReduction, int16_t powerLimit, int16_t *pMinPower, int16_t *pMaxPower); static void calctxpower(struct ath_hal *ah, int nchan, const struct ieee80211_channel *chans, int16_t tpcScaleReduction, int16_t powerLimit, int16_t *txpow) { int16_t minpow; int i; for (i = 0; i < nchan; i++) if (!setRateTable(ah, &chans[i], tpcScaleReduction, powerLimit, &minpow, &txpow[i])) { printf("unable to set rate table\n"); exit(-1); } } int n = 1; const char *sep = ""; int dopassive = 0; int showchannels = 0; int isdfs = 0; int is4ms = 0; static int anychan(const struct ieee80211_channel *chans, int nc, int flag) { int i; for (i = 0; i < nc; i++) if ((chans[i].ic_flags & flag) != 0) return 1; return 0; } static __inline int mapgsm(u_int freq, u_int flags) { freq *= 10; if (flags & IEEE80211_CHAN_QUARTER) freq += 5; else if (flags & IEEE80211_CHAN_HALF) freq += 10; else freq += 20; return (freq - 24220) / 5; } static __inline int mappsb(u_int freq, u_int flags) { return ((freq * 10) + (((freq % 5) == 2) ? 5 : 0) - 49400) / 5; } /* * Convert GHz frequency to IEEE channel number. */ int ath_hal_mhz2ieee(struct ath_hal *ah, u_int freq, u_int flags) { if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ if (freq == 2484) return 14; if (freq < 2484) return ((int)freq - 2407) / 5; else return 15 + ((freq - 2512) / 20); } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ if (IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) return mappsb(freq, flags); else if ((flags & IEEE80211_CHAN_A) && (freq <= 5000)) return (freq - 4000) / 5; else return (freq - 5000) / 5; } else { /* either, guess */ if (freq == 2484) return 14; if (freq < 2484) return ((int)freq - 2407) / 5; if (freq < 5000) { if (IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) return mappsb(freq, flags); else if (freq > 4900) return (freq - 4000) / 5; else return 15 + ((freq - 2512) / 20); } return (freq - 5000) / 5; } } #define IEEE80211_IS_CHAN_4MS(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_4MSXMIT) != 0) static void dumpchannels(struct ath_hal *ah, int nc, const struct ieee80211_channel *chans, int16_t *txpow) { int i; for (i = 0; i < nc; i++) { const struct ieee80211_channel *c = &chans[i]; int type; if (showchannels) printf("%s%3d", sep, ath_hal_mhz2ieee(ah, c->ic_freq, c->ic_flags)); else printf("%s%u", sep, c->ic_freq); if (IEEE80211_IS_CHAN_HALF(c)) type = 'H'; else if (IEEE80211_IS_CHAN_QUARTER(c)) type = 'Q'; else if (IEEE80211_IS_CHAN_TURBO(c)) type = 'T'; else if (IEEE80211_IS_CHAN_HT(c)) type = 'N'; else if (IEEE80211_IS_CHAN_A(c)) type = 'A'; else if (IEEE80211_IS_CHAN_108G(c)) type = 'T'; else if (IEEE80211_IS_CHAN_G(c)) type = 'G'; else type = 'B'; if (dopassive && IEEE80211_IS_CHAN_PASSIVE(c)) type = tolower(type); if (isdfs && is4ms) printf("%c%c%c %d.%d", type, IEEE80211_IS_CHAN_DFS(c) ? '*' : ' ', IEEE80211_IS_CHAN_4MS(c) ? '4' : ' ', txpow[i]/2, (txpow[i]%2)*5); else if (isdfs) printf("%c%c %d.%d", type, IEEE80211_IS_CHAN_DFS(c) ? '*' : ' ', txpow[i]/2, (txpow[i]%2)*5); else if (is4ms) printf("%c%c %d.%d", type, IEEE80211_IS_CHAN_4MS(c) ? '4' : ' ', txpow[i]/2, (txpow[i]%2)*5); else printf("%c %d.%d", type, txpow[i]/2, (txpow[i]%2)*5); if ((n++ % (showchannels ? 7 : 6)) == 0) sep = "\n"; else sep = " "; } } static void intersect(struct ieee80211_channel *dst, int16_t *dtxpow, int *nd, const struct ieee80211_channel *src, int16_t *stxpow, int ns) { int i = 0, j, k, l; while (i < *nd) { for (j = 0; j < ns && dst[i].ic_freq != src[j].ic_freq; j++) ; if (j < ns && dtxpow[i] == stxpow[j]) { for (k = i+1, l = i; k < *nd; k++, l++) dst[l] = dst[k]; (*nd)--; } else i++; } } static void usage(const char *progname) { printf("usage: %s [-acdefoilpr4ABGT] [-m opmode] [cc | rd]\n", progname); exit(-1); } static HAL_BOOL getChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan) { } static HAL_BOOL eepromRead(struct ath_hal *ah, u_int off, u_int16_t *data) { /* emulate enough stuff to handle japan channel shift */ switch (off) { case AR_EEPROM_VERSION: *data = eeversion; return AH_TRUE; case AR_EEPROM_REG_CAPABILITIES_OFFSET: *data = AR_EEPROM_EEREGCAP_EN_KK_NEW_11A; return AH_TRUE; case AR_EEPROM_REG_CAPABILITIES_OFFSET_PRE4_0: *data = AR_EEPROM_EEREGCAP_EN_KK_NEW_11A_PRE4_0; return AH_TRUE; } return AH_FALSE; } HAL_STATUS getCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t *result) { const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; switch (type) { case HAL_CAP_REG_DMN: /* regulatory domain */ *result = AH_PRIVATE(ah)->ah_currentRD; return HAL_OK; default: return HAL_EINVAL; } } #define HAL_MODE_HT20 \ (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20) #define HAL_MODE_HT40 \ (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NA_HT40MINUS) #define HAL_MODE_HT (HAL_MODE_HT20 | HAL_MODE_HT40) int main(int argc, char *argv[]) { static const u_int16_t tpcScaleReductionTable[5] = { 0, 3, 6, 9, MAX_RATE_POWER }; struct ath_hal_private ahp; struct ieee80211_channel achans[IEEE80211_CHAN_MAX]; int16_t atxpow[IEEE80211_CHAN_MAX]; struct ieee80211_channel bchans[IEEE80211_CHAN_MAX]; int16_t btxpow[IEEE80211_CHAN_MAX]; struct ieee80211_channel gchans[IEEE80211_CHAN_MAX]; int16_t gtxpow[IEEE80211_CHAN_MAX]; struct ieee80211_channel tchans[IEEE80211_CHAN_MAX]; int16_t ttxpow[IEEE80211_CHAN_MAX]; struct ieee80211_channel tgchans[IEEE80211_CHAN_MAX]; int16_t tgtxpow[IEEE80211_CHAN_MAX]; struct ieee80211_channel nchans[IEEE80211_CHAN_MAX]; int16_t ntxpow[IEEE80211_CHAN_MAX]; int i, na, nb, ng, nt, ntg, nn; HAL_BOOL showall = AH_FALSE; HAL_BOOL extendedChanMode = AH_TRUE; int modes = 0; int16_t tpcReduction, powerLimit; int showdfs = 0; int show4ms = 0; memset(&ahp, 0, sizeof(ahp)); ahp.ah_getChannelEdges = getChannelEdges; ahp.ah_getWirelessModes = getWirelessModes; ahp.ah_eepromRead = eepromRead; ahp.ah_getChipPowerLimits = getChipPowerLimits; ahp.ah_caps.halWirelessModes = HAL_MODE_ALL; ahp.ah_caps.halLow5GhzChan = 4920; ahp.ah_caps.halHigh5GhzChan = 6100; ahp.ah_caps.halLow2GhzChan = 2312; ahp.ah_caps.halHigh2GhzChan = 2732; ahp.ah_caps.halChanHalfRate = AH_TRUE; ahp.ah_caps.halChanQuarterRate = AH_TRUE; ahp.h.ah_getCapability = getCapability; ahp.ah_opmode = HAL_M_STA; tpcReduction = tpcScaleReductionTable[0]; powerLimit = MAX_RATE_POWER; while ((i = getopt(argc, argv, "acdeflm:pr4ABGhHNT")) != -1) switch (i) { case 'a': showall = AH_TRUE; break; case 'c': showchannels = AH_TRUE; break; case 'd': ath_hal_debug = HAL_DEBUG_ANY; break; case 'e': extendedChanMode = AH_FALSE; break; case 'f': showchannels = AH_FALSE; break; case 'l': cclist(); rdlist(); exit(0); case 'm': if (strncasecmp(optarg, "sta", 2) == 0) ahp.ah_opmode = HAL_M_STA; else if (strncasecmp(optarg, "ibss", 2) == 0) ahp.ah_opmode = HAL_M_IBSS; else if (strncasecmp(optarg, "adhoc", 2) == 0) ahp.ah_opmode = HAL_M_IBSS; else if (strncasecmp(optarg, "ap", 2) == 0) ahp.ah_opmode = HAL_M_HOSTAP; else if (strncasecmp(optarg, "hostap", 2) == 0) ahp.ah_opmode = HAL_M_HOSTAP; else if (strncasecmp(optarg, "monitor", 2) == 0) ahp.ah_opmode = HAL_M_MONITOR; else usage(argv[0]); break; case 'p': dopassive = 1; break; case 'A': modes |= HAL_MODE_11A; break; case 'B': modes |= HAL_MODE_11B; break; case 'G': modes |= HAL_MODE_11G; break; case 'h': modes |= HAL_MODE_HT20; break; case 'H': modes |= HAL_MODE_HT40; break; case 'N': modes |= HAL_MODE_HT; break; case 'T': modes |= HAL_MODE_TURBO | HAL_MODE_108G; break; case 'r': showdfs = 1; break; case '4': show4ms = 1; break; default: usage(argv[0]); } switch (argc - optind) { case 0: if (!cclookup("US", &rd, &cc)) { printf("%s: unknown country code\n", "US"); exit(-1); } break; case 1: /* cc/regdomain */ if (!cclookup(argv[optind], &rd, &cc)) { if (!rdlookup(argv[optind], &rd)) { const char* rdname; rd = strtoul(argv[optind], NULL, 0); rdname = getrdname(rd); if (rdname == NULL) { printf("%s: unknown country/regulatory " "domain code\n", argv[optind]); exit(-1); } } cc = CTRY_DEFAULT; } break; default: /* regdomain cc */ if (!rdlookup(argv[optind], &rd)) { const char* rdname; rd = strtoul(argv[optind], NULL, 0); rdname = getrdname(rd); if (rdname == NULL) { printf("%s: unknown country/regulatory " "domain code\n", argv[optind]); exit(-1); } } if (!cclookup(argv[optind+1], &rd, &cc)) cc = strtoul(argv[optind+1], NULL, 0); break; } if (cc != CTRY_DEFAULT) printf("\n%s (%s, 0x%x, %u) %s (0x%x, %u)\n", getccname(cc), getccisoname(cc), cc, cc, getrdname(rd), rd, rd); else printf("\n%s (0x%x, %u)\n", getrdname(rd), rd, rd); if (modes == 0) { /* NB: no HAL_MODE_HT */ modes = HAL_MODE_11A | HAL_MODE_11B | HAL_MODE_11G | HAL_MODE_TURBO | HAL_MODE_108G; } na = nb = ng = nt = ntg = nn = 0; if (modes & HAL_MODE_11G) { ahp.ah_currentRD = rd; if (ath_hal_getchannels(&ahp.h, gchans, IEEE80211_CHAN_MAX, &ng, HAL_MODE_11G, cc, rd, extendedChanMode) == HAL_OK) { calctxpower(&ahp.h, ng, gchans, tpcReduction, powerLimit, gtxpow); if (showdfs) isdfs |= anychan(gchans, ng, IEEE80211_CHAN_DFS); if (show4ms) is4ms |= anychan(gchans, ng, IEEE80211_CHAN_4MSXMIT); } } if (modes & HAL_MODE_11B) { ahp.ah_currentRD = rd; if (ath_hal_getchannels(&ahp.h, bchans, IEEE80211_CHAN_MAX, &nb, HAL_MODE_11B, cc, rd, extendedChanMode) == HAL_OK) { calctxpower(&ahp.h, nb, bchans, tpcReduction, powerLimit, btxpow); if (showdfs) isdfs |= anychan(bchans, nb, IEEE80211_CHAN_DFS); if (show4ms) is4ms |= anychan(bchans, nb, IEEE80211_CHAN_4MSXMIT); } } if (modes & HAL_MODE_11A) { ahp.ah_currentRD = rd; if (ath_hal_getchannels(&ahp.h, achans, IEEE80211_CHAN_MAX, &na, HAL_MODE_11A, cc, rd, extendedChanMode) == HAL_OK) { calctxpower(&ahp.h, na, achans, tpcReduction, powerLimit, atxpow); if (showdfs) isdfs |= anychan(achans, na, IEEE80211_CHAN_DFS); if (show4ms) is4ms |= anychan(achans, na, IEEE80211_CHAN_4MSXMIT); } } if (modes & HAL_MODE_TURBO) { ahp.ah_currentRD = rd; if (ath_hal_getchannels(&ahp.h, tchans, IEEE80211_CHAN_MAX, &nt, HAL_MODE_TURBO, cc, rd, extendedChanMode) == HAL_OK) { calctxpower(&ahp.h, nt, tchans, tpcReduction, powerLimit, ttxpow); if (showdfs) isdfs |= anychan(tchans, nt, IEEE80211_CHAN_DFS); if (show4ms) is4ms |= anychan(tchans, nt, IEEE80211_CHAN_4MSXMIT); } } if (modes & HAL_MODE_108G) { ahp.ah_currentRD = rd; if (ath_hal_getchannels(&ahp.h, tgchans, IEEE80211_CHAN_MAX, &ntg, HAL_MODE_108G, cc, rd, extendedChanMode) == HAL_OK) { calctxpower(&ahp.h, ntg, tgchans, tpcReduction, powerLimit, tgtxpow); if (showdfs) isdfs |= anychan(tgchans, ntg, IEEE80211_CHAN_DFS); if (show4ms) is4ms |= anychan(tgchans, ntg, IEEE80211_CHAN_4MSXMIT); } } if (modes & HAL_MODE_HT) { ahp.ah_currentRD = rd; if (ath_hal_getchannels(&ahp.h, nchans, IEEE80211_CHAN_MAX, &nn, modes & HAL_MODE_HT, cc, rd, extendedChanMode) == HAL_OK) { calctxpower(&ahp.h, nn, nchans, tpcReduction, powerLimit, ntxpow); if (showdfs) isdfs |= anychan(nchans, nn, IEEE80211_CHAN_DFS); if (show4ms) is4ms |= anychan(nchans, nn, IEEE80211_CHAN_4MSXMIT); } } if (!showall) { #define CHECKMODES(_modes, _m) ((_modes & (_m)) == (_m)) if (CHECKMODES(modes, HAL_MODE_11B|HAL_MODE_11G)) { /* b ^= g */ intersect(bchans, btxpow, &nb, gchans, gtxpow, ng); } if (CHECKMODES(modes, HAL_MODE_11A|HAL_MODE_TURBO)) { /* t ^= a */ intersect(tchans, ttxpow, &nt, achans, atxpow, na); } if (CHECKMODES(modes, HAL_MODE_11G|HAL_MODE_108G)) { /* tg ^= g */ intersect(tgchans, tgtxpow, &ntg, gchans, gtxpow, ng); } if (CHECKMODES(modes, HAL_MODE_11G|HAL_MODE_HT)) { /* g ^= n */ intersect(gchans, gtxpow, &ng, nchans, ntxpow, nn); } if (CHECKMODES(modes, HAL_MODE_11A|HAL_MODE_HT)) { /* a ^= n */ intersect(achans, atxpow, &na, nchans, ntxpow, nn); } #undef CHECKMODES } if (modes & HAL_MODE_11G) dumpchannels(&ahp.h, ng, gchans, gtxpow); if (modes & HAL_MODE_11B) dumpchannels(&ahp.h, nb, bchans, btxpow); if (modes & HAL_MODE_11A) dumpchannels(&ahp.h, na, achans, atxpow); if (modes & HAL_MODE_108G) dumpchannels(&ahp.h, ntg, tgchans, tgtxpow); if (modes & HAL_MODE_TURBO) dumpchannels(&ahp.h, nt, tchans, ttxpow); if (modes & HAL_MODE_HT) dumpchannels(&ahp.h, nn, nchans, ntxpow); printf("\n"); return (0); } /* * Search a list for a specified value v that is within * EEP_DELTA of the search values. Return the closest * values in the list above and below the desired value. * EEP_DELTA is a factional value; everything is scaled * so only integer arithmetic is used. * * NB: the input list is assumed to be sorted in ascending order */ static void ar5212GetLowerUpperValues(u_int16_t v, u_int16_t *lp, u_int16_t listSize, u_int16_t *vlo, u_int16_t *vhi) { u_int32_t target = v * EEP_SCALE; u_int16_t *ep = lp+listSize; /* * Check first and last elements for out-of-bounds conditions. */ if (target < (u_int32_t)(lp[0] * EEP_SCALE - EEP_DELTA)) { *vlo = *vhi = lp[0]; return; } if (target > (u_int32_t)(ep[-1] * EEP_SCALE + EEP_DELTA)) { *vlo = *vhi = ep[-1]; return; } /* look for value being near or between 2 values in list */ for (; lp < ep; lp++) { /* * If value is close to the current value of the list * then target is not between values, it is one of the values */ if (abs(lp[0] * EEP_SCALE - target) < EEP_DELTA) { *vlo = *vhi = lp[0]; return; } /* * Look for value being between current value and next value * if so return these 2 values */ if (target < (u_int32_t)(lp[1] * EEP_SCALE - EEP_DELTA)) { *vlo = lp[0]; *vhi = lp[1]; return; } } } /* * Find the maximum conformance test limit for the given channel and CTL info */ static u_int16_t ar5212GetMaxEdgePower(u_int16_t channel, RD_EDGES_POWER *pRdEdgesPower) { /* temp array for holding edge channels */ u_int16_t tempChannelList[NUM_EDGES]; u_int16_t clo, chi, twiceMaxEdgePower; int i, numEdges; /* Get the edge power */ for (i = 0; i < NUM_EDGES; i++) { if (pRdEdgesPower[i].rdEdge == 0) break; tempChannelList[i] = pRdEdgesPower[i].rdEdge; } numEdges = i; ar5212GetLowerUpperValues(channel, tempChannelList, numEdges, &clo, &chi); /* Get the index for the lower channel */ for (i = 0; i < numEdges && clo != tempChannelList[i]; i++) ; /* Is lower channel ever outside the rdEdge? */ HALASSERT(i != numEdges); if ((clo == chi && clo == channel) || (pRdEdgesPower[i].flag)) { /* * If there's an exact channel match or an inband flag set * on the lower channel use the given rdEdgePower */ twiceMaxEdgePower = pRdEdgesPower[i].twice_rdEdgePower; HALASSERT(twiceMaxEdgePower > 0); } else twiceMaxEdgePower = MAX_RATE_POWER; return twiceMaxEdgePower; } /* * Returns interpolated or the scaled up interpolated value */ static u_int16_t interpolate(u_int16_t target, u_int16_t srcLeft, u_int16_t srcRight, u_int16_t targetLeft, u_int16_t targetRight) { u_int16_t rv; int16_t lRatio; /* to get an accurate ratio, always scale, if want to scale, then don't scale back down */ if ((targetLeft * targetRight) == 0) return 0; if (srcRight != srcLeft) { /* * Note the ratio always need to be scaled, * since it will be a fraction. */ lRatio = (target - srcLeft) * EEP_SCALE / (srcRight - srcLeft); if (lRatio < 0) { /* Return as Left target if value would be negative */ rv = targetLeft; } else if (lRatio > EEP_SCALE) { /* Return as Right target if Ratio is greater than 100% (SCALE) */ rv = targetRight; } else { rv = (lRatio * targetRight + (EEP_SCALE - lRatio) * targetLeft) / EEP_SCALE; } } else { rv = targetLeft; } return rv; } /* * Return the four rates of target power for the given target power table * channel, and number of channels */ static void ar5212GetTargetPowers(struct ath_hal *ah, const struct ieee80211_channel *chan, TRGT_POWER_INFO *powInfo, u_int16_t numChannels, TRGT_POWER_INFO *pNewPower) { /* temp array for holding target power channels */ u_int16_t tempChannelList[NUM_TEST_FREQUENCIES]; u_int16_t clo, chi, ixlo, ixhi; int i; /* Copy the target powers into the temp channel list */ for (i = 0; i < numChannels; i++) tempChannelList[i] = powInfo[i].testChannel; ar5212GetLowerUpperValues(chan->ic_freq, tempChannelList, numChannels, &clo, &chi); /* Get the indices for the channel */ ixlo = ixhi = 0; for (i = 0; i < numChannels; i++) { if (clo == tempChannelList[i]) { ixlo = i; } if (chi == tempChannelList[i]) { ixhi = i; break; } } /* * Get the lower and upper channels, target powers, * and interpolate between them. */ pNewPower->twicePwr6_24 = interpolate(chan->ic_freq, clo, chi, powInfo[ixlo].twicePwr6_24, powInfo[ixhi].twicePwr6_24); pNewPower->twicePwr36 = interpolate(chan->ic_freq, clo, chi, powInfo[ixlo].twicePwr36, powInfo[ixhi].twicePwr36); pNewPower->twicePwr48 = interpolate(chan->ic_freq, clo, chi, powInfo[ixlo].twicePwr48, powInfo[ixhi].twicePwr48); pNewPower->twicePwr54 = interpolate(chan->ic_freq, clo, chi, powInfo[ixlo].twicePwr54, powInfo[ixhi].twicePwr54); } static RD_EDGES_POWER* findEdgePower(struct ath_hal *ah, u_int ctl) { int i; for (i = 0; i < _numCtls; i++) if (_ctl[i] == ctl) return &_rdEdgesPower[i * NUM_EDGES]; return AH_NULL; } /* * Sets the transmit power in the baseband for the given * operating channel and mode. */ static HAL_BOOL setRateTable(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t tpcScaleReduction, int16_t powerLimit, int16_t *pMinPower, int16_t *pMaxPower) { u_int16_t ratesArray[16]; u_int16_t *rpow = ratesArray; u_int16_t twiceMaxRDPower, twiceMaxEdgePower, twiceMaxEdgePowerCck; int8_t twiceAntennaGain, twiceAntennaReduction; TRGT_POWER_INFO targetPowerOfdm, targetPowerCck; RD_EDGES_POWER *rep; int16_t scaledPower; u_int8_t cfgCtl; twiceMaxRDPower = chan->ic_maxregpower * 2; *pMaxPower = -MAX_RATE_POWER; *pMinPower = MAX_RATE_POWER; /* Get conformance test limit maximum for this channel */ cfgCtl = ath_hal_getctl(ah, chan); rep = findEdgePower(ah, cfgCtl); if (rep != AH_NULL) twiceMaxEdgePower = ar5212GetMaxEdgePower(chan->ic_freq, rep); else twiceMaxEdgePower = MAX_RATE_POWER; if (IEEE80211_IS_CHAN_G(chan)) { /* Check for a CCK CTL for 11G CCK powers */ cfgCtl = (cfgCtl & 0xFC) | 0x01; rep = findEdgePower(ah, cfgCtl); if (rep != AH_NULL) twiceMaxEdgePowerCck = ar5212GetMaxEdgePower(chan->ic_freq, rep); else twiceMaxEdgePowerCck = MAX_RATE_POWER; } else { /* Set the 11B cck edge power to the one found before */ twiceMaxEdgePowerCck = twiceMaxEdgePower; } /* Get Antenna Gain reduction */ if (IEEE80211_IS_CHAN_5GHZ(chan)) { twiceAntennaGain = antennaGainMax[0]; } else { twiceAntennaGain = antennaGainMax[1]; } twiceAntennaReduction = ath_hal_getantennareduction(ah, chan, twiceAntennaGain); if (IEEE80211_IS_CHAN_OFDM(chan)) { /* Get final OFDM target powers */ if (IEEE80211_IS_CHAN_G(chan)) { /* TODO - add Turbo 2.4 to this mode check */ ar5212GetTargetPowers(ah, chan, trgtPwr_11g, numTargetPwr_11g, &targetPowerOfdm); } else { ar5212GetTargetPowers(ah, chan, trgtPwr_11a, numTargetPwr_11a, &targetPowerOfdm); } /* Get Maximum OFDM power */ /* Minimum of target and edge powers */ scaledPower = AH_MIN(twiceMaxEdgePower, twiceMaxRDPower - twiceAntennaReduction); /* * If turbo is set, reduce power to keep power * consumption under 2 Watts. Note that we always do * this unless specially configured. Then we limit * power only for non-AP operation. */ if (IEEE80211_IS_CHAN_TURBO(chan) #ifdef AH_ENABLE_AP_SUPPORT && AH_PRIVATE(ah)->ah_opmode != HAL_M_HOSTAP #endif ) { /* * If turbo is set, reduce power to keep power * consumption under 2 Watts */ if (eeversion >= AR_EEPROM_VER3_1) scaledPower = AH_MIN(scaledPower, turbo2WMaxPower5); /* * EEPROM version 4.0 added an additional * constraint on 2.4GHz channels. */ if (eeversion >= AR_EEPROM_VER4_0 && IEEE80211_IS_CHAN_2GHZ(chan)) scaledPower = AH_MIN(scaledPower, turbo2WMaxPower2); } /* Reduce power by max regulatory domain allowed restrictions */ scaledPower -= (tpcScaleReduction * 2); scaledPower = (scaledPower < 0) ? 0 : scaledPower; scaledPower = AH_MIN(scaledPower, powerLimit); scaledPower = AH_MIN(scaledPower, targetPowerOfdm.twicePwr6_24); /* Set OFDM rates 9, 12, 18, 24, 36, 48, 54, XR */ rpow[0] = rpow[1] = rpow[2] = rpow[3] = rpow[4] = scaledPower; rpow[5] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr36); rpow[6] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr48); rpow[7] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr54); #ifdef notyet if (eeversion >= AR_EEPROM_VER4_0) { /* Setup XR target power from EEPROM */ rpow[15] = AH_MIN(scaledPower, IS_CHAN_2GHZ(chan) ? xrTargetPower2 : xrTargetPower5); } else { /* XR uses 6mb power */ rpow[15] = rpow[0]; } #else rpow[15] = rpow[0]; #endif *pMinPower = rpow[7]; *pMaxPower = rpow[0]; #if 0 ahp->ah_ofdmTxPower = rpow[0]; #endif HALDEBUG(ah, HAL_DEBUG_ANY, "%s: MaxRD: %d TurboMax: %d MaxCTL: %d " "TPC_Reduction %d\n", __func__, twiceMaxRDPower, turbo2WMaxPower5, twiceMaxEdgePower, tpcScaleReduction * 2); } if (IEEE80211_IS_CHAN_CCK(chan)) { /* Get final CCK target powers */ ar5212GetTargetPowers(ah, chan, trgtPwr_11b, numTargetPwr_11b, &targetPowerCck); /* Reduce power by max regulatory domain allowed restrictions */ scaledPower = AH_MIN(twiceMaxEdgePowerCck, twiceMaxRDPower - twiceAntennaReduction); scaledPower -= (tpcScaleReduction * 2); scaledPower = (scaledPower < 0) ? 0 : scaledPower; scaledPower = AH_MIN(scaledPower, powerLimit); rpow[8] = (scaledPower < 1) ? 1 : scaledPower; /* Set CCK rates 2L, 2S, 5.5L, 5.5S, 11L, 11S */ rpow[8] = AH_MIN(scaledPower, targetPowerCck.twicePwr6_24); rpow[9] = AH_MIN(scaledPower, targetPowerCck.twicePwr36); rpow[10] = rpow[9]; rpow[11] = AH_MIN(scaledPower, targetPowerCck.twicePwr48); rpow[12] = rpow[11]; rpow[13] = AH_MIN(scaledPower, targetPowerCck.twicePwr54); rpow[14] = rpow[13]; /* Set min/max power based off OFDM values or initialization */ if (rpow[13] < *pMinPower) *pMinPower = rpow[13]; if (rpow[9] > *pMaxPower) *pMaxPower = rpow[9]; } #if 0 ahp->ah_tx6PowerInHalfDbm = *pMaxPower; #endif return AH_TRUE; } void* ath_hal_malloc(size_t size) { return calloc(1, size); } void ath_hal_free(void* p) { return free(p); } void ath_hal_vprintf(struct ath_hal *ah, const char* fmt, va_list ap) { vprintf(fmt, ap); } void ath_hal_printf(struct ath_hal *ah, const char* fmt, ...) { va_list ap; va_start(ap, fmt); ath_hal_vprintf(ah, fmt, ap); va_end(ap); } void HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...) { if (ath_hal_debug & mask) { __va_list ap; va_start(ap, fmt); ath_hal_vprintf(ah, fmt, ap); va_end(ap); } }