summaryrefslogtreecommitdiffstats
path: root/ah.c
diff options
context:
space:
mode:
Diffstat (limited to 'ah.c')
-rw-r--r--ah.c1029
1 files changed, 1029 insertions, 0 deletions
diff --git a/ah.c b/ah.c
new file mode 100644
index 0000000..c531b78d
--- /dev/null
+++ b/ah.c
@@ -0,0 +1,1029 @@
+/*
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ah.c,v 1.13 2008/11/10 04:08:00 sam Exp $
+ */
+#include "opt_ah.h"
+
+#include "ah.h"
+#include "ah_internal.h"
+#include "ah_devid.h"
+
+#ifdef AH_SUPPORT_AR5210
+extern struct ath_hal *ar5210Attach(uint16_t, HAL_SOFTC,
+ HAL_BUS_TAG, HAL_BUS_HANDLE, HAL_STATUS*);
+#endif
+#ifdef AH_SUPPORT_AR5211
+extern struct ath_hal *ar5211Attach(uint16_t, HAL_SOFTC,
+ HAL_BUS_TAG, HAL_BUS_HANDLE, HAL_STATUS*);
+#endif
+#ifdef AH_SUPPORT_AR5212
+extern struct ath_hal *ar5212Attach(uint16_t, HAL_SOFTC,
+ HAL_BUS_TAG, HAL_BUS_HANDLE, HAL_STATUS*);
+#endif
+#ifdef AH_SUPPORT_AR5312
+extern struct ath_hal *ar5312Attach(uint16_t, HAL_SOFTC,
+ HAL_BUS_TAG, HAL_BUS_HANDLE, HAL_STATUS*);
+#endif
+#ifdef AH_SUPPORT_AR5416
+extern struct ath_hal *ar5416Attach(uint16_t, HAL_SOFTC,
+ HAL_BUS_TAG, HAL_BUS_HANDLE, HAL_STATUS*);
+#endif
+#ifdef AH_SUPPORT_AR9160
+extern struct ath_hal *ar9160Attach(uint16_t, HAL_SOFTC,
+ HAL_BUS_TAG, HAL_BUS_HANDLE, HAL_STATUS*);
+#endif
+
+#include "version.h"
+char ath_hal_version[] = ATH_HAL_VERSION;
+const char* ath_hal_buildopts[] = {
+#ifdef AH_SUPPORT_AR5210
+ "AR5210",
+#endif
+#ifdef AH_SUPPORT_AR5211
+ "AR5211",
+#endif
+#ifdef AH_SUPPORT_AR5212
+ "AR5212",
+#endif
+#ifdef AH_SUPPORT_AR5312
+ "AR5312",
+#endif
+#ifdef AH_SUPPORT_AR5416
+ "AR5416",
+#endif
+#ifdef AH_SUPPORT_AR9180
+ "AR9180",
+#endif
+#ifdef AH_SUPPORT_5111
+ "RF5111",
+#endif
+#ifdef AH_SUPPORT_5112
+ "RF5112",
+#endif
+#ifdef AH_SUPPORT_2413
+ "RF2413",
+#endif
+#ifdef AH_SUPPORT_5413
+ "RF5413",
+#endif
+#ifdef AH_SUPPORT_2316
+ "RF2316",
+#endif
+#ifdef AH_SUPPORT_2317
+ "RF2317",
+#endif
+#ifdef AH_SUPPORT_2133
+ "RF2133",
+#endif
+#ifdef AH_SUPPORT_2425
+ "RF2425",
+#endif
+#ifdef AH_SUPPORT_2417
+ "RF2417",
+#endif
+#ifdef AH_DEBUG
+ "DEBUG",
+#endif
+#ifdef AH_ASSERT
+ "ASSERT",
+#endif
+#ifdef AH_DEBUG_ALQ
+ "DEBUG_ALQ",
+#endif
+#ifdef AH_REGOPS_FUNC
+ "REGOPS_FUNC",
+#endif
+#ifdef AH_PRIVATE_DIAG
+ "PRIVATE_DIAG",
+#endif
+#ifdef AH_DEBUG_COUNTRY
+ "DEBUG_COUNTRY",
+#endif
+#ifdef AH_NEED_DESC_SWAP
+ "TX_DESC_SWAP",
+#endif
+#ifdef AH_DISABLE_WME
+ "DISABLE_WME",
+#endif
+#ifdef AH_SUPPORT_11D
+ "11D",
+#endif
+ AH_NULL
+};
+
+static const char*
+ath_hal_devname(uint16_t devid)
+{
+ switch (devid) {
+ case AR5210_PROD:
+ case AR5210_DEFAULT:
+ return "Atheros 5210";
+
+ case AR5211_DEVID:
+ case AR5311_DEVID:
+ case AR5211_DEFAULT:
+ return "Atheros 5211";
+ case AR5211_FPGA11B:
+ return "Atheros 5211 (FPGA)";
+
+ case AR5212_FPGA:
+ return "Atheros 5212 (FPGA)";
+ case AR5212_AR5312_REV2:
+ case AR5212_AR5312_REV7:
+ return "Atheros 5312 WiSoC";
+ case AR5212_AR2315_REV6:
+ case AR5212_AR2315_REV7:
+ return "Atheros 2315 WiSoC";
+ case AR5212_AR2317_REV1:
+ return "Atheros 2317 WiSoC";
+ case AR5212_AR2313_REV8:
+ return "Atheros 2313 WiSoC";
+ case AR5212_DEVID:
+ case AR5212_DEVID_IBM:
+ case AR5212_DEFAULT:
+ return "Atheros 5212";
+ case AR5212_AR2413:
+ return "Atheros 2413";
+ case AR5212_AR2417:
+ return "Atheros 2417";
+ case AR5212_AR5413:
+ return "Atheros 5413";
+ case AR5212_AR5424:
+ return "Atheros 5424/2424";
+ case AR5416_DEVID_PCI:
+ case AR5416_DEVID_PCIE:
+ return "Atheros 5416";
+ case AR9160_DEVID_PCI:
+ return "Atheros 9160";
+ }
+ return AH_NULL;
+}
+
+const char*
+ath_hal_probe(uint16_t vendorid, uint16_t devid)
+{
+ return (vendorid == ATHEROS_VENDOR_ID ||
+ vendorid == ATHEROS_3COM_VENDOR_ID ||
+ vendorid == ATHEROS_3COM2_VENDOR_ID ?
+ ath_hal_devname(devid) : 0);
+}
+
+/*
+ * Attach detects device chip revisions, initializes the hwLayer
+ * function list, reads EEPROM information,
+ * selects reset vectors, and performs a short self test.
+ * Any failures will return an error that should cause a hardware
+ * disable.
+ */
+struct ath_hal*
+ath_hal_attach(uint16_t devid, HAL_SOFTC sc,
+ HAL_BUS_TAG st, HAL_BUS_HANDLE sh, HAL_STATUS *error)
+{
+ struct ath_hal *ah=AH_NULL;
+
+ switch (devid) {
+#ifdef AH_SUPPORT_AR5210
+ case AR5210_AP:
+ case AR5210_PROD:
+ case AR5210_DEFAULT:
+ ah = ar5210Attach(devid, sc, st, sh, error);
+ break;
+#endif
+#ifdef AH_SUPPORT_AR5211
+ case AR5211_DEVID:
+ case AR5311_DEVID:
+ case AR5211_FPGA11B:
+ case AR5211_DEFAULT:
+ ah = ar5211Attach(devid, sc, st, sh, error);
+ break;
+#endif
+#ifdef AH_SUPPORT_AR5212
+ case AR5212_DEVID_IBM:
+ case AR5212_AR2413:
+ case AR5212_AR2417:
+ case AR5212_AR5413:
+ case AR5212_AR5424:
+ case AR5212_DEVID_FF19: /* XXX PCI Express extra */
+ devid = AR5212_DEVID;
+ /* fall thru... */
+ case AR5212_DEVID:
+ case AR5212_FPGA:
+ case AR5212_DEFAULT:
+ ah = ar5212Attach(devid, sc, st, sh, error);
+ break;
+#endif
+#ifdef AH_SUPPORT_AR5312
+ case AR5212_AR5312_REV2:
+ case AR5212_AR5312_REV7:
+ case AR5212_AR2313_REV8:
+ case AR5212_AR2315_REV6:
+ case AR5212_AR2315_REV7:
+ case AR5212_AR2317_REV1:
+ ah = ar5312Attach(devid, sc, st, sh, error);
+ break;
+#endif
+#ifdef AH_SUPPORT_AR5416
+ case AR5416_DEVID_PCI:
+ case AR5416_DEVID_PCIE:
+ ah = ar5416Attach(devid, sc, st, sh, error);
+ break;
+#endif
+#ifdef AH_SUPPORT_AR9160
+ case AR9160_DEVID_PCI:
+ ah = ar9160Attach(devid, sc, st, sh, error);
+ break;
+#endif
+ default:
+ ah = AH_NULL;
+ *error = HAL_ENXIO;
+ break;
+ }
+ if (ah != AH_NULL) {
+ /* copy back private state to public area */
+ ah->ah_devid = AH_PRIVATE(ah)->ah_devid;
+ ah->ah_subvendorid = AH_PRIVATE(ah)->ah_subvendorid;
+ ah->ah_macVersion = AH_PRIVATE(ah)->ah_macVersion;
+ ah->ah_macRev = AH_PRIVATE(ah)->ah_macRev;
+ ah->ah_phyRev = AH_PRIVATE(ah)->ah_phyRev;
+ ah->ah_analog5GhzRev = AH_PRIVATE(ah)->ah_analog5GhzRev;
+ ah->ah_analog2GhzRev = AH_PRIVATE(ah)->ah_analog2GhzRev;
+ }
+ return ah;
+}
+
+/*
+ * Poll the register looking for a specific value.
+ */
+HAL_BOOL
+ath_hal_wait(struct ath_hal *ah, u_int reg, uint32_t mask, uint32_t val)
+{
+#define AH_TIMEOUT 1000
+ int i;
+
+ for (i = 0; i < AH_TIMEOUT; i++) {
+ if ((OS_REG_READ(ah, reg) & mask) == val)
+ return AH_TRUE;
+ OS_DELAY(10);
+ }
+ HALDEBUG(ah, HAL_DEBUG_REGIO | HAL_DEBUG_PHYIO,
+ "%s: timeout on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n",
+ __func__, reg, OS_REG_READ(ah, reg), mask, val);
+ return AH_FALSE;
+#undef AH_TIMEOUT
+}
+
+/*
+ * Reverse the bits starting at the low bit for a value of
+ * bit_count in size
+ */
+uint32_t
+ath_hal_reverseBits(uint32_t val, uint32_t n)
+{
+ uint32_t retval;
+ int i;
+
+ for (i = 0, retval = 0; i < n; i++) {
+ retval = (retval << 1) | (val & 1);
+ val >>= 1;
+ }
+ return retval;
+}
+
+/*
+ * Compute the time to transmit a frame of length frameLen bytes
+ * using the specified rate, phy, and short preamble setting.
+ */
+uint16_t
+ath_hal_computetxtime(struct ath_hal *ah,
+ const HAL_RATE_TABLE *rates, uint32_t frameLen, uint16_t rateix,
+ HAL_BOOL shortPreamble)
+{
+ uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
+ uint32_t kbps;
+
+ kbps = rates->info[rateix].rateKbps;
+ /*
+ * index can be invalid duting dynamic Turbo transitions.
+ */
+ if(kbps == 0) return 0;
+ switch (rates->info[rateix].phy) {
+
+ case IEEE80211_T_CCK:
+#define CCK_SIFS_TIME 10
+#define CCK_PREAMBLE_BITS 144
+#define CCK_PLCP_BITS 48
+ phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
+ if (shortPreamble && rates->info[rateix].shortPreamble)
+ phyTime >>= 1;
+ numBits = frameLen << 3;
+ txTime = CCK_SIFS_TIME + phyTime
+ + ((numBits * 1000)/kbps);
+ break;
+#undef CCK_SIFS_TIME
+#undef CCK_PREAMBLE_BITS
+#undef CCK_PLCP_BITS
+
+ case IEEE80211_T_OFDM:
+#define OFDM_SIFS_TIME 16
+#define OFDM_PREAMBLE_TIME 20
+#define OFDM_PLCP_BITS 22
+#define OFDM_SYMBOL_TIME 4
+
+#define OFDM_SIFS_TIME_HALF 32
+#define OFDM_PREAMBLE_TIME_HALF 40
+#define OFDM_PLCP_BITS_HALF 22
+#define OFDM_SYMBOL_TIME_HALF 8
+
+#define OFDM_SIFS_TIME_QUARTER 64
+#define OFDM_PREAMBLE_TIME_QUARTER 80
+#define OFDM_PLCP_BITS_QUARTER 22
+#define OFDM_SYMBOL_TIME_QUARTER 16
+
+ if (AH_PRIVATE(ah)->ah_curchan &&
+ IS_CHAN_QUARTER_RATE(AH_PRIVATE(ah)->ah_curchan)) {
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000;
+ HALASSERT(bitsPerSymbol != 0);
+
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME_QUARTER
+ + OFDM_PREAMBLE_TIME_QUARTER
+ + (numSymbols * OFDM_SYMBOL_TIME_QUARTER);
+ } else if (AH_PRIVATE(ah)->ah_curchan &&
+ IS_CHAN_HALF_RATE(AH_PRIVATE(ah)->ah_curchan)) {
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_HALF) / 1000;
+ HALASSERT(bitsPerSymbol != 0);
+
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME_HALF +
+ OFDM_PREAMBLE_TIME_HALF
+ + (numSymbols * OFDM_SYMBOL_TIME_HALF);
+ } else { /* full rate channel */
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000;
+ HALASSERT(bitsPerSymbol != 0);
+
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME + OFDM_PREAMBLE_TIME
+ + (numSymbols * OFDM_SYMBOL_TIME);
+ }
+ break;
+
+#undef OFDM_SIFS_TIME
+#undef OFDM_PREAMBLE_TIME
+#undef OFDM_PLCP_BITS
+#undef OFDM_SYMBOL_TIME
+
+ case IEEE80211_T_TURBO:
+#define TURBO_SIFS_TIME 8
+#define TURBO_PREAMBLE_TIME 14
+#define TURBO_PLCP_BITS 22
+#define TURBO_SYMBOL_TIME 4
+ /* we still save OFDM rates in kbps - so double them */
+ bitsPerSymbol = ((kbps << 1) * TURBO_SYMBOL_TIME) / 1000;
+ HALASSERT(bitsPerSymbol != 0);
+
+ numBits = TURBO_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME
+ + (numSymbols * TURBO_SYMBOL_TIME);
+ break;
+#undef TURBO_SIFS_TIME
+#undef TURBO_PREAMBLE_TIME
+#undef TURBO_PLCP_BITS
+#undef TURBO_SYMBOL_TIME
+
+ default:
+ HALDEBUG(ah, HAL_DEBUG_PHYIO,
+ "%s: unknown phy %u (rate ix %u)\n",
+ __func__, rates->info[rateix].phy, rateix);
+ txTime = 0;
+ break;
+ }
+ return txTime;
+}
+
+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 & CHANNEL_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 & CHANNEL_5GHZ) {/* 5Ghz band */
+ if (ath_hal_ispublicsafetysku(ah) &&
+ IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) {
+ return mappsb(freq, flags);
+ } else if ((flags & CHANNEL_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 (ath_hal_ispublicsafetysku(ah) &&
+ 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;
+ }
+}
+
+typedef enum {
+ WIRELESS_MODE_11a = 0,
+ WIRELESS_MODE_TURBO = 1,
+ WIRELESS_MODE_11b = 2,
+ WIRELESS_MODE_11g = 3,
+ WIRELESS_MODE_108g = 4,
+
+ WIRELESS_MODE_MAX
+} WIRELESS_MODE;
+
+static WIRELESS_MODE
+ath_hal_chan2wmode(struct ath_hal *ah, const HAL_CHANNEL *chan)
+{
+ if (IS_CHAN_CCK(chan))
+ return WIRELESS_MODE_11b;
+ if (IS_CHAN_G(chan))
+ return WIRELESS_MODE_11g;
+ if (IS_CHAN_108G(chan))
+ return WIRELESS_MODE_108g;
+ if (IS_CHAN_TURBO(chan))
+ return WIRELESS_MODE_TURBO;
+ return WIRELESS_MODE_11a;
+}
+
+/*
+ * Convert between microseconds and core system clocks.
+ */
+ /* 11a Turbo 11b 11g 108g */
+static const uint8_t CLOCK_RATE[] = { 40, 80, 22, 44, 88 };
+
+u_int
+ath_hal_mac_clks(struct ath_hal *ah, u_int usecs)
+{
+ const HAL_CHANNEL *c = (const HAL_CHANNEL *) AH_PRIVATE(ah)->ah_curchan;
+ u_int clks;
+
+ /* NB: ah_curchan may be null when called attach time */
+ if (c != AH_NULL) {
+ clks = usecs * CLOCK_RATE[ath_hal_chan2wmode(ah, c)];
+ if (IS_CHAN_HT40(c))
+ clks <<= 1;
+ else if (IS_CHAN_HALF_RATE(c))
+ clks >>= 1;
+ else if (IS_CHAN_QUARTER_RATE(c))
+ clks >>= 2;
+ } else
+ clks = usecs * CLOCK_RATE[WIRELESS_MODE_11b];
+ return clks;
+}
+
+u_int
+ath_hal_mac_usec(struct ath_hal *ah, u_int clks)
+{
+ const HAL_CHANNEL *c = (const HAL_CHANNEL *) AH_PRIVATE(ah)->ah_curchan;
+ u_int usec;
+
+ /* NB: ah_curchan may be null when called attach time */
+ if (c != AH_NULL) {
+ usec = clks / CLOCK_RATE[ath_hal_chan2wmode(ah, c)];
+ if (IS_CHAN_HT40(c))
+ usec >>= 1;
+ else if (IS_CHAN_HALF_RATE(c))
+ usec <<= 1;
+ else if (IS_CHAN_QUARTER_RATE(c))
+ usec <<= 2;
+ } else
+ usec = clks / CLOCK_RATE[WIRELESS_MODE_11b];
+ return usec;
+}
+
+/*
+ * Setup a h/w rate table's reverse lookup table and
+ * fill in ack durations. This routine is called for
+ * each rate table returned through the ah_getRateTable
+ * method. The reverse lookup tables are assumed to be
+ * initialized to zero (or at least the first entry).
+ * We use this as a key that indicates whether or not
+ * we've previously setup the reverse lookup table.
+ *
+ * XXX not reentrant, but shouldn't matter
+ */
+void
+ath_hal_setupratetable(struct ath_hal *ah, HAL_RATE_TABLE *rt)
+{
+#define N(a) (sizeof(a)/sizeof(a[0]))
+ int i;
+
+ if (rt->rateCodeToIndex[0] != 0) /* already setup */
+ return;
+ for (i = 0; i < N(rt->rateCodeToIndex); i++)
+ rt->rateCodeToIndex[i] = (uint8_t) -1;
+ for (i = 0; i < rt->rateCount; i++) {
+ uint8_t code = rt->info[i].rateCode;
+ uint8_t cix = rt->info[i].controlRate;
+
+ HALASSERT(code < N(rt->rateCodeToIndex));
+ rt->rateCodeToIndex[code] = i;
+ HALASSERT((code | rt->info[i].shortPreamble) <
+ N(rt->rateCodeToIndex));
+ rt->rateCodeToIndex[code | rt->info[i].shortPreamble] = i;
+ /*
+ * XXX for 11g the control rate to use for 5.5 and 11 Mb/s
+ * depends on whether they are marked as basic rates;
+ * the static tables are setup with an 11b-compatible
+ * 2Mb/s rate which will work but is suboptimal
+ */
+ rt->info[i].lpAckDuration = ath_hal_computetxtime(ah, rt,
+ WLAN_CTRL_FRAME_SIZE, cix, AH_FALSE);
+ rt->info[i].spAckDuration = ath_hal_computetxtime(ah, rt,
+ WLAN_CTRL_FRAME_SIZE, cix, AH_TRUE);
+ }
+#undef N
+}
+
+HAL_STATUS
+ath_hal_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;
+ case HAL_CAP_CIPHER: /* cipher handled in hardware */
+ case HAL_CAP_TKIP_MIC: /* handle TKIP MIC in hardware */
+ return HAL_ENOTSUPP;
+ case HAL_CAP_TKIP_SPLIT: /* hardware TKIP uses split keys */
+ return HAL_ENOTSUPP;
+ case HAL_CAP_PHYCOUNTERS: /* hardware PHY error counters */
+ return pCap->halHwPhyCounterSupport ? HAL_OK : HAL_ENXIO;
+ case HAL_CAP_WME_TKIPMIC: /* hardware can do TKIP MIC when WMM is turned on */
+ return HAL_ENOTSUPP;
+ case HAL_CAP_DIVERSITY: /* hardware supports fast diversity */
+ return HAL_ENOTSUPP;
+ case HAL_CAP_KEYCACHE_SIZE: /* hardware key cache size */
+ *result = pCap->halKeyCacheSize;
+ return HAL_OK;
+ case HAL_CAP_NUM_TXQUEUES: /* number of hardware tx queues */
+ *result = pCap->halTotalQueues;
+ return HAL_OK;
+ case HAL_CAP_VEOL: /* hardware supports virtual EOL */
+ return pCap->halVEOLSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_PSPOLL: /* hardware PS-Poll support works */
+ return pCap->halPSPollBroken ? HAL_ENOTSUPP : HAL_OK;
+ case HAL_CAP_COMPRESSION:
+ return pCap->halCompressSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_BURST:
+ return pCap->halBurstSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_FASTFRAME:
+ return pCap->halFastFramesSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_DIAG: /* hardware diagnostic support */
+ *result = AH_PRIVATE(ah)->ah_diagreg;
+ return HAL_OK;
+ case HAL_CAP_TXPOW: /* global tx power limit */
+ switch (capability) {
+ case 0: /* facility is supported */
+ return HAL_OK;
+ case 1: /* current limit */
+ *result = AH_PRIVATE(ah)->ah_powerLimit;
+ return HAL_OK;
+ case 2: /* current max tx power */
+ *result = AH_PRIVATE(ah)->ah_maxPowerLevel;
+ return HAL_OK;
+ case 3: /* scale factor */
+ *result = AH_PRIVATE(ah)->ah_tpScale;
+ return HAL_OK;
+ }
+ return HAL_ENOTSUPP;
+ case HAL_CAP_BSSIDMASK: /* hardware supports bssid mask */
+ return pCap->halBssIdMaskSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_MCAST_KEYSRCH: /* multicast frame keycache search */
+ return pCap->halMcastKeySrchSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_TSF_ADJUST: /* hardware has beacon tsf adjust */
+ return HAL_ENOTSUPP;
+ case HAL_CAP_CHAN_HALFRATE:
+ return pCap->halChanHalfRate ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_CHAN_QUARTERRATE:
+ return pCap->halChanQuarterRate ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_RFSILENT: /* rfsilent support */
+ switch (capability) {
+ case 0: /* facility is supported */
+ return pCap->halRfSilentSupport ? HAL_OK : HAL_ENOTSUPP;
+ case 1: /* current setting */
+ return AH_PRIVATE(ah)->ah_rfkillEnabled ?
+ HAL_OK : HAL_ENOTSUPP;
+ case 2: /* rfsilent config */
+ *result = AH_PRIVATE(ah)->ah_rfsilent;
+ return HAL_OK;
+ }
+ return HAL_ENOTSUPP;
+ case HAL_CAP_11D:
+#ifdef AH_SUPPORT_11D
+ return HAL_OK;
+#else
+ return HAL_ENOTSUPP;
+#endif
+ case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */
+ return AH_PRIVATE(ah)->ah_rxornIsFatal ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_HT:
+ return pCap->halHTSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_TX_CHAINMASK: /* mask of TX chains supported */
+ *result = pCap->halTxChainMask;
+ return HAL_OK;
+ case HAL_CAP_RX_CHAINMASK: /* mask of RX chains supported */
+ *result = pCap->halRxChainMask;
+ return HAL_OK;
+ case HAL_CAP_RXTSTAMP_PREC: /* rx desc tstamp precision (bits) */
+ *result = pCap->halTstampPrecision;
+ return HAL_OK;
+ default:
+ return HAL_EINVAL;
+ }
+}
+
+HAL_BOOL
+ath_hal_setcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
+ uint32_t capability, uint32_t setting, HAL_STATUS *status)
+{
+
+ switch (type) {
+ case HAL_CAP_TXPOW:
+ switch (capability) {
+ case 3:
+ if (setting <= HAL_TP_SCALE_MIN) {
+ AH_PRIVATE(ah)->ah_tpScale = setting;
+ return AH_TRUE;
+ }
+ break;
+ }
+ break;
+ case HAL_CAP_RFSILENT: /* rfsilent support */
+ /*
+ * NB: allow even if halRfSilentSupport is false
+ * in case the EEPROM is misprogrammed.
+ */
+ switch (capability) {
+ case 1: /* current setting */
+ AH_PRIVATE(ah)->ah_rfkillEnabled = (setting != 0);
+ return AH_TRUE;
+ case 2: /* rfsilent config */
+ /* XXX better done per-chip for validation? */
+ AH_PRIVATE(ah)->ah_rfsilent = setting;
+ return AH_TRUE;
+ }
+ break;
+ case HAL_CAP_REG_DMN: /* regulatory domain */
+ AH_PRIVATE(ah)->ah_currentRD = setting;
+ return AH_TRUE;
+ case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */
+ AH_PRIVATE(ah)->ah_rxornIsFatal = setting;
+ return AH_TRUE;
+ default:
+ break;
+ }
+ if (status)
+ *status = HAL_EINVAL;
+ return AH_FALSE;
+}
+
+/*
+ * Common support for getDiagState method.
+ */
+
+static u_int
+ath_hal_getregdump(struct ath_hal *ah, const HAL_REGRANGE *regs,
+ void *dstbuf, int space)
+{
+ uint32_t *dp = dstbuf;
+ int i;
+
+ for (i = 0; space >= 2*sizeof(uint32_t); i++) {
+ u_int r = regs[i].start;
+ u_int e = regs[i].end;
+ *dp++ = (r<<16) | e;
+ space -= sizeof(uint32_t);
+ do {
+ *dp++ = OS_REG_READ(ah, r);
+ r += sizeof(uint32_t);
+ space -= sizeof(uint32_t);
+ } while (r <= e && space >= sizeof(uint32_t));
+ }
+ return (char *) dp - (char *) dstbuf;
+}
+
+HAL_BOOL
+ath_hal_getdiagstate(struct ath_hal *ah, int request,
+ const void *args, uint32_t argsize,
+ void **result, uint32_t *resultsize)
+{
+ switch (request) {
+ case HAL_DIAG_REVS:
+ *result = &AH_PRIVATE(ah)->ah_devid;
+ *resultsize = sizeof(HAL_REVS);
+ return AH_TRUE;
+ case HAL_DIAG_REGS:
+ *resultsize = ath_hal_getregdump(ah, args, *result,*resultsize);
+ return AH_TRUE;
+ case HAL_DIAG_FATALERR:
+ *result = &AH_PRIVATE(ah)->ah_fatalState[0];
+ *resultsize = sizeof(AH_PRIVATE(ah)->ah_fatalState);
+ return AH_TRUE;
+ case HAL_DIAG_EEREAD:
+ if (argsize != sizeof(uint16_t))
+ return AH_FALSE;
+ if (!ath_hal_eepromRead(ah, *(const uint16_t *)args, *result))
+ return AH_FALSE;
+ *resultsize = sizeof(uint16_t);
+ return AH_TRUE;
+#ifdef AH_PRIVATE_DIAG
+ case HAL_DIAG_SETKEY: {
+ const HAL_DIAG_KEYVAL *dk;
+
+ if (argsize != sizeof(HAL_DIAG_KEYVAL))
+ return AH_FALSE;
+ dk = (const HAL_DIAG_KEYVAL *)args;
+ return ah->ah_setKeyCacheEntry(ah, dk->dk_keyix,
+ &dk->dk_keyval, dk->dk_mac, dk->dk_xor);
+ }
+ case HAL_DIAG_RESETKEY:
+ if (argsize != sizeof(uint16_t))
+ return AH_FALSE;
+ return ah->ah_resetKeyCacheEntry(ah, *(const uint16_t *)args);
+#endif /* AH_PRIVATE_DIAG */
+ case HAL_DIAG_11NCOMPAT:
+ if (argsize == 0) {
+ *resultsize = sizeof(uint32_t);
+ *((uint32_t *)(*result)) =
+ AH_PRIVATE(ah)->ah_11nCompat;
+ } else if (argsize == sizeof(uint32_t)) {
+ AH_PRIVATE(ah)->ah_11nCompat = *(const uint32_t *)args;
+ } else
+ return AH_FALSE;
+ return AH_TRUE;
+ }
+ return AH_FALSE;
+}
+
+/*
+ * Set the properties of the tx queue with the parameters
+ * from qInfo.
+ */
+HAL_BOOL
+ath_hal_setTxQProps(struct ath_hal *ah,
+ HAL_TX_QUEUE_INFO *qi, const HAL_TXQ_INFO *qInfo)
+{
+ uint32_t cw;
+
+ if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) {
+ HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
+ "%s: inactive queue\n", __func__);
+ return AH_FALSE;
+ }
+ /* XXX validate parameters */
+ qi->tqi_ver = qInfo->tqi_ver;
+ qi->tqi_subtype = qInfo->tqi_subtype;
+ qi->tqi_qflags = qInfo->tqi_qflags;
+ qi->tqi_priority = qInfo->tqi_priority;
+ if (qInfo->tqi_aifs != HAL_TXQ_USEDEFAULT)
+ qi->tqi_aifs = AH_MIN(qInfo->tqi_aifs, 255);
+ else
+ qi->tqi_aifs = INIT_AIFS;
+ if (qInfo->tqi_cwmin != HAL_TXQ_USEDEFAULT) {
+ cw = AH_MIN(qInfo->tqi_cwmin, 1024);
+ /* make sure that the CWmin is of the form (2^n - 1) */
+ qi->tqi_cwmin = 1;
+ while (qi->tqi_cwmin < cw)
+ qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1;
+ } else
+ qi->tqi_cwmin = qInfo->tqi_cwmin;
+ if (qInfo->tqi_cwmax != HAL_TXQ_USEDEFAULT) {
+ cw = AH_MIN(qInfo->tqi_cwmax, 1024);
+ /* make sure that the CWmax is of the form (2^n - 1) */
+ qi->tqi_cwmax = 1;
+ while (qi->tqi_cwmax < cw)
+ qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1;
+ } else
+ qi->tqi_cwmax = INIT_CWMAX;
+ /* Set retry limit values */
+ if (qInfo->tqi_shretry != 0)
+ qi->tqi_shretry = AH_MIN(qInfo->tqi_shretry, 15);
+ else
+ qi->tqi_shretry = INIT_SH_RETRY;
+ if (qInfo->tqi_lgretry != 0)
+ qi->tqi_lgretry = AH_MIN(qInfo->tqi_lgretry, 15);
+ else
+ qi->tqi_lgretry = INIT_LG_RETRY;
+ qi->tqi_cbrPeriod = qInfo->tqi_cbrPeriod;
+ qi->tqi_cbrOverflowLimit = qInfo->tqi_cbrOverflowLimit;
+ qi->tqi_burstTime = qInfo->tqi_burstTime;
+ qi->tqi_readyTime = qInfo->tqi_readyTime;
+
+ switch (qInfo->tqi_subtype) {
+ case HAL_WME_UPSD:
+ if (qi->tqi_type == HAL_TX_QUEUE_DATA)
+ qi->tqi_intFlags = HAL_TXQ_USE_LOCKOUT_BKOFF_DIS;
+ break;
+ default:
+ break; /* NB: silence compiler */
+ }
+ return AH_TRUE;
+}
+
+HAL_BOOL
+ath_hal_getTxQProps(struct ath_hal *ah,
+ HAL_TXQ_INFO *qInfo, const HAL_TX_QUEUE_INFO *qi)
+{
+ if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) {
+ HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
+ "%s: inactive queue\n", __func__);
+ return AH_FALSE;
+ }
+
+ qInfo->tqi_qflags = qi->tqi_qflags;
+ qInfo->tqi_ver = qi->tqi_ver;
+ qInfo->tqi_subtype = qi->tqi_subtype;
+ qInfo->tqi_qflags = qi->tqi_qflags;
+ qInfo->tqi_priority = qi->tqi_priority;
+ qInfo->tqi_aifs = qi->tqi_aifs;
+ qInfo->tqi_cwmin = qi->tqi_cwmin;
+ qInfo->tqi_cwmax = qi->tqi_cwmax;
+ qInfo->tqi_shretry = qi->tqi_shretry;
+ qInfo->tqi_lgretry = qi->tqi_lgretry;
+ qInfo->tqi_cbrPeriod = qi->tqi_cbrPeriod;
+ qInfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit;
+ qInfo->tqi_burstTime = qi->tqi_burstTime;
+ qInfo->tqi_readyTime = qi->tqi_readyTime;
+ return AH_TRUE;
+}
+
+ /* 11a Turbo 11b 11g 108g */
+static const int16_t NOISE_FLOOR[] = { -96, -93, -98, -96, -93 };
+
+/*
+ * Read the current channel noise floor and return.
+ * If nf cal hasn't finished, channel noise floor should be 0
+ * and we return a nominal value based on band and frequency.
+ *
+ * NB: This is a private routine used by per-chip code to
+ * implement the ah_getChanNoise method.
+ */
+int16_t
+ath_hal_getChanNoise(struct ath_hal *ah, HAL_CHANNEL *chan)
+{
+ HAL_CHANNEL_INTERNAL *ichan;
+
+ ichan = ath_hal_checkchannel(ah, chan);
+ if (ichan == AH_NULL) {
+ HALDEBUG(ah, HAL_DEBUG_NFCAL,
+ "%s: invalid channel %u/0x%x; no mapping\n",
+ __func__, chan->channel, chan->channelFlags);
+ return 0;
+ }
+ if (ichan->rawNoiseFloor == 0) {
+ WIRELESS_MODE mode = ath_hal_chan2wmode(ah, chan);
+
+ HALASSERT(mode < WIRELESS_MODE_MAX);
+ return NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, ichan);
+ } else
+ return ichan->rawNoiseFloor + ichan->noiseFloorAdjust;
+}
+
+/*
+ * Process all valid raw noise floors into the dBm noise floor values.
+ * Though our device has no reference for a dBm noise floor, we perform
+ * a relative minimization of NF's based on the lowest NF found across a
+ * channel scan.
+ */
+void
+ath_hal_process_noisefloor(struct ath_hal *ah)
+{
+ HAL_CHANNEL_INTERNAL *c;
+ int16_t correct2, correct5;
+ int16_t lowest2, lowest5;
+ int i;
+
+ /*
+ * Find the lowest 2GHz and 5GHz noise floor values after adjusting
+ * for statistically recorded NF/channel deviation.
+ */
+ correct2 = lowest2 = 0;
+ correct5 = lowest5 = 0;
+ for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) {
+ WIRELESS_MODE mode;
+ int16_t nf;
+
+ c = &AH_PRIVATE(ah)->ah_channels[i];
+ if (c->rawNoiseFloor >= 0)
+ continue;
+ mode = ath_hal_chan2wmode(ah, (HAL_CHANNEL *) c);
+ HALASSERT(mode < WIRELESS_MODE_MAX);
+ nf = c->rawNoiseFloor + NOISE_FLOOR[mode] +
+ ath_hal_getNfAdjust(ah, c);
+ if (IS_CHAN_5GHZ(c)) {
+ if (nf < lowest5) {
+ lowest5 = nf;
+ correct5 = NOISE_FLOOR[mode] -
+ (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c));
+ }
+ } else {
+ if (nf < lowest2) {
+ lowest2 = nf;
+ correct2 = NOISE_FLOOR[mode] -
+ (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c));
+ }
+ }
+ }
+
+ /* Correct the channels to reach the expected NF value */
+ for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) {
+ c = &AH_PRIVATE(ah)->ah_channels[i];
+ if (c->rawNoiseFloor >= 0)
+ continue;
+ /* Apply correction factor */
+ c->noiseFloorAdjust = ath_hal_getNfAdjust(ah, c) +
+ (IS_CHAN_5GHZ(c) ? correct5 : correct2);
+ HALDEBUG(ah, HAL_DEBUG_NFCAL, "%u/0x%x raw nf %d adjust %d\n",
+ c->channel, c->channelFlags, c->rawNoiseFloor,
+ c->noiseFloorAdjust);
+ }
+}
+
+/*
+ * INI support routines.
+ */
+
+int
+ath_hal_ini_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia,
+ int col, int regWr)
+{
+ int r;
+
+ for (r = 0; r < ia->rows; r++) {
+ OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0),
+ HAL_INI_VAL(ia, r, col));
+ DMA_YIELD(regWr);
+ }
+ return regWr;
+}
+
+void
+ath_hal_ini_bank_setup(uint32_t data[], const HAL_INI_ARRAY *ia, int col)
+{
+ int r;
+
+ for (r = 0; r < ia->rows; r++)
+ data[r] = HAL_INI_VAL(ia, r, col);
+}
+
+int
+ath_hal_ini_bank_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia,
+ const uint32_t data[], int regWr)
+{
+ int r;
+
+ for (r = 0; r < ia->rows; r++) {
+ OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0), data[r]);
+ DMA_YIELD(regWr);
+ }
+ return regWr;
+}
OpenPOWER on IntegriCloud