aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/ath/ath_hal/ah.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ath/ath_hal/ah.c')
-rw-r--r--sys/dev/ath/ath_hal/ah.c1585
1 files changed, 1585 insertions, 0 deletions
diff --git a/sys/dev/ath/ath_hal/ah.c b/sys/dev/ath/ath_hal/ah.c
new file mode 100644
index 000000000000..df2ca240c1a8
--- /dev/null
+++ b/sys/dev/ath/ath_hal/ah.c
@@ -0,0 +1,1585 @@
+/*-
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2002-2009 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.
+ */
+#include "opt_ah.h"
+
+#include "ah.h"
+#include "ah_internal.h"
+#include "ah_devid.h"
+#include "ah_eeprom.h" /* for 5ghz fast clock flag */
+
+#include "ar5416/ar5416reg.h" /* NB: includes ar5212reg.h */
+#include "ar9003/ar9300_devid.h"
+
+/* linker set of registered chips */
+OS_SET_DECLARE(ah_chips, struct ath_hal_chip);
+TAILQ_HEAD(, ath_hal_chip) ah_chip_list = TAILQ_HEAD_INITIALIZER(ah_chip_list);
+
+int
+ath_hal_add_chip(struct ath_hal_chip *ahc)
+{
+
+ TAILQ_INSERT_TAIL(&ah_chip_list, ahc, node);
+ return (0);
+}
+
+int
+ath_hal_remove_chip(struct ath_hal_chip *ahc)
+{
+
+ TAILQ_REMOVE(&ah_chip_list, ahc, node);
+ return (0);
+}
+
+/*
+ * Check the set of registered chips to see if any recognize
+ * the device as one they can support.
+ */
+const char*
+ath_hal_probe(uint16_t vendorid, uint16_t devid)
+{
+ struct ath_hal_chip * const *pchip;
+ struct ath_hal_chip *pc;
+
+ /* Linker set */
+ OS_SET_FOREACH(pchip, ah_chips) {
+ const char *name = (*pchip)->probe(vendorid, devid);
+ if (name != AH_NULL)
+ return name;
+ }
+
+ /* List */
+ TAILQ_FOREACH(pc, &ah_chip_list, node) {
+ const char *name = pc->probe(vendorid, devid);
+ if (name != AH_NULL)
+ return name;
+ }
+
+ return AH_NULL;
+}
+
+/*
+ * 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, uint16_t *eepromdata,
+ HAL_OPS_CONFIG *ah_config,
+ HAL_STATUS *error)
+{
+ struct ath_hal_chip * const *pchip;
+ struct ath_hal_chip *pc;
+
+ OS_SET_FOREACH(pchip, ah_chips) {
+ struct ath_hal_chip *chip = *pchip;
+ struct ath_hal *ah;
+
+ /* XXX don't have vendorid, assume atheros one works */
+ if (chip->probe(ATHEROS_VENDOR_ID, devid) == AH_NULL)
+ continue;
+ ah = chip->attach(devid, sc, st, sh, eepromdata, ah_config,
+ error);
+ 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;
+ }
+ }
+
+ /* List */
+ TAILQ_FOREACH(pc, &ah_chip_list, node) {
+ struct ath_hal_chip *chip = pc;
+ struct ath_hal *ah;
+
+ /* XXX don't have vendorid, assume atheros one works */
+ if (chip->probe(ATHEROS_VENDOR_ID, devid) == AH_NULL)
+ continue;
+ ah = chip->attach(devid, sc, st, sh, eepromdata, ah_config,
+ error);
+ 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;
+ }
+ }
+
+ return AH_NULL;
+}
+
+const char *
+ath_hal_mac_name(struct ath_hal *ah)
+{
+ switch (ah->ah_macVersion) {
+ case AR_SREV_VERSION_CRETE:
+ case AR_SREV_VERSION_MAUI_1:
+ return "AR5210";
+ case AR_SREV_VERSION_MAUI_2:
+ case AR_SREV_VERSION_OAHU:
+ return "AR5211";
+ case AR_SREV_VERSION_VENICE:
+ return "AR5212";
+ case AR_SREV_VERSION_GRIFFIN:
+ return "AR2413";
+ case AR_SREV_VERSION_CONDOR:
+ return "AR5424";
+ case AR_SREV_VERSION_EAGLE:
+ return "AR5413";
+ case AR_SREV_VERSION_COBRA:
+ return "AR2415";
+ case AR_SREV_2425: /* Swan */
+ return "AR2425";
+ case AR_SREV_2417: /* Nala */
+ return "AR2417";
+ case AR_XSREV_VERSION_OWL_PCI:
+ return "AR5416";
+ case AR_XSREV_VERSION_OWL_PCIE:
+ return "AR5418";
+ case AR_XSREV_VERSION_HOWL:
+ return "AR9130";
+ case AR_XSREV_VERSION_SOWL:
+ return "AR9160";
+ case AR_XSREV_VERSION_MERLIN:
+ if (AH_PRIVATE(ah)->ah_ispcie)
+ return "AR9280";
+ return "AR9220";
+ case AR_XSREV_VERSION_KITE:
+ return "AR9285";
+ case AR_XSREV_VERSION_KIWI:
+ if (AH_PRIVATE(ah)->ah_ispcie)
+ return "AR9287";
+ return "AR9227";
+ case AR_SREV_VERSION_AR9380:
+ if (ah->ah_macRev >= AR_SREV_REVISION_AR9580_10)
+ return "AR9580";
+ return "AR9380";
+ case AR_SREV_VERSION_AR9460:
+ return "AR9460";
+ case AR_SREV_VERSION_AR9330:
+ return "AR9330";
+ case AR_SREV_VERSION_AR9340:
+ return "AR9340";
+ case AR_SREV_VERSION_QCA9550:
+ return "QCA9550";
+ case AR_SREV_VERSION_AR9485:
+ return "AR9485";
+ case AR_SREV_VERSION_QCA9565:
+ return "QCA9565";
+ case AR_SREV_VERSION_QCA9530:
+ return "QCA9530";
+ }
+ return "????";
+}
+
+/*
+ * Return the mask of available modes based on the hardware capabilities.
+ */
+u_int
+ath_hal_getwirelessmodes(struct ath_hal*ah)
+{
+ return ath_hal_getWirelessModes(ah);
+}
+
+/* linker set of registered RF backends */
+OS_SET_DECLARE(ah_rfs, struct ath_hal_rf);
+TAILQ_HEAD(, ath_hal_rf) ah_rf_list = TAILQ_HEAD_INITIALIZER(ah_rf_list);
+
+int
+ath_hal_add_rf(struct ath_hal_rf *arf)
+{
+
+ TAILQ_INSERT_TAIL(&ah_rf_list, arf, node);
+ return (0);
+}
+
+int
+ath_hal_remove_rf(struct ath_hal_rf *arf)
+{
+
+ TAILQ_REMOVE(&ah_rf_list, arf, node);
+ return (0);
+}
+
+/*
+ * Check the set of registered RF backends to see if
+ * any recognize the device as one they can support.
+ */
+struct ath_hal_rf *
+ath_hal_rfprobe(struct ath_hal *ah, HAL_STATUS *ecode)
+{
+ struct ath_hal_rf * const *prf;
+ struct ath_hal_rf * rf;
+
+ OS_SET_FOREACH(prf, ah_rfs) {
+ struct ath_hal_rf *rf = *prf;
+ if (rf->probe(ah))
+ return rf;
+ }
+
+ TAILQ_FOREACH(rf, &ah_rf_list, node) {
+ if (rf->probe(ah))
+ return rf;
+ }
+ *ecode = HAL_ENOTSUPP;
+ return AH_NULL;
+}
+
+const char *
+ath_hal_rf_name(struct ath_hal *ah)
+{
+ switch (ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) {
+ case 0: /* 5210 */
+ return "5110"; /* NB: made up */
+ case AR_RAD5111_SREV_MAJOR:
+ case AR_RAD5111_SREV_PROD:
+ return "5111";
+ case AR_RAD2111_SREV_MAJOR:
+ return "2111";
+ case AR_RAD5112_SREV_MAJOR:
+ case AR_RAD5112_SREV_2_0:
+ case AR_RAD5112_SREV_2_1:
+ return "5112";
+ case AR_RAD2112_SREV_MAJOR:
+ case AR_RAD2112_SREV_2_0:
+ case AR_RAD2112_SREV_2_1:
+ return "2112";
+ case AR_RAD2413_SREV_MAJOR:
+ return "2413";
+ case AR_RAD5413_SREV_MAJOR:
+ return "5413";
+ case AR_RAD2316_SREV_MAJOR:
+ return "2316";
+ case AR_RAD2317_SREV_MAJOR:
+ return "2317";
+ case AR_RAD5424_SREV_MAJOR:
+ return "5424";
+
+ case AR_RAD5133_SREV_MAJOR:
+ return "5133";
+ case AR_RAD2133_SREV_MAJOR:
+ return "2133";
+ case AR_RAD5122_SREV_MAJOR:
+ return "5122";
+ case AR_RAD2122_SREV_MAJOR:
+ return "2122";
+ }
+ return "????";
+}
+
+/*
+ * 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 5000
+ return ath_hal_waitfor(ah, reg, mask, val, AH_TIMEOUT);
+#undef AH_TIMEOUT
+}
+
+HAL_BOOL
+ath_hal_waitfor(struct ath_hal *ah, u_int reg, uint32_t mask, uint32_t val, uint32_t timeout)
+{
+ int i;
+
+ for (i = 0; i < 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;
+}
+
+/*
+ * 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;
+}
+
+/* 802.11n related timing definitions */
+
+#define OFDM_PLCP_BITS 22
+#define HT_L_STF 8
+#define HT_L_LTF 8
+#define HT_L_SIG 4
+#define HT_SIG 8
+#define HT_STF 4
+#define HT_LTF(n) ((n) * 4)
+
+#define HT_RC_2_MCS(_rc) ((_rc) & 0x1f)
+#define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1)
+#define IS_HT_RATE(_rc) ( (_rc) & IEEE80211_RATE_MCS)
+
+/*
+ * Calculate the duration of a packet whether it is 11n or legacy.
+ */
+uint32_t
+ath_hal_pkt_txtime(struct ath_hal *ah, const HAL_RATE_TABLE *rates, uint32_t frameLen,
+ uint16_t rateix, HAL_BOOL isht40, HAL_BOOL shortPreamble,
+ HAL_BOOL includeSifs)
+{
+ uint8_t rc;
+ int numStreams;
+
+ rc = rates->info[rateix].rateCode;
+
+ /* Legacy rate? Return the old way */
+ if (! IS_HT_RATE(rc))
+ return ath_hal_computetxtime(ah, rates, frameLen, rateix,
+ shortPreamble, includeSifs);
+
+ /* 11n frame - extract out the number of spatial streams */
+ numStreams = HT_RC_2_STREAMS(rc);
+ KASSERT(numStreams > 0 && numStreams <= 4,
+ ("number of spatial streams needs to be 1..3: MCS rate 0x%x!",
+ rateix));
+
+ /* XXX TODO: Add SIFS */
+ return ath_computedur_ht(frameLen, rc, numStreams, isht40,
+ shortPreamble);
+}
+
+static const uint16_t ht20_bps[32] = {
+ 26, 52, 78, 104, 156, 208, 234, 260,
+ 52, 104, 156, 208, 312, 416, 468, 520,
+ 78, 156, 234, 312, 468, 624, 702, 780,
+ 104, 208, 312, 416, 624, 832, 936, 1040
+};
+static const uint16_t ht40_bps[32] = {
+ 54, 108, 162, 216, 324, 432, 486, 540,
+ 108, 216, 324, 432, 648, 864, 972, 1080,
+ 162, 324, 486, 648, 972, 1296, 1458, 1620,
+ 216, 432, 648, 864, 1296, 1728, 1944, 2160
+};
+
+/*
+ * Calculate the transmit duration of an 11n frame.
+ */
+uint32_t
+ath_computedur_ht(uint32_t frameLen, uint16_t rate, int streams,
+ HAL_BOOL isht40, HAL_BOOL isShortGI)
+{
+ uint32_t bitsPerSymbol, numBits, numSymbols, txTime;
+
+ KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate));
+ KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate));
+
+ if (isht40)
+ bitsPerSymbol = ht40_bps[HT_RC_2_MCS(rate)];
+ else
+ bitsPerSymbol = ht20_bps[HT_RC_2_MCS(rate)];
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ if (isShortGI)
+ txTime = ((numSymbols * 18) + 4) / 5; /* 3.6us */
+ else
+ txTime = numSymbols * 4; /* 4us */
+ return txTime + HT_L_STF + HT_L_LTF +
+ HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
+}
+
+/*
+ * 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, HAL_BOOL includeSifs)
+{
+ uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
+ uint32_t kbps;
+
+ /* Warn if this function is called for 11n rates; it should not be! */
+ if (IS_HT_RATE(rates->info[rateix].rateCode))
+ ath_hal_printf(ah, "%s: MCS rate? (index %d; hwrate 0x%x)\n",
+ __func__, rateix, rates->info[rateix].rateCode);
+
+ kbps = rates->info[rateix].rateKbps;
+ /*
+ * index can be invalid during dynamic Turbo transitions.
+ * XXX
+ */
+ if (kbps == 0)
+ return 0;
+ switch (rates->info[rateix].phy) {
+ case IEEE80211_T_CCK:
+ phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
+ if (shortPreamble && rates->info[rateix].shortPreamble)
+ phyTime >>= 1;
+ numBits = frameLen << 3;
+ txTime = phyTime
+ + ((numBits * 1000)/kbps);
+ if (includeSifs)
+ txTime += CCK_SIFS_TIME;
+ break;
+ case IEEE80211_T_OFDM:
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000;
+ HALASSERT(bitsPerSymbol != 0);
+
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_PREAMBLE_TIME
+ + (numSymbols * OFDM_SYMBOL_TIME);
+ if (includeSifs)
+ txTime += OFDM_SIFS_TIME;
+ break;
+ case IEEE80211_T_OFDM_HALF:
+ bitsPerSymbol = (kbps * OFDM_HALF_SYMBOL_TIME) / 1000;
+ HALASSERT(bitsPerSymbol != 0);
+
+ numBits = OFDM_HALF_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_HALF_PREAMBLE_TIME
+ + (numSymbols * OFDM_HALF_SYMBOL_TIME);
+ if (includeSifs)
+ txTime += OFDM_HALF_SIFS_TIME;
+ break;
+ case IEEE80211_T_OFDM_QUARTER:
+ bitsPerSymbol = (kbps * OFDM_QUARTER_SYMBOL_TIME) / 1000;
+ HALASSERT(bitsPerSymbol != 0);
+
+ numBits = OFDM_QUARTER_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_QUARTER_PREAMBLE_TIME
+ + (numSymbols * OFDM_QUARTER_SYMBOL_TIME);
+ if (includeSifs)
+ txTime += OFDM_QUARTER_SIFS_TIME;
+ break;
+ case IEEE80211_T_TURBO:
+ bitsPerSymbol = (kbps * TURBO_SYMBOL_TIME) / 1000;
+ HALASSERT(bitsPerSymbol != 0);
+
+ numBits = TURBO_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = TURBO_PREAMBLE_TIME
+ + (numSymbols * TURBO_SYMBOL_TIME);
+ if (includeSifs)
+ txTime += TURBO_SIFS_TIME;
+ break;
+ 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;
+}
+
+int
+ath_hal_get_curmode(struct ath_hal *ah, const struct ieee80211_channel *chan)
+{
+ /*
+ * Pick a default mode at bootup. A channel change is inevitable.
+ */
+ if (!chan)
+ return HAL_MODE_11NG_HT20;
+
+ if (IEEE80211_IS_CHAN_TURBO(chan))
+ return HAL_MODE_TURBO;
+
+ /* check for NA_HT before plain A, since IS_CHAN_A includes NA_HT */
+ if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan))
+ return HAL_MODE_11NA_HT20;
+ if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan))
+ return HAL_MODE_11NA_HT40PLUS;
+ if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan))
+ return HAL_MODE_11NA_HT40MINUS;
+ if (IEEE80211_IS_CHAN_A(chan))
+ return HAL_MODE_11A;
+
+ /* check for NG_HT before plain G, since IS_CHAN_G includes NG_HT */
+ if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan))
+ return HAL_MODE_11NG_HT20;
+ if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan))
+ return HAL_MODE_11NG_HT40PLUS;
+ if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan))
+ return HAL_MODE_11NG_HT40MINUS;
+
+ /*
+ * XXX For FreeBSD, will this work correctly given the DYN
+ * chan mode (OFDM+CCK dynamic) ? We have pure-G versions DYN-BG..
+ */
+ if (IEEE80211_IS_CHAN_G(chan))
+ return HAL_MODE_11G;
+ if (IEEE80211_IS_CHAN_B(chan))
+ return HAL_MODE_11B;
+
+ HALASSERT(0);
+ return HAL_MODE_11NG_HT20;
+}
+
+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;
+
+/*
+ * XXX TODO: for some (?) chips, an 11b mode still runs at 11bg.
+ * Maybe AR5211 has separate 11b and 11g only modes, so 11b is 22MHz
+ * and 11g is 44MHz, but AR5416 and later run 11b in 11bg mode, right?
+ */
+static WIRELESS_MODE
+ath_hal_chan2wmode(struct ath_hal *ah, const struct ieee80211_channel *chan)
+{
+ if (IEEE80211_IS_CHAN_B(chan))
+ return WIRELESS_MODE_11b;
+ if (IEEE80211_IS_CHAN_G(chan))
+ return WIRELESS_MODE_11g;
+ if (IEEE80211_IS_CHAN_108G(chan))
+ return WIRELESS_MODE_108g;
+ if (IEEE80211_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 };
+
+#define CLOCK_FAST_RATE_5GHZ_OFDM 44
+
+u_int
+ath_hal_mac_clks(struct ath_hal *ah, u_int usecs)
+{
+ const struct ieee80211_channel *c = AH_PRIVATE(ah)->ah_curchan;
+ u_int clks;
+
+ /* NB: ah_curchan may be null when called attach time */
+ /* XXX merlin and later specific workaround - 5ghz fast clock is 44 */
+ if (c != AH_NULL && IS_5GHZ_FAST_CLOCK_EN(ah, c)) {
+ clks = usecs * CLOCK_FAST_RATE_5GHZ_OFDM;
+ if (IEEE80211_IS_CHAN_HT40(c))
+ clks <<= 1;
+ } else if (c != AH_NULL) {
+ clks = usecs * CLOCK_RATE[ath_hal_chan2wmode(ah, c)];
+ if (IEEE80211_IS_CHAN_HT40(c))
+ clks <<= 1;
+ } else
+ clks = usecs * CLOCK_RATE[WIRELESS_MODE_11b];
+
+ /* Compensate for half/quarter rate */
+ if (c != AH_NULL && IEEE80211_IS_CHAN_HALF(c))
+ clks = clks / 2;
+ else if (c != AH_NULL && IEEE80211_IS_CHAN_QUARTER(c))
+ clks = clks / 4;
+
+ return clks;
+}
+
+u_int
+ath_hal_mac_usec(struct ath_hal *ah, u_int clks)
+{
+ uint64_t psec;
+
+ psec = ath_hal_mac_psec(ah, clks);
+ return (psec / 1000000);
+}
+
+/*
+ * XXX TODO: half, quarter rates.
+ */
+uint64_t
+ath_hal_mac_psec(struct ath_hal *ah, u_int clks)
+{
+ const struct ieee80211_channel *c = AH_PRIVATE(ah)->ah_curchan;
+ uint64_t psec;
+
+ /* NB: ah_curchan may be null when called attach time */
+ /* XXX merlin and later specific workaround - 5ghz fast clock is 44 */
+ if (c != AH_NULL && IS_5GHZ_FAST_CLOCK_EN(ah, c)) {
+ psec = (clks * 1000000ULL) / CLOCK_FAST_RATE_5GHZ_OFDM;
+ if (IEEE80211_IS_CHAN_HT40(c))
+ psec >>= 1;
+ } else if (c != AH_NULL) {
+ psec = (clks * 1000000ULL) / CLOCK_RATE[ath_hal_chan2wmode(ah, c)];
+ if (IEEE80211_IS_CHAN_HT40(c))
+ psec >>= 1;
+ } else
+ psec = (clks * 1000000ULL) / CLOCK_RATE[WIRELESS_MODE_11b];
+ return psec;
+}
+
+/*
+ * 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, AH_TRUE);
+ rt->info[i].spAckDuration = ath_hal_computetxtime(ah, rt,
+ WLAN_CTRL_FRAME_SIZE, cix, AH_TRUE, 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_DFS_DMN: /* DFS Domain */
+ *result = AH_PRIVATE(ah)->ah_dfsDomain;
+ 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_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:
+ return HAL_OK;
+
+ case HAL_CAP_HT:
+ return pCap->halHTSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_GTXTO:
+ return pCap->halGTTSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_FAST_CC:
+ return pCap->halFastCCSupport ? 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_NUM_GPIO_PINS:
+ *result = pCap->halNumGpioPins;
+ return HAL_OK;
+ case HAL_CAP_CST:
+ return pCap->halCSTSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_RTS_AGGR_LIMIT:
+ *result = pCap->halRtsAggrLimit;
+ return HAL_OK;
+ case HAL_CAP_4ADDR_AGGR:
+ return pCap->hal4AddrAggrSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_EXT_CHAN_DFS:
+ return pCap->halExtChanDfsSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_RX_STBC:
+ return pCap->halRxStbcSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_TX_STBC:
+ return pCap->halTxStbcSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_COMBINED_RADAR_RSSI:
+ return pCap->halUseCombinedRadarRssi ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_AUTO_SLEEP:
+ return pCap->halAutoSleepSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_MBSSID_AGGR_SUPPORT:
+ return pCap->halMbssidAggrSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_SPLIT_4KB_TRANS: /* hardware handles descriptors straddling 4k page boundary */
+ return pCap->hal4kbSplitTransSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_REG_FLAG:
+ *result = AH_PRIVATE(ah)->ah_currentRDext;
+ return HAL_OK;
+ case HAL_CAP_ENHANCED_DMA_SUPPORT:
+ return pCap->halEnhancedDmaSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_NUM_TXMAPS:
+ *result = pCap->halNumTxMaps;
+ return HAL_OK;
+ case HAL_CAP_TXDESCLEN:
+ *result = pCap->halTxDescLen;
+ return HAL_OK;
+ case HAL_CAP_TXSTATUSLEN:
+ *result = pCap->halTxStatusLen;
+ return HAL_OK;
+ case HAL_CAP_RXSTATUSLEN:
+ *result = pCap->halRxStatusLen;
+ return HAL_OK;
+ case HAL_CAP_RXFIFODEPTH:
+ switch (capability) {
+ case HAL_RX_QUEUE_HP:
+ *result = pCap->halRxHpFifoDepth;
+ return HAL_OK;
+ case HAL_RX_QUEUE_LP:
+ *result = pCap->halRxLpFifoDepth;
+ return HAL_OK;
+ default:
+ return HAL_ENOTSUPP;
+ }
+ case HAL_CAP_RXBUFSIZE:
+ case HAL_CAP_NUM_MR_RETRIES:
+ *result = pCap->halNumMRRetries;
+ return HAL_OK;
+ case HAL_CAP_BT_COEX:
+ return pCap->halBtCoexSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_SPECTRAL_SCAN:
+ return pCap->halSpectralScanSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_HT20_SGI:
+ return pCap->halHTSGI20Support ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_RXTSTAMP_PREC: /* rx desc tstamp precision (bits) */
+ *result = pCap->halRxTstampPrecision;
+ return HAL_OK;
+ case HAL_CAP_ANT_DIV_COMB: /* AR9285/AR9485 LNA diversity */
+ return pCap->halAntDivCombSupport ? HAL_OK : HAL_ENOTSUPP;
+
+ case HAL_CAP_ENHANCED_DFS_SUPPORT:
+ return pCap->halEnhancedDfsSupport ? HAL_OK : HAL_ENOTSUPP;
+
+ /* FreeBSD-specific entries for now */
+ case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */
+ return AH_PRIVATE(ah)->ah_rxornIsFatal ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_INTRMASK: /* mask of supported interrupts */
+ *result = pCap->halIntrMask;
+ return HAL_OK;
+ case HAL_CAP_BSSIDMATCH: /* hardware has disable bssid match */
+ return pCap->halBssidMatchSupport ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_STREAMS: /* number of 11n spatial streams */
+ switch (capability) {
+ case 0: /* TX */
+ *result = pCap->halTxStreams;
+ return HAL_OK;
+ case 1: /* RX */
+ *result = pCap->halRxStreams;
+ return HAL_OK;
+ default:
+ return HAL_ENOTSUPP;
+ }
+ case HAL_CAP_RXDESC_SELFLINK: /* hardware supports self-linked final RX descriptors correctly */
+ return pCap->halHasRxSelfLinkedTail ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_BB_READ_WAR: /* Baseband read WAR */
+ return pCap->halHasBBReadWar? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_SERIALISE_WAR: /* PCI register serialisation */
+ return pCap->halSerialiseRegWar ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_MFP: /* Management frame protection setting */
+ *result = pCap->halMfpSupport;
+ return HAL_OK;
+ case HAL_CAP_RX_LNA_MIXING: /* Hardware uses an RX LNA mixer to map 2 antennas to a 1 stream receiver */
+ return pCap->halRxUsingLnaMixing ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_DO_MYBEACON: /* Hardware supports filtering my-beacons */
+ return pCap->halRxDoMyBeacon ? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_TXTSTAMP_PREC: /* tx desc tstamp precision (bits) */
+ *result = pCap->halTxTstampPrecision;
+ 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++) {
+ uint32_t r = regs[i].start;
+ uint32_t e = regs[i].end;
+ *dp++ = r;
+ *dp++ = e;
+ space -= 2*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;
+}
+
+static void
+ath_hal_setregs(struct ath_hal *ah, const HAL_REGWRITE *regs, int space)
+{
+ while (space >= sizeof(HAL_REGWRITE)) {
+ OS_REG_WRITE(ah, regs->addr, regs->value);
+ regs++, space -= sizeof(HAL_REGWRITE);
+ }
+}
+
+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_SETREGS:
+ ath_hal_setregs(ah, args, argsize);
+ *resultsize = 0;
+ 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);
+#ifdef AH_SUPPORT_WRITE_EEPROM
+ case HAL_DIAG_EEWRITE: {
+ const HAL_DIAG_EEVAL *ee;
+ if (argsize != sizeof(HAL_DIAG_EEVAL))
+ return AH_FALSE;
+ ee = (const HAL_DIAG_EEVAL *)args;
+ return ath_hal_eepromWrite(ah, ee->ee_off, ee->ee_data);
+ }
+#endif /* AH_SUPPORT_WRITE_EEPROM */
+#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;
+ case HAL_DIAG_CHANSURVEY:
+ *result = &AH_PRIVATE(ah)->ah_chansurvey;
+ *resultsize = sizeof(HAL_CHANNEL_SURVEY);
+ 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_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;
+ qInfo->tqi_compBuf = qi->tqi_physCompBuf;
+ 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, const struct ieee80211_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->ic_freq, chan->ic_flags);
+ 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;
+}
+
+/*
+ * Fetch the current setup of ctl/ext noise floor values.
+ *
+ * If the CHANNEL_MIMO_NF_VALID flag isn't set, the array is simply
+ * populated with values from NOISE_FLOOR[] + ath_hal_getNfAdjust().
+ *
+ * The caller must supply ctl/ext NF arrays which are at least
+ * AH_MAX_CHAINS entries long.
+ */
+int
+ath_hal_get_mimo_chan_noise(struct ath_hal *ah,
+ const struct ieee80211_channel *chan, int16_t *nf_ctl,
+ int16_t *nf_ext)
+{
+ HAL_CHANNEL_INTERNAL *ichan;
+ int i;
+
+ 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->ic_freq, chan->ic_flags);
+ for (i = 0; i < AH_MAX_CHAINS; i++) {
+ nf_ctl[i] = nf_ext[i] = 0;
+ }
+ return 0;
+ }
+
+ /* Return 0 if there's no valid MIMO values (yet) */
+ if (! (ichan->privFlags & CHANNEL_MIMO_NF_VALID)) {
+ for (i = 0; i < AH_MAX_CHAINS; i++) {
+ nf_ctl[i] = nf_ext[i] = 0;
+ }
+ return 0;
+ }
+ if (ichan->rawNoiseFloor == 0) {
+ WIRELESS_MODE mode = ath_hal_chan2wmode(ah, chan);
+ HALASSERT(mode < WIRELESS_MODE_MAX);
+ /*
+ * See the comment below - this could cause issues for
+ * stations which have a very low RSSI, below the
+ * 'normalised' NF values in NOISE_FLOOR[].
+ */
+ for (i = 0; i < AH_MAX_CHAINS; i++) {
+ nf_ctl[i] = nf_ext[i] = NOISE_FLOOR[mode] +
+ ath_hal_getNfAdjust(ah, ichan);
+ }
+ return 1;
+ } else {
+ /*
+ * The value returned here from a MIMO radio is presumed to be
+ * "good enough" as a NF calculation. As RSSI values are calculated
+ * against this, an adjusted NF may be higher than the RSSI value
+ * returned from a vary weak station, resulting in an obscenely
+ * high signal strength calculation being returned.
+ *
+ * This should be re-evaluated at a later date, along with any
+ * signal strength calculations which are made. Quite likely the
+ * RSSI values will need to be adjusted to ensure the calculations
+ * don't "wrap" when RSSI is less than the "adjusted" NF value.
+ * ("Adjust" here is via ichan->noiseFloorAdjust.)
+ */
+ for (i = 0; i < AH_MAX_CHAINS; i++) {
+ nf_ctl[i] = ichan->noiseFloorCtl[i] + ath_hal_getNfAdjust(ah, ichan);
+ nf_ext[i] = ichan->noiseFloorExt[i] + ath_hal_getNfAdjust(ah, ichan);
+ }
+ return 1;
+ }
+}
+
+/*
+ * 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;
+ /* XXX can't identify proper mode */
+ mode = IS_CHAN_5GHZ(c) ? WIRELESS_MODE_11a : WIRELESS_MODE_11g;
+ 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 raw nf %d adjust %d\n",
+ c->channel, 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;
+
+ HALASSERT(col < ia->cols);
+ for (r = 0; r < ia->rows; r++) {
+ OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0),
+ HAL_INI_VAL(ia, r, col));
+
+ /* Analog shift register delay seems needed for Merlin - PR kern/154220 */
+ if (HAL_INI_VAL(ia, r, 0) >= 0x7800 && HAL_INI_VAL(ia, r, 0) < 0x7900)
+ OS_DELAY(100);
+
+ DMA_YIELD(regWr);
+ }
+ return regWr;
+}
+
+void
+ath_hal_ini_bank_setup(uint32_t data[], const HAL_INI_ARRAY *ia, int col)
+{
+ int r;
+
+ HALASSERT(col < ia->cols);
+ 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;
+}
+
+/*
+ * These are EEPROM board related routines which should likely live in
+ * a helper library of some sort.
+ */
+
+/**************************************************************
+ * ath_ee_getLowerUppderIndex
+ *
+ * Return indices surrounding the value in sorted integer lists.
+ * Requirement: the input list must be monotonically increasing
+ * and populated up to the list size
+ * Returns: match is set if an index in the array matches exactly
+ * or a the target is before or after the range of the array.
+ */
+HAL_BOOL
+ath_ee_getLowerUpperIndex(uint8_t target, uint8_t *pList, uint16_t listSize,
+ uint16_t *indexL, uint16_t *indexR)
+{
+ uint16_t i;
+
+ /*
+ * Check first and last elements for beyond ordered array cases.
+ */
+ if (target <= pList[0]) {
+ *indexL = *indexR = 0;
+ return AH_TRUE;
+ }
+ if (target >= pList[listSize-1]) {
+ *indexL = *indexR = (uint16_t)(listSize - 1);
+ return AH_TRUE;
+ }
+
+ /* look for value being near or between 2 values in list */
+ for (i = 0; i < listSize - 1; i++) {
+ /*
+ * If value is close to the current value of the list
+ * then target is not between values, it is one of the values
+ */
+ if (pList[i] == target) {
+ *indexL = *indexR = i;
+ return AH_TRUE;
+ }
+ /*
+ * Look for value being between current value and next value
+ * if so return these 2 values
+ */
+ if (target < pList[i + 1]) {
+ *indexL = i;
+ *indexR = (uint16_t)(i + 1);
+ return AH_FALSE;
+ }
+ }
+ HALASSERT(0);
+ *indexL = *indexR = 0;
+ return AH_FALSE;
+}
+
+/**************************************************************
+ * ath_ee_FillVpdTable
+ *
+ * Fill the Vpdlist for indices Pmax-Pmin
+ * Note: pwrMin, pwrMax and Vpdlist are all in dBm * 4
+ */
+HAL_BOOL
+ath_ee_FillVpdTable(uint8_t pwrMin, uint8_t pwrMax, uint8_t *pPwrList,
+ uint8_t *pVpdList, uint16_t numIntercepts, uint8_t *pRetVpdList)
+{
+ uint16_t i, k;
+ uint8_t currPwr = pwrMin;
+ uint16_t idxL, idxR;
+
+ HALASSERT(pwrMax > pwrMin);
+ for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) {
+ ath_ee_getLowerUpperIndex(currPwr, pPwrList, numIntercepts,
+ &(idxL), &(idxR));
+ if (idxR < 1)
+ idxR = 1; /* extrapolate below */
+ if (idxL == numIntercepts - 1)
+ idxL = (uint16_t)(numIntercepts - 2); /* extrapolate above */
+ if (pPwrList[idxL] == pPwrList[idxR])
+ k = pVpdList[idxL];
+ else
+ k = (uint16_t)( ((currPwr - pPwrList[idxL]) * pVpdList[idxR] + (pPwrList[idxR] - currPwr) * pVpdList[idxL]) /
+ (pPwrList[idxR] - pPwrList[idxL]) );
+ HALASSERT(k < 256);
+ pRetVpdList[i] = (uint8_t)k;
+ currPwr += 2; /* half dB steps */
+ }
+
+ return AH_TRUE;
+}
+
+/**************************************************************************
+ * ath_ee_interpolate
+ *
+ * Returns signed interpolated or the scaled up interpolated value
+ */
+int16_t
+ath_ee_interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight,
+ int16_t targetLeft, int16_t targetRight)
+{
+ int16_t rv;
+
+ if (srcRight == srcLeft) {
+ rv = targetLeft;
+ } else {
+ rv = (int16_t)( ((target - srcLeft) * targetRight +
+ (srcRight - target) * targetLeft) / (srcRight - srcLeft) );
+ }
+ return rv;
+}
+
+/*
+ * Adjust the TSF.
+ */
+void
+ath_hal_adjusttsf(struct ath_hal *ah, int32_t tsfdelta)
+{
+ /* XXX handle wrap/overflow */
+ OS_REG_WRITE(ah, AR_TSF_L32, OS_REG_READ(ah, AR_TSF_L32) + tsfdelta);
+}
+
+/*
+ * Enable or disable CCA.
+ */
+void
+ath_hal_setcca(struct ath_hal *ah, int ena)
+{
+ /*
+ * NB: fill me in; this is not provided by default because disabling
+ * CCA in most locales violates regulatory.
+ */
+}
+
+/*
+ * Get CCA setting.
+ *
+ * XXX TODO: turn this and the above function into methods
+ * in case there are chipset differences in handling CCA.
+ */
+int
+ath_hal_getcca(struct ath_hal *ah)
+{
+ u_int32_t diag;
+ if (ath_hal_getcapability(ah, HAL_CAP_DIAG, 0, &diag) != HAL_OK)
+ return 1;
+ return ((diag & 0x500000) == 0);
+}
+
+/*
+ * Set the current state of self-generated ACK and RTS/CTS frames.
+ *
+ * For correct DFS operation, the device should not even /ACK/ frames
+ * that are sent to it during CAC or CSA.
+ */
+void
+ath_hal_set_dfs_cac_tx_quiet(struct ath_hal *ah, HAL_BOOL ena)
+{
+
+ if (ah->ah_setDfsCacTxQuiet == NULL)
+ return;
+ ah->ah_setDfsCacTxQuiet(ah, ena);
+}
+
+/*
+ * This routine is only needed when supporting EEPROM-in-RAM setups
+ * (eg embedded SoCs and on-board PCI/PCIe devices.)
+ */
+/* NB: This is in 16 bit words; not bytes */
+/* XXX This doesn't belong here! */
+#define ATH_DATA_EEPROM_SIZE 2048
+
+HAL_BOOL
+ath_hal_EepromDataRead(struct ath_hal *ah, u_int off, uint16_t *data)
+{
+ if (ah->ah_eepromdata == AH_NULL) {
+ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no eeprom data!\n", __func__);
+ return AH_FALSE;
+ }
+ if (off > ATH_DATA_EEPROM_SIZE) {
+ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: offset %x > %x\n",
+ __func__, off, ATH_DATA_EEPROM_SIZE);
+ return AH_FALSE;
+ }
+ (*data) = ah->ah_eepromdata[off];
+ return AH_TRUE;
+}
+
+/*
+ * Do a 2GHz specific MHz->IEEE based on the hardware
+ * frequency.
+ *
+ * This is the unmapped frequency which is programmed into the hardware.
+ */
+int
+ath_hal_mhz2ieee_2ghz(struct ath_hal *ah, int freq)
+{
+
+ if (freq == 2484)
+ return 14;
+ if (freq < 2484)
+ return ((int) freq - 2407) / 5;
+ else
+ return 15 + ((freq - 2512) / 20);
+}
+
+/*
+ * Clear the current survey data.
+ *
+ * This should be done during a channel change.
+ */
+void
+ath_hal_survey_clear(struct ath_hal *ah)
+{
+
+ OS_MEMZERO(&AH_PRIVATE(ah)->ah_chansurvey,
+ sizeof(AH_PRIVATE(ah)->ah_chansurvey));
+}
+
+/*
+ * Add a sample to the channel survey.
+ */
+void
+ath_hal_survey_add_sample(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hs)
+{
+ HAL_CHANNEL_SURVEY *cs;
+
+ cs = &AH_PRIVATE(ah)->ah_chansurvey;
+
+ OS_MEMCPY(&cs->samples[cs->cur_sample], hs, sizeof(*hs));
+ cs->samples[cs->cur_sample].seq_num = cs->cur_seq;
+ cs->cur_sample = (cs->cur_sample + 1) % CHANNEL_SURVEY_SAMPLE_COUNT;
+ cs->cur_seq++;
+}